Welcome to Rust Web Services
This course is opinionated. It reflects how production Rust web services are built in 2026. You might make different choices, and that's fine.
One rule before we start: understand what you're using. Rust's ecosystem has excellent crates, but each one you add is a dependency you own. We'll add things deliberately, one at a time, with a reason for each.
What We're Building
A URL shortener API — a REST service that creates short codes for long URLs, tracks click counts, and returns redirect targets. Simple enough to fit in one course, real enough to exercise every concept.
By the end you'll have a production-shaped Rust service with proper structure, async handlers, JSON serialization, database access, middleware, error handling, testing, and deployment.
What You'll Learn
- Project structure: how to organize an Axum service without over-engineering
- Axum: routing, extractors, state sharing, and the Handler trait
- Serde: JSON serialization and deserialization with zero boilerplate
- Error handling:
thiserror, custom error types, and consistent API responses - Configuration: environment variables and
.envfiles withdotenvy - Middleware: logging, request IDs, and CORS with
tower - SQLx: async PostgreSQL with compile-time query checking
- Database migrations:
sqlx migratefor schema management - Testing: unit tests, integration tests, and testing handlers directly
- Graceful shutdown: catching signals and draining in-flight requests
- Deployment: Docker, environment configuration, and health checks
Why Axum?
Axum is built by the Tokio team on top of tower and hyper. It composes well, has no magic, and its error model maps directly to what you learned in Rust Essentials. Handlers are plain async functions — no macros, no framework-specific traits to memorize.
If you know Go's net/http, Axum will feel familiar: explicit routing, middleware as layers, no hidden global state.
Prerequisites
Complete Rust Essentials first. This course assumes you understand ownership, traits, Result, async/await, and Arc<Mutex<T>>. We won't re-explain those — we'll use them.
Let's build something.