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.