DEV Community

ANKUSH CHOUDHARY JOHAL
ANKUSH CHOUDHARY JOHAL

Posted on • Originally published at johal.in

Step-by-Step: Set Up a Rust 1.85 Web Server with Actix 5.0 and PostgreSQL 17

Step-by-Step: Set Up a Rust 1.85 Web Server with Actix 5.0 and PostgreSQL 17

This guide walks you through building a production-ready web server using Rust 1.85, the Actix 5.0 web framework, and PostgreSQL 17 as your database. We’ll cover project initialization, database integration, API endpoint creation, and testing.

Prerequisites

Ensure you have the following installed before starting:

  • Rust 1.85 (install via rustup, then run rustup install 1.85.0 && rustup default 1.85.0)
  • PostgreSQL 17 (follow official installation instructions for your OS)
  • Cargo (comes with Rust)
  • psql (PostgreSQL CLI, included with PostgreSQL 17)

Step 1: Set Up PostgreSQL 17 Database

First, create a dedicated database and user for your project:

sudo -u postgres psql
CREATE DATABASE rust_actix_db;
CREATE USER rust_user WITH PASSWORD 'secure_password_here';
GRANT ALL PRIVILEGES ON DATABASE rust_actix_db TO rust_user;
\q
Enter fullscreen mode Exit fullscreen mode

Note: Replace secure_password_here with a strong password of your choice.

Step 2: Initialize Rust Project

Create a new Rust project using Cargo:

cargo new rust-actix-postgres-server
cd rust-actix-postgres-server
Enter fullscreen mode Exit fullscreen mode

Step 3: Add Dependencies

Update your Cargo.toml to include Actix 5.0, SQLx for PostgreSQL integration, and dotenv for environment variables:

[package]
name = "rust-actix-postgres-server"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-web = "5.0"
tokio = { version = "1.0", features = ["full"] }
sqlx = { version = "0.8", features = ["runtime-tokio-native-tls", "postgres", "macros", "chrono"] }
dotenv = "0.15"
chrono = { version = "0.4", features = ["serde"] }
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Environment Variables

Create a .env file in the project root to store your database connection string:

DATABASE_URL=postgres://rust_user:secure_password_here@localhost/rust_actix_db
SERVER_ADDR=127.0.0.1:8080
Enter fullscreen mode Exit fullscreen mode

Replace secure_password_here with the password you set in Step 1. Add .env to your .gitignore to avoid committing sensitive data.

Step 5: Set Up Database Connection Pool

Create a src/db.rs file to handle PostgreSQL connections using SQLx:

use sqlx::postgres::PgPoolOptions;
use std::time::Duration;

pub async fn init_pool() -> sqlx::PgPool {
    let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgPoolOptions::new()
        .max_connections(5)
        .acquire_timeout(Duration::from_secs(3))
        .connect(&database_url)
        .await
        .expect("Failed to connect to Postgres")
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Define Data Models

Create a src/models.rs file to define your data structures. We’ll use a simple User model for this guide:

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;

#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub email: String,
    pub created_at: DateTime,
}

#[derive(Debug, Deserialize)]
pub struct CreateUserRequest {
    pub username: String,
    pub email: String,
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Create Database Migrations (Optional but Recommended)

Install SQLx CLI to manage migrations:

cargo install sqlx-cli --features postgres
Enter fullscreen mode Exit fullscreen mode

Create a migration to set up the users table:

sqlx migrate add create_users_table
Enter fullscreen mode Exit fullscreen mode

Edit the generated migration file in src/migrations/YYYYMMDDHHMMSS_create_users_table.sql:

CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Enter fullscreen mode Exit fullscreen mode

Run the migration:

sqlx migrate run
Enter fullscreen mode Exit fullscreen mode

Step 8: Write API Endpoints

Update src/main.rs to define your Actix 5.0 routes and handlers:

use actix_web::{web, App, HttpServer, Responder, HttpResponse};
use dotenv::dotenv;
use sqlx::PgPool;

mod db;
mod models;

use models::{User, CreateUserRequest};

// GET /users: Fetch all users
async fn get_users(pool: web::Data) -> impl Responder {
    let result = sqlx::query_as::<_, User>("SELECT id, username, email, created_at FROM users")
        .fetch_all(pool.get_ref())
        .await;

    match result {
        Ok(users) => HttpResponse::Ok().json(users),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

// POST /users: Create a new user
async fn create_user(
    pool: web::Data,
    req: web::Json,
) -> impl Responder {
    let result = sqlx::query_as::<_, User>(
        "INSERT INTO users (username, email) VALUES ($1, $2) RETURNING id, username, email, created_at"
    )
    .bind(&req.username)
    .bind(&req.email)
    .fetch_one(pool.get_ref())
    .await;

    match result {
        Ok(user) => HttpResponse::Created().json(user),
        Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let pool = db::init_pool().await;

    let server_addr = std::env::var("SERVER_ADDR").unwrap_or_else(|_| "127.0.0.1:8080".to_string());
    println!("Starting server at http://{}", server_addr);

    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .route("/users", web::get().to(get_users))
            .route("/users", web::post().to(create_user))
    })
    .bind(server_addr)?
    .run()
    .await
}
Enter fullscreen mode Exit fullscreen mode

Step 9: Test the Server

Build and run your server:

cargo run
Enter fullscreen mode Exit fullscreen mode

You should see Starting server at http://127.0.0.1:8080. Test the endpoints with curl:

# Create a new user
curl -X POST http://127.0.0.1:8080/users \
  -H "Content-Type: application/json" \
  -d '{"username": "test_user", "email": "test@example.com"}'

# Fetch all users
curl http://127.0.0.1:8080/users
Enter fullscreen mode Exit fullscreen mode

Conclusion

You’ve successfully set up a Rust 1.85 web server using Actix 5.0 and PostgreSQL 17. This foundation can be extended with additional endpoints, authentication, middleware, and deployment configurations for production use.

Top comments (0)