How Axum and Rocket compare on routing, request guards, middleware, configuration and release activity, with the same endpoint written in each.
Axum and Rocket take opposite approaches to the same job. Axum keeps routing in plain Rust functions and borrows its middleware from Tower, while Rocket puts the method and path in an attribute and validates input through request guards. Both run async handlers on Tokio, and the choice mostly decides how much the framework generates for you and how much you write yourself.
The main differences:
Router, while in Rocket they are declared on each handler and collected in routes![].FromRequest.Axum handlers take extractors as arguments, and the Axum extractor documentation lists the built-in ones and explains ordering rules, such as the body extractor coming last. The signup endpoint in Axum 0.7 with a SQLx pool:
use axum::{extract::State, http::StatusCode, routing::post, Json, Router};
use serde::Deserialize;
use sqlx::PgPool;
#[derive(Deserialize)]
struct Signup {
email: String,
}
async fn create_user(
State(pool): State<PgPool>,
Json(req): Json<Signup>,
) -> Result<StatusCode, StatusCode> {
sqlx::query("INSERT INTO users (email) VALUES ($1)")
.bind(&req.email)
.execute(&pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(StatusCode::CREATED)
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let pool = PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
let app = Router::new()
.route("/users", post(create_user))
.with_state(pool);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
Axum has no configuration system or database integration of its own. Most teams pick SQLx or Diesel and a config crate, and wire both through State.
Rocket's route attributes are procedural macros that generate the routing and parsing code at compile time. The Rocket requests guide describes request guards, which let a handler require an authenticated user or a database connection by adding a typed argument. The same endpoint with rocket_db_pools:
#[macro_use]
extern crate rocket;
use rocket::http::Status;
use rocket::serde::{json::Json, Deserialize};
use rocket_db_pools::{sqlx, Connection, Database};
// The URL comes from Rocket.toml under [default.databases.users].
#[derive(Database)]
#[database("users")]
struct Users(sqlx::PgPool);
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct Signup {
email: String,
}
#[post("/users", data = "<req>")]
async fn create_user(mut db: Connection<Users>, req: Json<Signup>) -> Result<Status, Status> {
sqlx::query("INSERT INTO users (email) VALUES ($1)")
.bind(&req.email)
.execute(&mut **db)
.await
.map_err(|_| Status::InternalServerError)?;
Ok(Status::Created)
}
#[launch]
fn rocket() -> _ {
rocket::build().attach(Users::init()).mount("/", routes![create_user])
}
This sample needs rocket_db_pools = { version = "0.2", features = ["sqlx_postgres"] } in Cargo.toml.
Rocket's release pace has been slower than Axum's. Version 0.5.1 shipped in May 2024 and 0.5.2 in October 2026, according to crates.io, while Axum has moved from 0.7 to 0.8 in the same period.
Rocket code is shorter per route, and the macros catch mismatches between the path and the handler arguments at compile time. Axum code is more explicit, which tends to make unusual patterns easier to express. For an AI coding agent, Axum's single router file gives one place to read every route, while Rocket routes are spread across #[get] and #[post] attributes and the routes![] list.
Encore combines the Rust service you have with infrastructure declared as static values. It works through an infrastructure SDK added as the encore-dev crate, and the framework keeps routing and parsing. Postgres databases, Pub/Sub topics, object storage buckets, Redis, cron jobs and secrets become declarations that Encore discovers and provisions.
This Axum 0.7 service reads from a declared database and runs a nightly cleanup as a cron job that calls one of its routes. The verified column comes from a migration in ./migrations:
use axum::{extract::Path, http::StatusCode, routing::{get, post}, Router};
use encore::cron;
use encore::storage::postgres::Database;
static DB: Database = Database::new("users").migrations("./migrations");
static PURGE: cron::Job = cron::Job::new(
"purge-unverified",
"/internal/purge-unverified",
cron::Schedule::Cron("0 3 * * *"),
)
.title("Delete unverified signups");
async fn purge() -> StatusCode {
match DB
.execute("DELETE FROM users WHERE verified = false", &[])
.await
{
Ok(_) => StatusCode::OK,
Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
async fn email(Path(id): Path<i64>) -> Result<String, StatusCode> {
let row = DB
.query_opt("SELECT email FROM users WHERE id = $1", &[&id])
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.ok_or(StatusCode::NOT_FOUND)?;
Ok(row.get("email"))
}
#[tokio::main]
async fn main() {
encore::initialize();
let app = Router::new()
.route("/users/:id/email", get(email))
.route(PURGE.path(), post(purge));
let addr = std::env::var("ENCORE_LISTEN_ADDR").unwrap_or("0.0.0.0:8080".into());
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app)
.with_graceful_shutdown(encore::shutdown::signal())
.await
.unwrap();
}
The statics are not tied to Axum. A Rocket handler can call DB.query_opt the same way, and its database then comes from Encore instead of Rocket.toml.
encore run starts Postgres locally and applies the migrations. Cron jobs do not run locally or in preview environments, so call /internal/purge-unverified yourself to test it, and the CLI reference covers encore test with a fresh database per test.
A deploy provisions the database and schedules the job in your own AWS or GCP account.
Cloud access sits with Encore's IAM role, not the agent, so an agent that adds the job writes a declaration rather than scheduler or IAM configuration. The AI integration guide covers the rules files that teach an agent Encore's conventions.
Rust support is in beta, and you can request access.
Encore works with either when you want Postgres and cron jobs declared in Rust and provisioned in your own cloud account.
Axum is the more common choice for new APIs because it builds on the Tokio and Tower ecosystem and releases often. Rocket suits teams that value concise macro-based routes and request guards, and that are comfortable with a slower release pace.
Yes. Rocket 0.5.2 was published to crates.io in October 2026, following 0.5.1 in May 2024. Releases are less frequent than Axum's, so check recent activity before starting a large codebase on it.
Yes. Rocket 0.5 runs on stable Rust, and handlers can be async functions running on Tokio.
Add the encore-dev crate, declare a cron::Job static with a schedule and a path, and route that path to a handler with PURGE.path() or the literal path. encore run runs the rest of the app locally, and on deploy Encore calls the path on schedule in your own AWS or GCP account.
Deploy to your cloud on AWS and Google Cloud
without managing infrastructure
Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.