Skip to main content

nestrs_core/
guard.rs

1//! Route guards ([`CanActivate`]) — run before the handler (NestJS `UseGuards` analogue).
2
3use async_trait::async_trait;
4use axum::http::request::Parts;
5use axum::response::{IntoResponse, Response};
6use serde_json::json;
7
8/// Failure returned from [`CanActivate::can_activate`]; becomes a JSON error body (401 / 403).
9#[derive(Debug, Clone)]
10pub enum GuardError {
11    Unauthorized(String),
12    Forbidden(String),
13}
14
15impl GuardError {
16    pub fn unauthorized(message: impl Into<String>) -> Self {
17        Self::Unauthorized(message.into())
18    }
19
20    pub fn forbidden(message: impl Into<String>) -> Self {
21        Self::Forbidden(message.into())
22    }
23}
24
25impl IntoResponse for GuardError {
26    fn into_response(self) -> Response {
27        let (status, message, error_label) = match &self {
28            GuardError::Unauthorized(m) => (
29                axum::http::StatusCode::UNAUTHORIZED,
30                m.clone(),
31                "Unauthorized",
32            ),
33            GuardError::Forbidden(m) => (axum::http::StatusCode::FORBIDDEN, m.clone(), "Forbidden"),
34        };
35        let body = axum::Json(json!({
36            "statusCode": status.as_u16(),
37            "message": message,
38            "error": error_label,
39        }));
40        (status, body).into_response()
41    }
42}
43
44/// Authorize the request before the handler runs. Declare per-route guard types in the `impl_routes!`
45/// macro: `GET "/x" with (A, B) => MyController::handler,` — use `with ()` when there are no route guards.
46/// For a guard on **all** routes of a controller, use `controller_guards (G)` on `impl_routes!` (see the
47/// `nestrs` crate); that runs **outside** route-level guards.
48///
49/// Stateless guards are usually unit structs with [`Default`].
50#[async_trait]
51pub trait CanActivate: Default + Send + Sync + 'static {
52    async fn can_activate(&self, parts: &Parts) -> Result<(), GuardError>;
53}