Cover Page
Backend Page
Rust with axum
Cargo.toml: add dependencies
Change to your chatterd folder and edit the file Cargo.toml to add the 3rd-party libraries we will be using.
server$ cd ~/reactive/chatterd
server$ vi Cargo.toml
In Cargo.toml, add the following lines below the existing [dependencies] tag:
axum-extra = { version = "0.10.1" }
bb8 = "0.9.0"
bb8-postgres = "0.9.0"
chrono = { version = "0.4.40", features = ["serde"] }
postgres = { version = "0.19.10", features = ["with-chrono-0_4", "with-uuid-1"] }
serde = { version = "1.0.219", features = ["derive"] }
tokio-postgres = "0.7.13"
uuid = { version = "1.16.0", features = ["v4", "macro-diagnostics", "serde"] }
chatterd package
Edit src/main.rs:
server$ vi src/main.rs
and replace the routing line in the use axum block with:
routing::{ get, post },
and add the following use lines along with the PGPPool type alias:
use bb8::Pool;
use bb8_postgres::PostgresConnectionManager;
use tokio_postgres::NoTls;
type PGPool = Pool<PostgresConnectionManager<NoTls>>;
Add a pgppool property to your AppState struct definition after the line
client: Client:
pgpool: PGPool,
In the main() function, after the line, client: Client::new(), add a pgpool property to your AppState instantiation. For the property, we set up a pool of open connections to our PostgreSQL chatterdb database. Maintaining a pool of open connections avoids the cost of opening and closing a connection on every database operation. The password used in instantiating PostgresConnectionManager must match the one you used when setting up PostgreSQL earlier.
pgpool: Pool::builder()
.build(
PostgresConnectionManager::new_from_stringlike(
"host=localhost user=chatter password=chattchatt dbname=chatterdb",
NoTls,
)
.map_err(|err| {
eprintln!("{:?}", err);
process::exit(1)
})
.unwrap(),
)
.await
.map_err(|err| {
eprintln!("{:?}", err);
process::exit(1)
})
.unwrap(),
Continuing inside the main() function, find the router = Router::new() instantiation statement and add two routes right
after the route for /llmprompt. These serve Chatter’s two APIs: HTTP GET request, with URL endpoint getchatts, and HTTP POST request, with URL endpoint postchatt. With each endpoint, we also specify the MethodRouter and handler function assigned to it:
.route("/getchatts", get(handlers::getchatts))
.route("/postchatt", post(handlers::postchatt))
The functions getchatts() and postchatt() will be implemented in handlers.rs.
We’re done with main.rs. Save and exit the file.
handlers module
Edit src/handlers.rs:
server$ vi src/handlers.rs
First modify the use imports at the top of the file:
-
replace existing
use serde_jsonline with this:use serde_json::{ json, Value }; - and add these new
uselines:use chrono::{ DateTime, Local }; use serde::{ Deserialize, Serialize }; use uuid::Uuid;
We define a Chatt struct to help postchatt() deserialize JSON
received from clients. Add these lines right below the above:
#[derive(Debug, Serialize, Deserialize)]
pub struct Chatt {
username: String,
message: String,
id: Option<String>,
timestamp: Option<DateTime<Local>>,
}
To the existing logging functions, add the following:
fn logClientErr(clientIP: SocketAddr, errcode: StatusCode, errmsg: String) -> (StatusCode, String) {
tracing::info!("{:?} | {:?} |", errcode, clientIP);
(errcode, errmsg)
}
The handler getchatts() uses an open connection from the connection pool passed
in from main(), as part of AppState, to query the database for stored chatts.
Once all the rows from the database are retrieved, we insert the resulting array of chatts into the response JSON object.
Add getchatts() to your handlers.rs after the definition of the llmprompt() function.
pub async fn getchatts(
State(appState): State<AppState>,
ConnectInfo(clientIP): ConnectInfo<SocketAddr>,
) -> Result<Json<Value>, (StatusCode, String)> {
let chatterDB = appState
.pgpool
.get()
.await
.map_err(|err| logServerErr(clientIP, err.to_string()))?;
let mut chattArr: Vec<Vec<Option<String>>> = Vec::new();
for row in chatterDB
.query(
"SELECT username, message, id, time FROM chatts",
&[],
)
.await
.map_err(|err| logServerErr(clientIP, err.to_string()))?
{
chattArr.push(vec![
row.get(0),
row.get(1),
Some(row.get::<usize, Uuid>(2).to_string()),
Some(row.get::<usize, DateTime<Local>>(3).to_string()),
]);
}
logOk(clientIP);
Ok(Json(json!(chattArr)))
}
Similarly, postchatt() receives a posted chatt in the expected JSON format,
deserializes it into the Chatt struct, and inserts it into the database, using
a connection from the pool. The UUID and time stamp of each chatt are generated
at insertion time. Add postchatt() to the end of your handlers.rs:
pub async fn postchatt(
State(appState): State<AppState>,
ConnectInfo(clientIP): ConnectInfo<SocketAddr>,
Json(chatt): Json<Chatt>,
) -> Result<Json<Value>, (StatusCode, String)> {
let chatterDB = appState
.pgpool
.get()
.await
.map_err(|err| logServerErr(clientIP, err.to_string()))?;
chatterDB
.execute(
"INSERT INTO chatts (username, message, id) VALUES ($1, $2, gen_random_uuid())",
&[&chatt.username, &chatt.message],
)
.await
.map_err(|err| logClientErr(clientIP, StatusCode::NOT_ACCEPTABLE, err.to_string()))?;
logOk(clientIP);
Ok(Json(json!({})))
}
We’re done with handlers.rs. Save and exit the file.
Build and test run
To build your server:
server$ cargo build --release
As before, it will take some time to download and build all the 3rd-party crates. Be patient.
Linking error with cargo build?
When running cargo build --release, if you see:
error: linking with cc failed: exit status: 1
note: collect2: fatal error: ld terminated with signal 9 [Killed]
below a long list of object files, try running cargo build --release again. It usually works the second time around, when it will have less remaining linking to do. If the error persisted, please talk to the teaching staff.
![]()
Rust is a compiled language, like C/C++ and unlike Python, which is an interpreted language. This means you must run cargo build each and every time you made changes to your code, for the changes to show up in your executable.
To run your server:
server$ sudo ./chatterd
# Hit ^C to end the test
You can test your implementation following the instructions in the Testing Chatter APIs section.
References
| Prepared by Sugih Jamin | Last updated July 23rd, 2025 |