Skip to content

Routing and Handlers

In Express you attach handlers directly to app or a Router. In Axum you build a Router struct by chaining .route() calls. There are no magic strings for HTTP verbs — instead you import functions like get, post, put, delete from axum::routing.

TypeScript
import express from 'express';
const router = express.Router();
router.get('/users', listUsers);
router.post('/users', createUser);
router.get('/users/:id', getUser);
router.delete('/users/:id', deleteUser);
export default router;
Rust
use axum::{
routing::{delete, get, post},
Router,
};
use crate::handlers::users::{
create_user, delete_user, get_user, list_users,
};
pub fn users_router() -> Router {
Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(get_user).delete(delete_user))
}

An Axum handler is any async fn whose arguments are extractors and whose return type implements IntoResponse. The simplest handler returns a string:

TypeScript
// Express
async function hello(req, res) {
res.send('Hello, World!');
}
Rust
// Axum — returning a plain string
async fn hello() -> &'static str {
"Hello, World!"
}

Axum handlers can return many types: &str, String, Json<T>, (StatusCode, Json<T>), or your own custom type as long as it implements IntoResponse.

TypeScript
// Express — manual JSON response
async function listUsers(req, res) {
const users = [{ id: 1, name: 'Alice' }];
res.status(200).json(users);
}
Rust
use axum::{http::StatusCode, Json};
use serde::Serialize;
#[derive(Serialize)]
struct User {
id: u32,
name: String,
}
async fn list_users() -> (StatusCode, Json<Vec<User>>) {
let users = vec![User { id: 1, name: "Alice".to_string() }];
(StatusCode::OK, Json(users))
}

Just like Express lets you mount sub-routers at a prefix, Axum supports Router::nest:

TypeScript
// Express
import userRouter from './users.router';
app.use('/api/v1', userRouter);
Rust
use axum::Router;
pub fn create_router() -> Router {
Router::new()
.nest("/api/v1", users_router())
.nest("/api/v1", health_router())
}

Run locally — handlers need a running Axum server. Use cargo run to start the server, then curl http://localhost:3000/users to test.

How do you attach a GET handler to `/users` in Axum?
What trait must an Axum handler's return type implement?
Which Axum function is equivalent to Express's `app.use('/api/v1', subRouter)`?