M Logo
← All tutorials

Rust Backend with Axum

Build a blog API in Rust step by step with Axum 0.8: routing, JSON, state, error handling, modules and configuration.

Part 1

Rust Web Server with Axum 0.8: Your First Server

Build your first Rust web server with Axum 0.8 and Tokio: a handler, a Router, tokio::main, a path extractor, and the errors you will hit along the way (E0752, the ":id" panic, 400 and 404). Copy-paste ready code.

rustaxumtokiobackendweb server

Part 2

Rust Web Server with Axum 0.8: JSON In and Out with Serde

Send and receive JSON in Axum 0.8 with Serde: derive Serialize and Deserialize, the Json extractor, 201 Created, and the 415, 422 and 400 request errors.

rustaxumserdejsonbackendrest api

Part 3

Rust Web Server with Axum 0.8: In-Memory CRUD with State

Episode 3 of the Rust backend series: keep posts in memory with Arc, Mutex and with_state, fix the missing Clone error, and build all five CRUD routes with 201, 204 and 404.

rustaxumcrudstatearcmutexrest-api

Part 4

Rust Web Server with Axum 0.8: Error Handling with Result, ? and IntoResponse

Give an Axum API real error messages: an error enum, impl IntoResponse, Result and the ? operator in every handler, and what the E0277 Handler error really means.

rustaxumerror-handlingintoresponsebackend

Part 5

Rust Web Server with Axum 0.8: JSON Errors with thiserror and From

Give every Axum error the same JSON body with thiserror, catch axum's JsonRejection, and fix E0277 '? couldn't convert the error' with #[from].

rustaxumthiserrorerror-handlingjson

Part 6

Rust Web Server with Axum 0.8: Project Structure with Modules

Split one long main.rs into models, error and handlers modules with mod, use, pub and crate::, and fix E0425, E0603 and E0616 on the way.

rustaxummodulesbackend

Part 7

Rust Web Server with Axum 0.8: Configuration with .env, std::env and OnceLock

Read the port and host from environment variables, load a .env file with dotenvy, read the config once with OnceLock, and turn a bad PORT value into a clear panic.

rustaxumdotenvyenvconfigoncelock

Part 8

Rust Web Server with Axum 0.8: Logging with tracing, RUST_LOG and TraceLayer

Replace println with the tracing crates: log levels and fields, an EnvFilter set by RUST_LOG, TraceLayer from tower-http for every request, a warn! event in our errors, and #[instrument] spans.

rustaxumtracingloggingtower-http

Part 9

Rust Web Server with Axum 0.8: A Real Database with SQLx and SQLite

Connect the Axum API to SQLite with SQLx: a SqlitePool in the state, create_if_missing and WAL mode, FromRow and query_as, so posts survive a restart.

rustaxumsqlxsqlitedatabase

Part 10

Rust Web Server with Axum 0.8: SQLx Migrations and Compile-Time Checked Queries

Move the schema into SQLx migrations with sqlx-cli, run them at start with migrate!, and let the query_as! macro check every SQL query at compile time, with sqlx prepare and SQLX_OFFLINE for builds without a database.

rustaxumsqlxmigrationssqlite

Part 11

Rust Web Server with Axum 0.8: Full CRUD with a Repository Trait

Put every database call behind a PostRepo trait with async methods, see why Arc<dyn PostRepo> fails with E0038, and make the handlers generic over the repository instead.

rustaxumsqlxrepository-patterntraitsgenerics

Part 12

Rust Web Server with Axum 0.8: Comments, Foreign Keys and ON DELETE CASCADE

Add a comments table that references posts, nested /posts/{id}/comments routes, ON DELETE CASCADE, and turn a FOREIGN KEY constraint failure into a clean 404.

rustaxumsqlxsqliteforeign-keys

Part 13

Rust Web Server with Axum 0.8: Transactions, 409 Conflict and "database is locked"

Give posts a unique slug and answer a duplicate with 409 Conflict instead of 500, save a post and its tags all or nothing with an SQLx transaction, and see "database is locked" after SQLite's busy timeout.

rustaxumsqlxsqlitetransactions

Part 14

Rust Web Server with Axum 0.8: Pagination, Filters and Sorting with Query

Read page, per_page, tag and sort from the URL with axum's Query extractor, build the SQL safely with SQLx's QueryBuilder and push_bind, return a Page with a total count, and turn bad query strings into a JSON 400.

rustaxumsqlxpaginationquery

Part 15

Rust Web Server with Axum 0.8: Full-Text Search with FTS5, and Soft Delete

Search posts by their words with an SQLite FTS5 index kept in sync by triggers, rank with bm25 and show snippets, quote user input so a stray quote cannot break the query, and move deleted posts to a trash with a deleted_at column.

rustaxumsqlxsqlitefts5search

Part 16

Rust Web Server with Axum 0.8: Validation with the validator Crate and 422

Validate request bodies with the validator crate's derive (length rules, custom functions and messages), build our own ValidJson extractor with FromRequest, and answer one 422 that lists every bad field.

rustaxumvalidationvalidatorextractor

Part 17

Rust Web Server with Axum 0.8: Sign-Up with argon2 Password Hashing

Add a users table and a /signup route, see why a plain-text password is a disaster, hash passwords with argon2 and a random salt, run the slow hash with spawn_blocking, and answer 409 for a taken email.

rustaxumargon2password-hashingauth

Part 18

Rust Web Server with Axum 0.8: Login with JSON Web Tokens and 401

Log users in with a JSON Web Token: claims with sub, role and exp, a secret from .env, jsonwebtoken 11's crypto backend feature, argon2 verify_password, one 401 for every login failure, and a /me route that rejects a tampered token.

rustaxumjwtjsonwebtokenauth

Part 19

Rust Web Server with Axum 0.8: An Auth Extractor with FromRequestParts

Turn the token check into an AuthUser extractor with FromRequestParts, protect every route that changes data, put the body extractor last to fix E0277, store an author_id on posts, and answer 401 for every missing or broken token.

rustaxumextractorjwtauth

Part 20

Rust Web Server with Axum 0.8: Refresh Tokens and HttpOnly Cookies

Make access tokens short-lived and add long-lived refresh tokens: random bytes stored as SHA-256 hashes, sent in an HttpOnly, Secure, SameSite cookie with axum-extra's CookieJar, rotated on every use, and deleted on logout.

rustaxumjwtrefresh-tokenscookiesauth

Part 21

Rust Web Server with Axum 0.8: Roles, Ownership and 403 Forbidden

Decide what a logged-in user may do: 401 versus 403, a Role enum parsed with FromStr, an owner-or-admin rule for editing posts, and an AdminUser extractor for admin-only routes.

rustaxumauthorizationrolesauth

Part 22

Rust Web Server with Axum 0.8: Log In with GitHub (OAuth 2)

Add "Log in with GitHub" to the Axum API with the oauth2 crate: the authorization code flow, state and PKCE in a short cookie, the code exchange with reqwest, GitHub's user-agent rule behind a 403, and GitHub users stored in our own users table with our own tokens.

rustaxumoauth2githubauth

Part 23

Rust Web Server with Axum 0.8: CORS, Security Headers and Rate Limiting with 429

Prepare the API for the real internet: a CorsLayer with an explicit list of origins, three security headers with SetResponseHeaderLayer, and per-IP rate limiting on the login routes with tower_governor, including the "Unable To Extract Key" fix with ConnectInfo.

rustaxumcorsrate-limitingsecuritytower-http

Part 24

Rust Web Server with Axum 0.8: File Uploads with Multipart, 413 and 415

Upload images to posts with axum's Multipart extractor (behind the multipart feature), save them under our own UUID names, serve them with ServeDir, raise the 2 MB default with DefaultBodyLimit, and check magic bytes to answer 415 for fake images.

rustaxummultipartfile-uploadtower-http

Part 25

Rust Web Server with Axum 0.8: A Storage Trait for Local Disk and Memory

Hide where uploaded files live behind a Storage trait with put, get and delete, implement it for the local disk (with a safe file-name check) and for memory, pass it to handlers as an Extension, and pick the store from an environment variable.

rustaxumtraitsstoragefile-upload

Part 26

Rust Web Server with Axum 0.8: Sending Email with lettre

Send a welcome email on sign-up with lettre's async SMTP transport and a local test mail server, see a down mail server turn sign-up into a 500, and make the email non-fatal with a logged warning.

rustaxumemaillettresmtp

Part 27

Rust Web Server with Axum 0.8: Background Jobs and Timers with tokio::spawn and mpsc

Stop making users wait for email: a Job enum, a bounded Tokio mpsc channel as a job queue, a worker task started with tokio::spawn, the E0382 "use of moved value" on the way, and an hourly cleanup timer with tokio::time::interval.

rustaxumtokiobackground-jobsmpsc

Part 28

Rust Web Server with Axum 0.8: Caching with RwLock, then Redis

Cache posts in memory with an RwLock, hit the hidden "future is not Send" error from a lock guard held across .await, invalidate on every write, then move the cache to Redis with JSON values, a 60-second expiry, and a fallback when Redis is down.

rustaxumcachingredisrwlock

Part 29

Rust Web Server with Axum 0.8: Live Comments over WebSocket with tokio::select!

Push new comments to every reader in real time: axum's ws feature and WebSocketUpgrade, a Tokio broadcast channel, tokio::select! to wait for the channel and the client at once, a per-post filter, Lagged receivers, and a clean close.

rustaxumwebsockettokiobroadcast

Part 30

Rust Web Server with Axum 0.8: Server-Sent Events

Stream live comments one way over plain HTTP with server-sent events: the SSE text format, axum's Sse with a Stream, tokio-stream's BroadcastStream and filter_map, KeepAlive for quiet connections, curl -N, and EventSource in the browser.

rustaxumssestreamstokio

Part 31

Rust Web Server with Axum 0.8: API Versioning with nest, merge and Deprecation Headers

Rename a JSON field without breaking old clients: put routes under /api/v1 and /api/v2 with Router::nest, keep the old root paths with merge (nesting at the root panics in axum 0.8), add a PostV2 with From, and send Deprecation, Sunset and Link headers on v1.

rustaxumapi-versioningroutingrest-api

Part 32

Rust Web Server with Axum 0.8: Timeouts (408) and Graceful Shutdown

Cancel requests that hang with tower-http's TimeoutLayer and a 408, keep WebSocket and SSE routes outside the timeout, and shut down gracefully on Ctrl+C and SIGTERM so running requests finish before the server exits.

rustaxumtimeoutsgraceful-shutdownproduction

Part 33

Rust Web Server with Axum 0.8: Health Checks and Prometheus Metrics

Add /health/live and /health/ready (503 when the database or Redis is down), count and time every request with a middleware and the metrics crate, expose Prometheus text at /metrics, and keep label cardinality small with MatchedPath in a route_layer.

rustaxummetricsprometheushealth-checks

Part 34

Rust Web Server with Axum 0.8: OpenAPI Docs and Swagger UI with utoipa

Generate an OpenAPI file from the Axum code with utoipa: #[utoipa::path] on handlers, ToSchema on types (with doc comments and examples), an OpenApi struct, Swagger UI served from the app, and a bearer security scheme for protected routes.

rustaxumopenapiswaggerutoipa

Part 35

Rust Web Server with Axum 0.8: Testing with Unit Tests and Router Tests

Write unit tests with #[cfg(test)] and find a real slugify bug, then test the whole Axum router without a network using tower's oneshot, a fresh SQLite database per test with #[sqlx::test], a login helper, and the ConnectInfo a rate limiter needs.

rustaxumtestingsqlxtower

Part 36

Rust Web Server with Axum 0.8: Docker, One Image That Runs Anywhere

Package the Axum server as a small Docker image: a two-stage Dockerfile with a cached dependency layer, SQLX_OFFLINE builds, a .dockerignore that keeps secrets out, settings from environment variables, Redis on a Docker network, a data volume, and a non-root user.

rustaxumdockerdeploymentsqlx

Part 37

Rust Web Server with Axum 0.8: CI with GitHub Actions, and Deploy

Run cargo fmt --check, clippy with -D warnings and cargo test on every push with GitHub Actions, fix the three real failures on the way, build and push the Docker image to GitHub's container registry, and deploy by pulling and running it.

rustaxumgithub-actionscidocker

Part 38

Rust Web Server with Axum 0.8: Declarative Macros with macro_rules!

Write declarative macros with macro_rules!: patterns and fragments (expr, ident, ty), why square!(2 + 1) gives 9, repetition with $(...),*, an optional trailing comma, reading the expanded code, hygiene, and a real macro that shrinks a 19-method fake repository.

rustmacrosmacro-rulesmetaprogramming

Part 39

Rust Web Server with Axum 0.8: Writing a Procedural Macro, Our Own Derive

Write a derive macro, #[derive(Columns)], in its own proc-macro crate: TokenStream in and out, syn to parse the struct, quote to generate code, a proper compile error with a span instead of a panic, and a #[columns(prefix = "p")] helper attribute.

rustmacrosproc-macrosynquote

Part 40

Rust Web Server with Axum 0.8: How Future and Pin Really Work

Open the async box: implement Future by hand with poll, Ready and Pending, see a program hang when the Waker is forgotten, learn why async fn state machines need Pin, hit the Unpin error, and write a Timed wrapper future with pin-project-lite that logs slow database queries.

rustasyncfuturepintokio

Part 41

Rust Web Server with Axum 0.8: unsafe Rust and Calling C (FFI)

Learn what unsafe allows by calling C from the Axum server: sqlite3_libversion through libsqlite3-sys and CStr, E0133, SAFETY comments, compiling our own C file with build.rs and the cc crate, unsafe extern "C", a dangling CString pointer, NulError, and a safe wrapper.

rustunsafefficbuild-rs

Part 42

Rust Web Server with Axum 0.8: Profiling and Performance

Measure before you optimize: load-test the Axum API with oha, compare debug and release builds, find the hot spot with perf, read SQLite's EXPLAIN QUERY PLAN, add an index for sorting, and measure again; plus why a slow, rate-limited login is a feature.

rustaxumperformanceprofilingsqlite

© 2026 All Rights Reserved By Mahmudul Alam