Guards and Interceptors
Protect your routes and wrap handler execution with custom logic.
NestForge provides two primary mechanisms to intercept and control the request execution flow: Guards for access control and Interceptors for execution wrapping.
Guards
Section titled “Guards”Guards have a single responsibility. They determine whether a given request will be handled by the route handler or not, based on certain conditions (like permissions, roles, ACLs, etc.) present at run-time.
Why Guards?
Section titled “Why Guards?”In traditional web applications, authentication and authorization were often handled by middleware. While middleware is a fine choice for things like logging or header manipulation, it’s not ideal for authorization because it has no knowledge of which handler will be executed after it.
Guards have access to the RequestContext, metadata, and can be applied at the controller or route level.
Creating a Guard
Section titled “Creating a Guard”The easiest way to create a guard is using the nestforge::guard! macro.
nestforge::guard!(ApiKeyGuard, |ctx| { let has_key = ctx.headers.get("x-api-key").is_some();
if !has_key { return Err(nestforge::HttpException::unauthorized("Missing API Key")); }
Ok(())});Applying Guards
Section titled “Applying Guards”You can apply guards at the controller level or route level using the #[nestforge::use_guards()] attribute.
#[controller("/admin")]#[nestforge::use_guards(ApiKeyGuard)] // Applied to all routes in this controllerpub struct AdminController;
#[routes]impl AdminController { #[nestforge::get("/secret")] #[nestforge::use_guards(SuperAdminGuard)] // Additional guard for this specific route async fn get_secret() -> ApiResult<String> { ... }}Interceptors
Section titled “Interceptors”Interceptors are inspired by the Aspect-Oriented Programming (AOP) technique. They allow you to wrap the handler execution, giving you the ability to:
- Bind extra logic before and after method execution.
- Transform the result returned from a function.
- Transform the exception thrown from a function.
- Completely override the handler execution (e.g., for caching purposes).
Creating an Interceptor
Section titled “Creating an Interceptor”Using the nestforge::interceptor! macro:
nestforge::interceptor!(LoggingInterceptor, |ctx, req, next| { let start = std::time::Instant::now(); let method = ctx.method.clone(); let path = ctx.uri.path().to_string();
let response = (next)(req).await;
println!("[{}] {} took {}ms", method, path, start.elapsed().as_millis()); response});Use Case: Response Mapping
Section titled “Use Case: Response Mapping”Interceptors are perfect for transforming global response shapes.
nestforge::interceptor!(TransformInterceptor, |ctx, req, next| { let response = (next)(req).await; // Map the success response into a standard format // ... response});Summary Comparison
Section titled “Summary Comparison”| Feature | Middleware | Guards | Interceptors |
|---|---|---|---|
| Aware of Handler? | No | Yes | Yes |
| Execution Order | 1st | 2nd | 3rd (Wraps Handler) |
| Best Use Case | Raw HTTP details (CORS, Logs) | Authorization & Permissions | Logic wrapping, Caching, Mapping |
| Can block request? | Yes | Yes | Yes |
Global Registration
Section titled “Global Registration”You can register guards and interceptors globally in your main.rs:
NestForgeFactory::<AppModule>::create()? .use_guard::<GlobalAuthGuard>() .use_interceptor::<GlobalLoggingInterceptor>() .listen(3000) .await?;