Handler<_, _, _>` is not satisfied

Hello, i just started learning Rust and after finishing rust book i'm trying to make my first api for website. Currently stuck at errors

error[E0277]: the trait bound `fn(axum::Json<CreateUser>, State<Pool<Postgres>>) -> impl Future<Output = Result<StatusCode, StatusCode>> {create_user}: Handler<_, _, _>` is not satisfied
   --> src/main.rs:19:31
    |
19  |         .route("/users", post(users::create_user))
    |                          ---- ^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(axum::Json<CreateUser>, State<Pool<Postgres>>) -> impl Future<Output = Result<StatusCode, StatusCode>> {create_user}`
    |                          |
    |                          required by a bound introduced by this call
    |
    = help: the following other types implement trait `Handler<T, S, B>`:
              `Layered<L, H, T, S, B, B2>` implements `Handler<T, S, B2>`
              `MethodRouter<S, B>` implements `Handler<(), S, B>`
note: required by a bound in `post`
   --> C:\Users\maciej.suchanski\.cargo\registry\src\index.crates.io-6f17d22bba15001f\axum-0.6.20\src\routing\method_routing.rs:407:1
    |
407 | top_level_handler_fn!(post, POST);
    | ^^^^^^^^^^^^^^^^^^^^^^----^^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `post`
    = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0277]: the trait bound `fn(axum::extract::Path<i32>, axum::Json<UpdateUser>, State<Pool<Postgres>>) -> impl Future<Output = Result<StatusCode, StatusCode>> {update_user}: Handler<_, _, _>` is not satisfied
   --> src/main.rs:20:34
    |
20  |         .route("/users/:id", put(users::update_user))
    |                              --- ^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _, _>` is not implemented for fn item `fn(Path<i32>, Json<UpdateUser>, State<Pool<Postgres>>) -> impl Future<Output = ...> {update_user}`
    |                              |
    |                              required by a bound introduced by this call
    |
    = help: the following other types implement trait `Handler<T, S, B>`:
              `Layered<L, H, T, S, B, B2>` implements `Handler<T, S, B2>`
              `MethodRouter<S, B>` implements `Handler<(), S, B>`
note: required by a bound in `put`
   --> C:\Users\maciej.suchanski\.cargo\registry\src\index.crates.io-6f17d22bba15001f\axum-0.6.20\src\routing\method_routing.rs:408:1
    |
408 | top_level_handler_fn!(put, PUT);
    | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `put`
    = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

When i remove thoose 2 routes (post and put) api builds and works. Whole project code below:
db.rs

use sqlx::{PgPool, Pool, Postgres};
use std::env;

pub async fn init_pool() -> Pool<Postgres> {
    let database_url =
        env::var("DATABASE_URL").expect("DATABASE_URL must be set in the environment");
    PgPool::connect(&database_url)
        .await
        .expect("Failed to create database pool")
}

main.rs (vscode highlights users::create_user and users::update_user as errors)

use axum::{
    routing::{delete, get, post, put},
    Router,
};
use dotenv::dotenv;
use std::net::SocketAddr;

mod db;
mod users;

#[tokio::main]
async fn main() {
    dotenv().ok();
    let pool = db::init_pool().await;

    let app = Router::new()
        .route("/users", get(users::get_users))
        .route("/users/:id", get(users::get_user))
        .route("/users", post(users::create_user))
        .route("/users/:id", put(users::update_user))
        .route("/users/:id", delete(users::delete_user))
        .with_state(pool);

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("Listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

users.rs

use axum::{
    extract::{Json, Path, State},
    http::StatusCode,
    response::Json as AxumJson,
};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;

#[derive(Serialize, Deserialize, sqlx::FromRow)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub password: String,
    pub updated_at: Option<chrono::NaiveDateTime>,
    pub active: bool,
}

#[derive(Deserialize)]
pub struct CreateUser {
    pub username: String,
    pub password: String,
}

#[derive(Deserialize)]
pub struct UpdateUser {
    pub username: Option<String>,
    pub password: Option<String>,
    pub active: Option<bool>,
}

pub async fn get_users(State(pool): State<PgPool>) -> Result<AxumJson<Vec<User>>, StatusCode> {
    let users = sqlx::query_as!(
        User,
        r#"
        SELECT id, username, password, updated_at, active FROM users
        "#
    )
    .fetch_all(&pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(AxumJson(users))
}

pub async fn get_user(
    Path(id): Path<i32>,
    State(pool): State<PgPool>,
) -> Result<AxumJson<User>, StatusCode> {
    let user = sqlx::query_as!(
        User,
        "SELECT id, username, password, updated_at, active FROM users WHERE id = $1",
        id
    )
    .fetch_one(&pool)
    .await
    .map_err(|_| StatusCode::NOT_FOUND)?;

    Ok(AxumJson(user))
}

pub async fn create_user(
    Json(payload): Json<CreateUser>,
    State(pool): State<PgPool>,
) -> Result<StatusCode, StatusCode> {
    sqlx::query!(
        r#"
        INSERT INTO users (username, password, updated_at, active)
        VALUES ($1, $2, now(), true)
        "#,
        payload.username,
        payload.password
    )
    .execute(&pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(StatusCode::CREATED)
}

pub async fn update_user(
    Path(id): Path<i32>,
    Json(payload): Json<UpdateUser>,
    State(pool): State<PgPool>,
) -> Result<StatusCode, StatusCode> {
    sqlx::query!(
        r#"
        UPDATE users SET
            username = COALESCE($1, username),
            password = COALESCE($2, password),
            active = COALESCE($3, active),
            updated_at = now()
        WHERE id = $4
        "#,
        payload.username,
        payload.password,
        payload.active,
        id
    )
    .execute(&pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(StatusCode::OK)
}

pub async fn delete_user(
    Path(id): Path<i32>,
    State(pool): State<PgPool>,
) -> Result<StatusCode, StatusCode> {
    sqlx::query!("DELETE FROM users WHERE id = $1", id)
        .execute(&pool)
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(StatusCode::NO_CONTENT)
}

and just in case .env

DATABASE_URL=postgres://postgres:admin@localhost:5432/postgres

and .toml

[package]
name = "api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.6"
tokio = { version = "1", features = ["full"] }
sqlx = { version = "0.7", features = [
    "postgres",
    "runtime-tokio-native-tls",
    "chrono",
] }
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
dotenv = "0.15"
serde_json = "1.0"
hyper = { version = "0.14", features = ["full"] }
tower = "0.4"

Any ideas are welcome.

Just found out i can expand the errors by using #[axum::debug_handler]
(first needed to add it into dependencies as macros 'axum = { version = "0.6", features = ["macros"] }'), the problem was with order of in parameters. Compared to my code from the question i switched places the state(pool) and json(payload), now all works!

#[axum::debug_handler]
pub async fn update_user(
    Path(id): Path<i32>,
    State(pool): State<PgPool>,
    Json(payload): Json<UpdateUser>,
) -> Result<StatusCode, StatusCode> {
    sqlx::query!(
        r#"
        UPDATE users SET
            username = COALESCE($1, username),
            password = COALESCE($2, password),
            active = COALESCE($3, active),
            updated_at = now()
        WHERE id = $4
        "#,
        payload.username,
        payload.password,
        payload.active,
        id
    )
    .execute(&pool)
    .await
    .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(StatusCode::OK)
}
4 Likes