Skip to main content

Project Structure

Organizing your Actix Web codebase.

Actix 4.9+ 5 min read

# Organizing Your Project

As your Actix Web application grows beyond a single file, it's essential to organize your code to maintain readability and maintainability. While Rust allows you to structure projects however you want, certain patterns are common in web applications.

##A Scalable Layout

A standard REST API might be organized like this:

code_blocksrust
src/
├── main.rs         # Entry point, HttpServer setup
├── db.rs           # Database connection and models
├── handlers/       # Request handlers (controllers)
│   ├── mod.rs      # Handler exports
│   ├── users.rs    # User-related endpoints
│   └── posts.rs    # Post-related endpoints
├── middleware/     # Custom middleware
├── models/         # Data structures and Serde models
└── routes.rs       # Route definitions grouping

#The `routes.rs` Module

Instead of cluttering main.rs with every single route registration, you can use Actix Web's ServiceConfig to group routes into modules.

code_blocksrust
// src/handlers/users.rs
use actix_web::{get, HttpResponse, Responder};

#[get("/")]
pub async fn get_all_users() -> impl Responder {
    HttpResponse::Ok().body("List of users")
}

// src/routes.rs
use actix_web::web;
use crate::handlers::users;

pub fn configure(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/users")
            .service(users::get_all_users)
    );
}

Then, in main.rs:

code_blocksrust
use actix_web::{App, HttpServer};
mod handlers;
mod routes;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().configure(routes::configure)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

##Modularity

This ServiceConfig pattern allows you to cleanly separate concerns. A large application might have multiple config functions across different modules, each responsible for configuring its own routes and state.

By keeping your routes, models, and handlers distinct, you ensure that your code remains testable and easy to navigate for new developers joining your project.

Was this page helpful?

Reference Implementation

The following code snippet demonstrates the authoritative pattern for this documentation.

src/main.rs
1use actix_web::{web, App, HttpServer, Responder};
2
3async fn ping() -> impl Responder {
4 "pong"
5}
6
7// Imagine this is in a separate module
8fn api_config(cfg: &mut web::ServiceConfig) {
9 cfg.route("/ping", web::get().to(ping));
10}
11
12#[actix_web::main]
13async fn main() -> std::io::Result<()> {
14 HttpServer::new(|| {
15 App::new().configure(api_config)
16 })
17 .bind("127.0.0.1:8080")?
18 .run()
19 .await
20}
Was this page helpful?