Rust program is not inserting inserting row into Postgres

Am following a Rust tutorial online which consists of creating a minimal Rust HTTP server which uses a Postgres database in a Docker container. Am running this inside my macOS Sequoia via Docker Desktop.
rust-crud-api/cargo.toml
:
[package]
name = "rust-crud-api"
version = "0.1.0"
edition = "2021"
[dependencies]
postgres = "0.19.10"
serde = "1.0.219"
serde_json = "1.0.140"
serde_derive = "1.0.219"
rust-crud-api/main.rs
:
use postgres::Error as PostgresError;
use postgres::{Client, NoTls};
use std::env;
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
#[macro_use]
extern crate serde_derive;
#[derive(Serialize, Deserialize)]
struct User {
id: Option<i32>,
name: String,
email: String,
}
const DB_URL: &str = env!("DATABASE_URL");
const OK_RESPONSE: &str = "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n";
const NOT_FOUND: &str = "HTTP/1.1 404 NOT FOUND\r\n\r\n";
const INTERNAL_SERVER_ERROR: &str = "HTTP/1.1 500 INTERNAL SERVER ERROR\r\n\r\n";
fn main() {
if let Err(e) = set_database() {
println!("Error: {}", e);
return;
}
let listener = TcpListener::bind(format!("0.0.0.0:8080")).unwrap();
println!("Server started at port 8080");
for stream in listener.incoming() {
match stream {
Ok(stream) => {
handle_client(stream);
}
Err(e) => {
println!("Error: {}", e);
}
}
}
}
fn handle_client(mut stream: TcpStream) {
let mut buffer = [0; 1024];
let mut request = String::new();
match stream.read(&mut buffer) {
Ok(size) => {
request.push_str(String::from_utf8_lossy(&buffer[..size]).as_ref());
let (status_line, content) = match &*request {
r if r.starts_with("POST /users") => handle_post_request(r),
r if r.starts_with("GET /users") => handle_get_all_request(r),
_ => (NOT_FOUND.to_string(), "404 Not Found".to_string()),
};
stream
.write_all(format!("{}{}", status_line, content).as_bytes())
.unwrap();
}
Err(e) => {
println!("Error: {}", e);
}
}
}
fn handle_post_request(request: &str) -> (String, String) {
match (
get_user_request_body(&request),
Client::connect(DB_URL, NoTls),
) {
(Ok(user), Ok(mut client)) => {
client
.execute(
"INSERT INTO users (name, email) VALUES ($1, $2)",
&[&user.name, &user.email],
)
.unwrap();
(OK_RESPONSE.to_string(), "User created".to_string())
}
_ => (INTERNAL_SERVER_ERROR.to_string(), "Error".to_string()),
}
}
fn handle_get_all_request(request: &str) -> (String, String) {
match Client::connect(DB_URL, NoTls) {
Ok(mut client) => {
let mut users = Vec::new();
for row in client.query("SELECT * FROM users", &[]).unwrap() {
users.push(User {
id: row.get(0),
name: row.get(1),
email: row.get(2),
});
}
(
OK_RESPONSE.to_string(),
serde_json::to_string(&users).unwrap(),
)
}
_ => (INTERNAL_SERVER_ERROR.to_string(), "Error".to_string()),
}
}
fn get_id(request: &str) -> &str {
request
.split("/")
.nth(2)
.unwrap_or_default()
.split_whitespace()
.next()
.unwrap_or_default()
}
fn get_user_request_body(request: &str) -> Result<User, serde_json::Error> {
serde_json::from_str(request.split("\r\n\t\n").last().unwrap_or_default())
}
fn set_database() -> Result<(), PostgresError> {
let mut client = Client::connect(DB_URL, NoTls)?;
client.batch_execute(
"CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
email VARCHAR NOT NULL
)",
)?;
Ok(())
}
rust-crud-api/Dockerfile
:
FROM rust:1.86.0 as builder
WORKDIR /app
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL
COPY . .
RUN cargo build --release
FROM ubuntu:25.04
WORKDIR /usr/local/bin
COPY --from=builder /app/target/release/rust-crud-api .
CMD ["./rust-crud-api"]
rust-crud-api/docker-compose.yml
:
services:
rustapp:
container_name: rustapp
image: francescoxx/rustapp:1.0.0
build:
context: .
dockerfile: Dockerfile
args:
DATABASE_URL: postgres://postgres:postgres@db:5432/postgres
ports:
- '8080:8080'
depends_on:
- db
db:
container_name: db
image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- '5432:5432'
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata: {}
- Setup the database using docker compose:
docker compose up -d db
- Built and ran the Rust app:
docker compose up rustapp
When I go to Postman and hit the http://localhost:8080/users endpoint, I get the following:
HTTP 200 OK
Response body is an empty JSON array (since no users have been created/inserted):
[]
- When I try doing a HTTP POST against the
http://localhost:8080/users
endpoint with the following JSON request body/payload:
{
"name": "joe",
"email": "Read more"
}
Receive an HTTP 500
with Error
for the Response body.
There are no errors written to stdout
, however...
- When I go inside the Postgres instance the
users
table is created but empty:
docker exec -it db psql -U postgres
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------+-------+----------
public | users | table | postgres
(1 row)
postgres=# SELECT * FROM users;
id | name | email
----+------+-------
(0 rows)
What am I possibly doing wrong? Thanks in advance!
Answer
The problem was the \t
escape sequence inside this function:
fn get_user_request_body(request: &str) -> Result<User, serde_json::Error> {
serde_json::from_str(request.split("\r\n\t\n").last().unwrap_or_default())
}
Fix was replacing that \t
with a \r
:
serde_json::from_str(request.split("\r\n\r\n").last().unwrap_or_default())
Enjoyed this article?
Check out more content on our blog or follow us on social media.
Browse more articles