Search code examples
rustrust-axum

How to route to multiple axum handlers based on URL path parameter type?


I want to create these routes in axum:

/v1/users/:username  # String
/v1/users/:user_id   # u64

But creating these routes:

Router::new()
    .route("/v1/users/:username", get(get_user))
    .route("/v1/users/:user_id", get(get_user))

Is not possible due to this error:

thread 'main' panicked at 'Invalid route "/v1/users/:user_id": insertion failed due to conflict with previously registered route: /v1/users/:username'

How can I create the same route which accepts many different URL parameter types?


Solution

  • This doesn't even make sense on a conceptual level. Imagine you receive a request with uri path /v1/users/12345. How should the 12345 part be interpreted? As a username, or as a numeric id? If you have some internal logic that for example requires that username must start with a letter, then you could do the following:

    1. Add a single route route("/v1/users/:username-or-id", get(get_user)).

    2. In get_user handler use Path(String) extractor.

    3. Inside get_user manually try to parse path segment and use different business logic depending on the result.

    For example:

    async fn get_user(Path(username_or_id): Path<String>) -> impl IntoResponse {
        if let Ok(numeric_id) = username_or_id.parse::<u64>() {
            // use `numeric_id: u64`
        } else {
            let username = username_or_id;
            // use `username: String`
        }
    }
    

    Instead of this you could just have two separate endpoints, which probably will be clearer and harder to misuse.

    /v1/users/:username  # String
    /v1/users/id/:user_id   # u64