Expand description
§nest-rs-guards
Transport-spanning guards for nestrs — one trait, three transports
(HTTP, GraphQL, WS). Declared once with
App::builder().use_guards_global(...), every handler on every
transport runs through the chain.
Plug-in point for the Layer System: every guard is a [Layer], so the
#[routes] / #[resolver] / #[messages] shapers dedup by TypeId when
the same guard is declared at multiple sites (global + controller +
method) — the broadest LayerSite wins and the
rest log a warn. The framework runs guards in declaration order;
[Layer::priority] is an opt-in tiebreaker.
#[public] is not a framework-level skip: the macro attaches a
Public marker via the same metadata channel
as #[meta(...)], and each guard decides whether to honor it. An
AbilityGuard may still run on a public route to apply visitor rules;
an AuthGuard may skip rejection when no token is present.
§Defining a guard
Override only the check_* method(s) where this guard has work to do —
the rest inherit Ok(()) defaults. Layer provides priority() /
name() defaults; override priority() only when this guard must beat
declaration order.
use nest_rs_guards::prelude::*;
#[injectable]
#[derive(Default)]
pub struct AuditGuard;
impl Layer for AuditGuard {}
#[async_trait]
impl Guard for AuditGuard {
async fn check_http(&self, req: &mut HttpRequest) -> Result<(), Denial> {
tracing::info!(target: "audit", method = %req.method(), path = %req.uri(), "request");
Ok(())
}
}§Registering globally
use nest_rs::App;
use nest_rs_guards::{AppBuilderGuardsExt, guard};
App::builder()
.use_guards_global([guard::<AuthGuard>(), guard::<AuthzGuard>()])
.module::<AppModule>()
.build().await?
.run().awaitDeclaration order is the runtime order. If you list AuthzGuard before
AuthGuard the authorization check runs against an empty principal — a
name-based heuristic logs a warn at boot.
§Marking a handler #[public]
#[get("/health/live")]
#[public]
async fn live() -> &'static str { "ok" }The macro attaches a Public marker to the
route. Guards that want to honor it read it via the transport’s
reflector and adjust their policy.
§Architecture
Each shaper macro (#[routes], #[resolver], #[messages]) emits a
call to one of RouteShaper / run_layered_graphql_chain
/ run_layered_ws_chain at the start of every handler — the per-route
entry is what gives TypeId-level dedup against the global chain. Guards
have no transport-edge band: the pool executes in the shaper
(post-routing, so it reads #[public]), at a Guarded self-mount’s
edge (SelfMountGuardWrap), or in-band on /graphql (the
GlobalPoolOperationGuard
fallback when no bridge is registered).
Larger than its siblings on purpose. Where nest-rs-interceptors /
nest-rs-filters / nest-rs-exception-filters each carry only their own
trait + a builder + a registry, this crate also owns the cross-transport
dispatch machinery (the RouteShaper entry, the layer-chain helpers,
the graphql/ws chain runners) that the other three trio members consume.
Splitting it would mean duplicating the chain across crates or routing
through a fifth — both worse than the asymmetry.
Re-exports§
pub use dispatch::RouteShaper;pub use dispatch::denial_to_graphql_error;pub use dispatch::denial_to_http_response;pub use dispatch::run_layered_graphql_chain;pub use dispatch::run_layered_ws_chain;
Modules§
- dispatch
- Runtime dispatch of the Layer System chain — the helpers and types the three shaper macros emit at the start of every handler.
- layer_
chain - Layer chain composition — the dedup-by-
TypeIdlogic shared by every execution site of the Layer System (the per-route shaper, the transport pool folds, the GraphQL / WS in-band chains). - prelude
- Re-export everything a custom guard implementation needs in one
use.
Structs§
- Guard
AsWs Message Check - Newtype adapter that lets any
Guardsatisfy theWsMessageCheckinterface — the bridge the#[messages]macro uses to put guards in the per-event chain table without nest-rs-ws depending on nest-rs-guards. - Guard
Endpoint - Wraps any poem endpoint with a
Guard’scheck_httpstep. - Guard
Spec - One entry in the
use_guards_globallist. Created byguard::<T>(); resolved against the live container at configure time. - Guard
Specs - The unresolved
Vec<GuardSpec>seeded into the container byAppBuilder::use_guards_global(...). Each transport reads it at configure time and resolves against the live container. - Pipe
Spec - One entry in the
use_pipes_globallist — same shape asGuardSpec. - Pipe
Specs - The unresolved
Vec<PipeSpec>seeded byAppBuilder::use_pipes_global. - Resolved
Layer - A layer that survived dedup, paired with its origin site and the name the shaper logged at mount.
Enums§
- Denial
- What a
Guardreturns on rejection. - Layer
Site - Where a layer was declared. Used by the dedup logic — when the same
TypeIdappears at several sites, the broadest site wins because a wider declaration signals “this must run everywhere — don’t bypass it locally”.
Traits§
- AppBuilder
Guards Ext - Adds
.use_guards_global(...)toAppBuilder. - AppBuilder
Pipes Ext - Adds
.use_pipes_global(...)toAppBuilder. Each pipe runs before every JSON HTTP handler; per-route opt-out via#[no_pipes]. - Guard
- A transport-spanning guard.
- Guard
Ext - Extension trait so a poem endpoint can chain
.guard(arc_guard).
Functions§
- guard
- Construct a
GuardSpecfor the given guard type. - pipe
- Construct a
PipeSpecfor the given pipe type.
Type Aliases§
- Graphql
Next - Continuation passed to
Interceptor::wrap_graphql..awaitinvokes the next interceptor in the chain (or the resolver itself when this is the innermost wrap). - WsNext
- Continuation passed to
Interceptor::wrap_ws..awaitinvokes the next interceptor in the chain (or the message handler itself when this is the innermost wrap).