Website Backend Development with Rust (Actix Web)

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Showing 1 of 1 servicesAll 2065 services
Website Backend Development with Rust (Actix Web)
Complex
from 2 weeks to 3 months
FAQ
Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Website Backend Development with Rust (Actix Web)

Actix Web is one of the fastest HTTP frameworks in existing benchmarks. On TechEmpower, it consistently ranks in the top five among all languages and frameworks. Payment for performance is a higher entry bar: ownership, lifetimes, async Rust — these aren't learned over a weekend.

When Actix Web is chosen

Services with strict latency requirements (< 1ms p99), financial transaction processing, high-load API gateways, infrastructure components — Rust territory. Also: when memory consumption predictability without GC pauses is needed, or when the service runs in embedded/edge environments with limited resources.

Application structure

// main.rs
use actix_web::{middleware, web, App, HttpServer};
use sqlx::PgPool;

mod config;
mod db;
mod errors;
mod handlers;
mod models;
mod services;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenvy::dotenv().ok();
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let cfg = config::Config::from_env().expect("invalid config");
    let pool = PgPool::connect(&cfg.database_url).await.expect("db connect failed");
    sqlx::migrate!("./migrations").run(&pool).await.expect("migration failed");

    let pool = web::Data::new(pool);

    HttpServer::new(move || {
        App::new()
            .app_data(pool.clone())
            .app_data(web::JsonConfig::default().error_handler(errors::json_error_handler))
            .wrap(middleware::Logger::default())
            .wrap(middleware::Compress::default())
            .service(
                web::scope("/api/v1")
                    .service(handlers::users::scope())
                    .service(handlers::orders::scope()),
            )
    })
    .bind(("0.0.0.0", cfg.port))?
    .workers(num_cpus::get())
    .run()
    .await
}

Models and database queries via sqlx

sqlx checks SQL queries at compile time — typos and type mismatches become build errors, not runtime panics:

// models/user.rs
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use time::OffsetDateTime;
use uuid::Uuid;

#[derive(Debug, Serialize, FromRow)]
pub struct User {
    pub id: Uuid,
    pub email: String,
    pub display_name: String,
    #[serde(skip)]
    pub password_hash: String,
    pub created_at: OffsetDateTime,
}

#[derive(Debug, Deserialize)]
pub struct CreateUserPayload {
    pub email: String,
    pub display_name: String,
    pub password: String,
}

// db/users.rs
pub async fn find_by_id(pool: &PgPool, id: Uuid) -> sqlx::Result<Option<User>> {
    sqlx::query_as!(
        User,
        r#"
        SELECT id, email, display_name, password_hash, created_at
        FROM users
        WHERE id = $1
        "#,
        id
    )
    .fetch_optional(pool)
    .await
}

pub async fn create(pool: &PgPool, payload: &CreateUserPayload) -> sqlx::Result<User> {
    let hash = bcrypt::hash(&payload.password, bcrypt::DEFAULT_COST).unwrap();
    sqlx::query_as!(
        User,
        r#"
        INSERT INTO users (id, email, display_name, password_hash)
        VALUES ($1, $2, $3, $4)
        RETURNING *
        "#,
        Uuid::new_v4(),
        payload.email,
        payload.display_name,
        hash
    )
    .fetch_one(pool)
    .await
}

Handlers and routing

// handlers/users.rs
use actix_web::{get, post, web, HttpResponse, Scope};
use sqlx::PgPool;
use uuid::Uuid;

use crate::{db, errors::AppError, models::user::CreateUserPayload};

pub fn scope() -> Scope {
    web::scope("/users")
        .service(get_user)
        .service(create_user)
}

#[get("/{id}")]
async fn get_user(
    pool: web::Data<PgPool>,
    id: web::Path<Uuid>,
) -> Result<HttpResponse, AppError> {
    let user = db::users::find_by_id(&pool, *id)
        .await?
        .ok_or(AppError::NotFound("user not found".into()))?;

    Ok(HttpResponse::Ok().json(user))
}

#[post("")]
async fn create_user(
    pool: web::Data<PgPool>,
    payload: web::Json<CreateUserPayload>,
) -> Result<HttpResponse, AppError> {
    let user = db::users::create(&pool, &payload).await?;
    Ok(HttpResponse::Created().json(user))
}

Error handling

// errors.rs
use actix_web::{HttpResponse, ResponseError};
use serde_json::json;

#[derive(Debug, thiserror::Error)]
pub enum AppError {
    #[error("not found: {0}")]
    NotFound(String),
    #[error("validation error: {0}")]
    Validation(String),
    #[error("database error")]
    Database(#[from] sqlx::Error),
    #[error("unauthorized")]
    Unauthorized,
}

impl ResponseError for AppError {
    fn error_response(&self) -> HttpResponse {
        match self {
            AppError::NotFound(msg) => HttpResponse::NotFound().json(json!({ "error": msg })),
            AppError::Validation(msg) => {
                HttpResponse::UnprocessableEntity().json(json!({ "error": msg }))
            }
            AppError::Unauthorized => HttpResponse::Unauthorized().json(json!({ "error": "unauthorized" })),
            AppError::Database(e) => {
                tracing::error!("db error: {:?}", e);
                HttpResponse::InternalServerError().json(json!({ "error": "internal error" }))
            }
        }
    }
}

Authentication middleware

// middleware/auth.rs
use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, Error};
use futures_util::future::{ok, LocalBoxFuture, Ready};
use jsonwebtoken::{decode, DecodingKey, Validation};

pub struct JwtAuth;

impl<S, B> Transform<S, ServiceRequest> for JwtAuth
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    // ... standard Transform implementation
    fn new_transform(&self, service: S) -> Self::Future {
        ok(JwtAuthMiddleware { service })
    }
}

Cargo.toml dependencies

[dependencies]
actix-web = "4"
sqlx = { version = "0.8", features = ["postgres", "uuid", "time", "runtime-tokio"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["v4", "serde"] }
time = { version = "0.3", features = ["serde"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
jsonwebtoken = "9"
bcrypt = "0.15"
thiserror = "1"
dotenvy = "0.15"
num_cpus = "1"

Deployment

Final binary is 5–15 MB, no runtime. Minimal Docker image:

FROM rust:1.77-slim AS builder
WORKDIR /app
COPY . .
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/myapi /usr/local/bin/
CMD ["myapi"]

Or scratch image if no dynamic dependencies:

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/myapi /
CMD ["/myapi"]

Development timeline

Actix Web requires more development time than Rails or Node.js. Simple CRUD API (5–8 resources): 2–3 weeks with infrastructure setup and tests. High-load service with custom middleware, connection pool tuning and load testing: 4–7 weeks. Development time is offset by operational savings: one Actix instance replaces 5–10 Node.js services under similar load.