rest_api/
rest-api.rs

1//! Auth API Example
2//! Run:
3//!   cargo run --example rest-api
4
5use rapid_rs::prelude::*;
6
7#[derive(Serialize, Deserialize, Clone, ToSchema)]
8struct User {
9    id: Uuid,
10    email: String,
11    name: String,
12    created_at: DateTime<Utc>,
13}
14
15#[derive(Deserialize, Validate, ToSchema)]
16struct CreateUserRequest {
17    #[validate(email(message = "Invalid email format"))]
18    email: String,
19
20    #[validate(length(
21        min = 2,
22        max = 100,
23        message = "Name must be between 2 and 100 characters"
24    ))]
25    name: String,
26}
27
28#[derive(Deserialize, Validate, ToSchema)]
29struct UpdateUserRequest {
30    #[validate(length(
31        min = 2,
32        max = 100,
33        message = "Name must be between 2 and 100 characters"
34    ))]
35    name: Option<String>,
36}
37
38// In-memory "database" for demo purposes
39use std::collections::HashMap;
40use std::sync::{Arc, Mutex};
41
42type Database = Arc<Mutex<HashMap<Uuid, User>>>;
43
44/// Create a new user
45async fn create_user(
46    State(db): State<Database>,
47    ValidatedJson(payload): ValidatedJson<CreateUserRequest>,
48) -> ApiResult<User> {
49    let user = User {
50        id: Uuid::new_v4(),
51        email: payload.email,
52        name: payload.name,
53        created_at: Utc::now(),
54    };
55
56    db.lock().unwrap().insert(user.id, user.clone());
57
58    Ok(Json(user))
59}
60
61/// Get all users
62async fn list_users(State(db): State<Database>) -> ApiResult<Vec<User>> {
63    let users: Vec<User> = db.lock().unwrap().values().cloned().collect();
64    Ok(Json(users))
65}
66
67/// Get a user by ID
68async fn get_user(
69    State(db): State<Database>,
70    Path(id): Path<Uuid>,
71) -> Result<Json<User>, ApiError> {
72    let db = db.lock().unwrap();
73    let user = db
74        .get(&id)
75        .ok_or_else(|| ApiError::NotFound(format!("User with id {} not found", id)))?;
76
77    Ok(Json(user.clone()))
78}
79
80/// Update a user
81async fn update_user(
82    State(db): State<Database>,
83    Path(id): Path<Uuid>,
84    ValidatedJson(payload): ValidatedJson<UpdateUserRequest>,
85) -> Result<Json<User>, ApiError> {
86    let mut db = db.lock().unwrap();
87    let user = db
88        .get_mut(&id)
89        .ok_or_else(|| ApiError::NotFound(format!("User with id {} not found", id)))?;
90
91    if let Some(name) = payload.name {
92        user.name = name;
93    }
94
95    Ok(Json(user.clone()))
96}
97
98/// Delete a user
99async fn delete_user(
100    State(db): State<Database>,
101    Path(id): Path<Uuid>,
102) -> Result<Json<User>, ApiError> {
103    let mut db = db.lock().unwrap();
104    let user = db
105        .remove(&id)
106        .ok_or_else(|| ApiError::NotFound(format!("User with id {} not found", id)))?;
107
108    Ok(Json(user))
109}
110
111fn user_routes() -> Router<Database> {
112    Router::new()
113        .route("/users", post(create_user))
114        .route("/users", get(list_users))
115        .route("/users/:id", get(get_user))
116        .route("/users/:id", patch(update_user))
117        .route("/users/:id", delete(delete_user))
118}
119
120#[tokio::main]
121async fn main() {
122    // Create shared database
123    let db: Database = Arc::new(Mutex::new(HashMap::new()));
124
125    // Build and run the app
126    App::new()
127        .auto_configure()
128        .mount(user_routes().with_state(db))
129        .run()
130        .await
131        .unwrap();
132}