Migrating from Axum
Migrating from Axum
Guide for developers migrating from Axum to Reinhardt.
Table of Contents
Handler Differences
Axum Handlers
use axum::{
extract::{Path, Query},
Json, response::Json,
};
async fn get_user(
Path(id): Path<u32>,
Query(params): Query<Params>,
) -> Json<User> {
let user = fetch_user(id, params).await;
Json(user)
}Reinhardt Handlers
use reinhardt::{Path, Query};
use reinhardt::Response;
async fn get_user(
Path(id): Path<u32>,
Query(params): Query<Params>,
) -> Response {
let user = fetch_user(id, params).await;
Response::ok().with_json(&user).unwrap()
}
// Or implement Handler trait for stateful handlers
use reinhardt::Handler;
use async_trait::async_trait;
struct UserHandler {
// State
}
#[async_trait]
impl Handler for UserHandler {
async fn handle(&self, request: Request) -> Result<Response, Error> {
Ok(Response::ok())
}
}State vs DI Context
Axum State
use axum::{
extract::State,
response::Json,
};
use std::sync::Arc;
struct AppState {
db: Arc<Database>,
};
async fn get_user(
State(state): State<Arc<AppState>>,
) -> Json<User> {
let user = state.db.get_user(123).await;
Json(user)
}Reinhardt DI Context
use reinhardt::Response;
// Get dependencies from request via DI context
async fn get_user(req: Request) -> Response {
if let Some(db) = req.get_di_context::<Database>() {
let user = db.get_user(123).await;
Response::ok().with_json(&user).unwrap()
} else {
Response::internal_server_error()
}
}Layer vs Middleware
Axum Layers
use axum::{
routing::get,
Router,
Layer,
middleware::Logger,
};
let app = Router::new()
.route("/users", get(handler))
.layer(Logger::new());Reinhardt Middleware
use reinhardt::ServerRouter;
use reinhardt::LoggingMiddleware;
let router = ServerRouter::new()
.with_middleware(LoggingMiddleware::new())
.function("/users", Method::GET, handler);Router Differences
Axum Router
use axum::{
routing::get,
Router,
Json,
};
let app = Router::new()
.route("/users", get(get_users))
.route("/users", post(create_user))
.route("/users/:id", get(get_user));Reinhardt Router
use reinhardt::ServerRouter;
use hyper::Method;
let router = ServerRouter::new()
.function("/users", Method::GET, get_users)
.function("/users", Method::POST, create_user)
.function_named("/users/{id}", Method::GET, "user-detail", get_user);Extractors
Axum Extractors
use axum::{
extract::{Path, Query, Json},
response::Json,
};
async fn handler(
Path(id): Path<u32>,
Query(params): Query<Params>,
Json(data): Json<Data>,
) -> Json<Response> {
// ...
}Reinhardt DI Params
use reinhardt::{Path, Query, Json};
use reinhardt::Response;
async fn handler(
Path(id): Path<u32>,
Query(params): Query<Params>,
Json(data): Json<Data>,
) -> Response {
// ...
}Available extractors in Reinhardt:
Json<T>- JSON bodyForm<T>- Form dataQuery<T>- Query parametersPath<T>- Path parametersBody- Raw body bytesMultipart- Multipart form dataHeader<T>- Typed headerHeaderNamed- Named header
Response Building
Axum
use axum::{
extract::Path,
Json,
response::{Html, IntoResponse, Json},
};
async fn get_user(Path(id): Path<u32>) -> impl IntoResponse {
let user = fetch_user(id).await;
Json(user)
}Reinhardt
use reinhardt::Path;
use reinhardt::Response;
async fn get_user(Path(id): Path<u32>) -> Response {
let user = fetch_user(id).await;
Response::ok().with_json(&user).unwrap()
}Error Handling
Axum
use axum::{
response::{IntoResponse, Response},
Json,
http::StatusCode,
};
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
AppError::NotFound => (StatusCode::NOT_FOUND, "Not found"),
AppError::InternalError => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error"),
};
(status, Json(serde_json::json!({ "error": message }))).into_response()
}
}Reinhardt
use reinhardt::{Error, Response};
impl From<AppError> for Response {
fn from(err: AppError) -> Self {
match err {
AppError::NotFound => Response::not_found(),
AppError::InternalError => Response::internal_server_error(),
}
}
}For enum-shaped application errors, derive HttpError instead of writing the conversion by hand:
use reinhardt::{HttpError, Response};
#[derive(Debug, HttpError)]
#[http_error(response, body = "error")]
enum AppError {
#[http_error(status = NOT_FOUND, message = "Not found")]
NotFound,
#[http_error(status = INTERNAL_SERVER_ERROR, message = "Internal error")]
InternalError,
}
fn into_response(error: AppError) -> Response {
Response::from(error)
}body = "error" writes the client message directly into the response error field. Use it only when all messages are safe for clients. The default body mode omits 5xx details and is safer for internal errors.
Middleware Creation
Axum Tower Middleware
use axum::{
extract::Request,
response::Response,
};
use tower::{ServiceBuilder, ServiceExt};
use http::StatusCode;
async fn middleware(request: Request, next: Next) -> Response {
println!("Request: {:?}", request.uri());
next.run(request).await
}
let app = Router::new()
.route("/users", get(handler))
.layer(middleware);Reinhardt Middleware
use async_trait::async_trait;
use reinhardt::{Handler, Middleware, Request, Response};
pub struct LoggingMiddleware;
#[async_trait]
impl Middleware for LoggingMiddleware {
async fn process(&self, request: Request, next: Arc<dyn Handler>) -> Result<Response> {
println!("Request: {} {}", request.method, request.uri.path());
next.handle(request).await
}
}Migration Checklist
- Convert handlers to
Handlertrait or functions - Replace State with DI context
- Replace layers with middleware
- Convert routing configuration
- Update extractors to DI params
- Update error handling
- Add tests