Building an API with Rust and Axum – Part 1: Project Initialization & Setup


In today’s fast-paced backend development landscape, Rust is gaining traction for its performance, safety, and growing ecosystem. In this blog series, we’ll walk through how to build a modern, scalable, and maintainable API using Axum, SeaORM, and Rust. We'll use my open-source CMS API project, my-cms, as the reference implementation.

This first post focuses on setting up the project from scratch: initializing the Rust project, configuring dependencies, and bootstrapping a basic HTTP server with Axum.

Prerequisites #

Before we begin, ensure you have the following installed on your system:

  • Rust: The programming language.
  • Cargo: Rust's package manager (installed with Rust).
  • SQLite: The database we'll use in this tutorial.

🔧 Step 1: Project Initialization #

Start by creating a new project using Cargo:

cargo new my-cms-api
cd my-cms-api

This generates the default Rust project structure:

my-cms-api/
├── Cargo.toml
└── src
    └── main.rs

We’ll be expanding this structure as we go.

📦 Step 2: Add Dependencies #

Open Cargo.toml and add the following dependencies:

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
sea-orm = { version = "0.12", features = ["sqlx-postgres", "runtime-tokio-rustls", "macros"] }
dotenvy = "0.15"
tracing = "0.1"
tracing-subscriber = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Dependency Breakdown #

  • axum – Web framework built on hyper, designed for modular, type-safe APIs.
  • tokio – Asynchronous runtime that powers most of the ecosystem.
  • sea-orm – Async ORM that works great with PostgreSQL, MySQL, or SQLite.
  • dotenvy – Loads environment variables from a .env file (we’ll use this for configuration).
  • tracing & tracing-subscriber – For structured, async-compatible logging.
  • serde & serde_json – For (de)serializing data structures in JSON.

🌐 Step 3: Bootstrap the HTTP Server #

Let’s get our server running. Open src/main.rs and write this basic Axum app:

use axum::{routing::get, Router};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // Initialize router with a single route
    let app = Router::new().route("/", get(root_handler));

    // Define server address
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("🚀 Server running at http://{}", addr);

    // Start server
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// Basic handler
async fn root_handler() -> &'static str {
    "Hello, Rust + Axum!"
}

Run the server with:

cargo run

Visit http://localhost:3000 and you should see Hello, Rust + Axum!.

📁 Step 4: Prepare Project Structure for Growth #

To keep things clean and modular, we’ll split concerns early. Let’s create some folders:

mkdir -p src/{routes,handlers,config,database,models}
touch src/routes/mod.rs src/handlers/mod.rs

Example of modular routing (src/routes/mod.rs):

use axum::{Router, routing::get};
use crate::handlers::health::health_check;

pub fn create_routes() -> Router {
    Router::new().route("/health", get(health_check))
}

Example handler (src/handlers/health.rs):

pub async fn health_check() -> &'static str {
    "OK"
}

Update src/main.rs to use these:

mod routes;
mod handlers;

use routes::create_routes;

#[tokio::main]
async fn main() {
    let app = create_routes();
    // ... rest is same
}

🧪 Step 5: Add .env Configuration #

Create a .env file in the root:

DATABASE_URL=postgres://postgres:password@localhost:5432/mycms

Load it at runtime with dotenvy:

use dotenvy::dotenv;

#[tokio::main]
async fn main() {
    dotenv().ok();
    // ...
}

✅ What’s Next? #

You now have a structured, minimal web server using Axum, ready to evolve into a full API. In the next post (Part 2), we’ll dive into designing a clean architecture using Axum’s routing system, integrating SeaORM for persistence, and separating concerns via modules.

Want a sneak peek? Check out the repo: https://github.com/doitsu2014/my-cms

Let me know when you're ready to work on Part 2, or if you want a Markdown export of this post.

By [email protected]

Published on 4/8/2025