Sasha Zoria
Sasha Zoria

Reputation: 771

Axum with_state API error: Expected MethodRouter<()>, found MethodRouter<AppState>

I'm working on an Axum project and encountering an issue when using the with_state API. My goal is to provide an application state (AppState) to the handlers in my Axum router, but I keep getting the following error: note: expected struct MethodRouter<()> found struct MethodRouter<AppState> Here's the structure of my project:

#[tokio::main]
async fn main() {
    if let Ok(db) = init_db().await {
        let state = state::app_state::AppState::new(db);
        let routes = routes::root::routes();
        let router = Router::new().with_state(state).nest("/api", routes);
        let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
        axum::serve(listener, router).await.unwrap();
    }
}

root.rs

pub fn routes() -> Router {
    Router::new().merge(user_routes())
}

routes/user.rs

pub fn routes() -> Router {
    Router::new().route("/auth/register", post(register_user))
}

handler/user.rs

pub async fn register_user(Json(payload): Json<User>, State(state): State<AppState>) -> impl IntoResponse {
    ...
}

#[derive(Clone)]
pub struct AppState {
    db_conn: Database
}

impl AppState {
    pub fn new(db: Database) -> AppState {
        AppState {
            db_conn: db
        }
    }
}

The error suggests a type mismatch between MethodRouter<()> and MethodRouter, and I suspect the issue may lie with how I'm setting up my routes or merging them in root.rs. Any suggestions for resolving this?

What I’ve Tried: I've tried different configurations with with_state and nest, but the error persists. Any insights would be greatly appreciated!

Upvotes: 1

Views: 290

Answers (2)

igo
igo

Reputation: 1787

I've bumped into the same problem.

The reason was simple:
As we use with_state(state) all the routers, merged/nested into your app router, must have type of Router<AppState>.
That is:

root.rs

pub fn routes() -> Router<AppState> {
    Router::new().merge(user_routes())
}

routes/user.rs

pub fn routes() -> Router<AppState> {
    Router::new().route("/auth/register", post(register_user))
}

btw you forgot to put parentheses at the function call:
.nest("/api", routes())

Upvotes: 0

Revanth Shalon
Revanth Shalon

Reputation: 141

So I see your code here

#[tokio::main]
async fn main() {
    if let Ok(db) = init_db().await {
        let state = state::app_state::AppState::new(db);
        let routes = routes::root::routes();
        let router = Router::new()
                     .with_state(state)
                     .nest("/api", routes);  //<-- Issue lies here
        let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
        axum::serve(listener, router).await.unwrap();
    }
}

In Axum, when a request passes through the layers,

        requests
           |
           v
+----- layer_three -----+
| +---- layer_two ----+ |
| | +-- layer_one --+ | |
| | |               | | |
| | |    handler    | | |
| | |               | | |
| | +-- layer_one --+ | |
| +---- layer_two ----+ |
+----- layer_three -----+
           |
           v
        responses

The state values that you declare, if it is meant to pass through the other routes that you have specified, then it must be layered before the route.

Coming back to your code,

#[tokio::main]
async fn main() {
    if let Ok(db) = init_db().await {
        let state = state::app_state::AppState::new(db);
        let routes = routes::root::routes();
        let router = Router::new()
                    .nest("/api", routes)
                    .with_state(state);  // I moved down the state layer
        let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
        axum::serve(listener, router).await.unwrap();
    }
}

This should work in your case. If it doesn't let me know.

You can reference this answer to get how we pass state through routes. https://stackoverflow.com/a/79465348/11138368

Reference documentation for Axum: https://docs.rs/axum/latest/axum/middleware/

Upvotes: 0

Related Questions