Why can't I pass a mut axum router to a function to configure routes?

139 views Asked by At

I was playing around with axum while learning Rust, and wanted to try a change where I create the Router in one place but define the routes in another module. Since it failed spectacularly I thought about doing something easier first like configure the routes in the same module just a different function. No matter what I tried, passing mutable or reference Rust just doesn't allow it.

I just want to be allowed to configure the routes separately from the place I create the router. Here is my latest attempt:

use axum::{
    extract::Path,
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use tokio;

async fn root_handler() -> &'static str {
    "Hello, world!"
}

pub fn configure_routes(app: &mut Router) {
    app.route("/", get(root_handler));
}

#[tokio::main]
async fn main() {
    let mut app = Router::new();
    configure_routes(&mut app);
    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

Why doesn't this work? And how can I configure the routes separately?

2

There are 2 answers

4
Chayim Friedman On

Router::route() consumes self and returns it, so you need to do the same:

use axum::{
    extract::Path,
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use tokio;

async fn root_handler() -> &'static str {
    "Hello, world!"
}

pub fn configure_routes(app: Router) -> Router {
    app.route("/", get(root_handler))
}

#[tokio::main]
async fn main() {
    let mut app = Router::new();
    app = configure_routes(app);
    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}
0
trust_nickol On

You can use router.merge(router: Router) to combine several Router definitions. Documentation

use axum::{
    extract::Path,
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use tokio;
use axum::response::IntoResponse;

async fn root_handler() -> &'static str {
    "Hello, world!"
}

async fn health() -> impl IntoResponse {
    r#"{"Ok"}"#
}

pub fn app_router() -> Router {
    Router::new() //
        .route("/", get(root_handler))
}

fn health_router() -> Router {
    Router::new() //
        .route("/health", get(health))
}

#[tokio::main]
async fn main() {
    let mut routes =
        Router::new()
            .merge(app_router())
            .merge(health_router());

    // run our app with hyper, listening globally on port 3000
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, routes).await.unwrap();
}