Register your authentication guard globally with the APP_GUARD token, so every route requires authentication unless you mark it public. Per controller guards are easy to forget on a new route, and a scan will find the one you missed. A global guard turns the default from open to closed.
What guards are
The NestJS documentation describes guards as classes that implement CanActivate and decide whether a request reaches the route handler, based on runtime conditions such as roles or permissions. They run after all middleware and before any interceptor or pipe.
That order matters. Validation pipes run after guards, so an unauthenticated request is rejected before its body is even validated.
Two ways to apply a guard
| Method | Runs where | Can use dependency injection |
|---|---|---|
| @UseGuards on a controller or route | Only where you add it | Yes |
| app.useGlobalGuards() | All routes | No, it runs outside any module |
| APP_GUARD provider in a module | All routes | Yes |
The docs explain that registering with APP_GUARD supports dependency injection, which useGlobalGuards does not. If you register several APP_GUARD providers, all run for every request in registration order.
providers: [
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_GUARD, useClass: RolesGuard },
],
Marking public routes
With a global guard, the safe pattern is to flag exceptions. A common approach is a custom decorator that sets metadata, which the guard reads through the Reflector and skips authentication for. Keep the list of public routes short and review it.
The same idea applies to rate limiting. The throttler package is also applied globally with APP_GUARD, and SkipThrottle exempts a route, so the exceptions are visible in code. See NestJS throttler setup behind a proxy.
Steps
- Count routes. List every controller method in the app.
- Add the authentication guard through APP_GUARD in the root module.
- Create a public decorator and apply it to health checks, login and anything meant to be open.
- Run your tests. Failures show routes that relied on being open without saying so.
- Add a test that walks all registered routes and asserts that any route without the public flag returns 401 without credentials.
- Run a scan against staging and confirm no unexpected route answers anonymously.
What guards do not cover
Authentication is not authorization. A guard that checks a token does not check that user A may read record B. Object level checks belong in the handler or service, where the record is known. OWASP recommends verifying, in every function that uses client input to access a record, that the logged in user may act on that specific record.
Try it yourself
Click through it at your own pace, or open it full screen. See how a route that answers anonymously is reported.
Safeguard's SBOM and dependency scanning covers your npm packages, and a free scan is available.
Quick check
Do guards run before validation pipes?
Yes. According to the NestJS docs they run after middleware and before interceptors and pipes.
Is useGlobalGuards or APP_GUARD better?
APP_GUARD, if your guard needs injected services, since useGlobalGuards runs outside any module.
Sources
Last reviewed: 19 October 2026.