nestforge 1.9.0

NestJS-inspired modular backend framework for Rust
Documentation
use axum::{
    body::Body,
    http::{Request, StatusCode},
};
use tower::ServiceExt;

#[derive(Clone)]
struct UserEntity {
    id: u64,
    email: String,
    password_hash: String,
}

#[nestforge::response_dto]
struct UserDto {
    id: u64,
    email: String,
}

struct UserSerializer;

impl nestforge::ResponseSerializer<UserEntity> for UserSerializer {
    type Output = UserDto;

    fn serialize(value: UserEntity) -> Self::Output {
        let _ = value.password_hash;
        UserDto {
            id: value.id,
            email: value.email,
        }
    }
}

#[nestforge::controller("/serializers")]
#[derive(Default)]
struct SerializerController;

#[nestforge::routes]
impl SerializerController {
    #[nestforge::get("/user")]
    async fn user() -> nestforge::ApiSerializedResult<UserEntity, UserSerializer> {
        Ok(nestforge::Serialized::new(UserEntity {
            id: 7,
            email: "alice@example.com".to_string(),
            password_hash: "secret".to_string(),
        }))
    }
}

#[nestforge::module(controllers = [SerializerController])]
#[derive(Default)]
struct SerializerModule;

impl SerializerModule {}

#[tokio::test]
async fn response_serializers_shape_handler_payloads() {
    let app = nestforge::NestForgeFactory::<SerializerModule>::create()
        .expect("factory")
        .into_router();

    let response = app
        .oneshot(
            Request::builder()
                .uri("/serializers/user")
                .body(Body::empty())
                .expect("request"),
        )
        .await
        .expect("response");

    assert_eq!(response.status(), StatusCode::OK);

    let body = axum::body::to_bytes(response.into_body(), usize::MAX)
        .await
        .expect("body");
    let json: serde_json::Value = serde_json::from_slice(&body).expect("json");

    assert_eq!(json["id"], 7);
    assert_eq!(json["email"], "alice@example.com");
    assert!(json.get("password_hash").is_none());
}