rust 75 lines · 8 steps

Substate extraction with FromRef in Axum

How Axum's FromRef lets handlers pull just the piece of app state they need instead of the whole struct.

Explained by highlit
1use std::sync::Arc;
2 
3use axum::{
4 extract::{FromRef, State},
5 http::StatusCode,
6 routing::{get, post},
7 Json, Router,
8};
9use serde::{Deserialize, Serialize};
10 
11#[derive(Clone)]
12struct AppState {
13 db: Arc<PgPool>,
14 auth: AuthState,
15}
16 
17#[derive(Clone)]
18struct AuthState {
19 jwt_secret: Arc<str>,
20 sessions: Arc<SessionStore>,
21}
22 
23impl FromRef<AppState> for AuthState {
24 fn from_ref(state: &AppState) -> Self {
25 state.auth.clone()
26 }
27}
28 
29impl FromRef<AppState> for Arc<PgPool> {
30 fn from_ref(state: &AppState) -> Self {
31 state.db.clone()
32 }
33}
34 
35#[derive(Deserialize)]
36struct LoginRequest {
37 email: String,
38 password: String,
39}
40 
41#[derive(Serialize)]
42struct TokenResponse {
43 token: String,
44}
45 
46fn public_routes() -> Router<AppState> {
47 Router::new()
48 .route("/health", get(|| async { StatusCode::OK }))
49 .route("/auth/login", post(login))
50}
51 
52fn authenticated_routes() -> Router<AppState> {
53 Router::new()
54 .route("/me", get(current_user))
55 .route("/posts", post(create_post))
56 .layer(axum::middleware::from_fn(require_bearer_token))
57}
58 
59pub fn app(state: AppState) -> Router {
60 Router::new()
61 .nest("/api", public_routes().merge(authenticated_routes()))
62 .with_state(state)
63}
64 
65async fn login(
66 State(auth): State<AuthState>,
67 State(db): State<Arc<PgPool>>,
68 Json(body): Json<LoginRequest>,
69) -> Result<Json<TokenResponse>, StatusCode> {
70 let user = User::verify_credentials(&db, &body.email, &body.password)
71 .await
72 .map_err(|_| StatusCode::UNAUTHORIZED)?;
73 let token = auth.issue_token(user.id);
74 Ok(Json(TokenResponse { token }))
75}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1FromRef decouples handlers from the full state struct so each one asks only for the substate it uses.
  2. 2Wrapping shared resources in Arc makes cloning state per-request cheap regardless of what it holds.
  3. 3Splitting routes into groups lets you attach middleware like auth to exactly the endpoints that need it.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Substate extraction with FromRef in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code