Lesson 38 of 5020 min read

How to Protect Routes Using Guards and Custom Decorators in NestJS

Learn how to combine guards and custom parameter decorators in NestJS to cleanly protect routes and access the authenticated user's data.

Author: CodersNexus

How to Protect Routes Using Guards and Custom Decorators in NestJS

You've now learned guards, JWT strategies, and RBAC individually across this module. This lesson pulls those pieces together into the practical patterns you'll actually reach for in a real codebase: applying authentication broadly (even globally), cleanly extracting the current user without repeating boilerplate in every controller, and explicitly marking specific routes as public exceptions.

Protect Routes Guards Custom Decorators NestJS: Learning Objectives

  • Apply an authentication guard globally across an entire application.
  • Build a custom @Public() decorator to exempt specific routes from global authentication.
  • Build a custom @CurrentUser() parameter decorator to cleanly access request.user.
  • Combine global guards, exemption decorators, and parameter decorators in one cohesive pattern.
  • Understand the trade-offs of global versus per-route guard application.

Protect Routes Guards Custom Decorators NestJS: Key Terms and Definitions

  • Global guard: A guard registered using app.useGlobalGuards(), applied to every route in the application by default.
  • @Public() decorator: A custom decorator marking specific routes as exempt from an otherwise globally-applied authentication guard.
  • @CurrentUser() decorator: A custom parameter decorator that extracts the authenticated user directly from the request, avoiding repetitive @Req() req access.
  • createParamDecorator(): A NestJS function used to build custom parameter decorators like @CurrentUser().
  • Guard exemption pattern: The combination of a global guard plus a metadata-based decorator (like @Public()) used to selectively bypass that guard for specific routes.

How Protect Routes Guards Custom Decorators NestJS Works: Detailed Explanation

Applying an authentication guard individually to every single controller, as shown in earlier lessons, works but doesn't scale well as an application grows to dozens of controllers; it's easy to forget to protect a new route. A common, more robust pattern instead applies the authentication guard globally, using app.useGlobalGuards(new JwtAuthGuard()) (typically a thin custom guard wrapping AuthGuard('jwt')) in main.ts, meaning every route is protected by default, and developers must make a deliberate choice to expose anything publicly, rather than accidentally forgetting to protect something.

This global-by-default approach creates an obvious problem: some routes, like login, registration, or a public health-check endpoint, genuinely need to remain accessible without authentication. The standard solution mirrors the RBAC pattern from earlier in this module: a custom @Public() decorator, built using SetMetadata('isPublic', true), marks specific routes as exempt. Your global guard's canActivate() method then uses Reflector to check for this metadata before enforcing authentication, immediately returning true (allowing the request through) if a route is marked public, and only performing the actual JWT verification otherwise.

The second practical pattern this lesson covers addresses a smaller but very common annoyance: repeatedly writing @Req() req and then manually accessing req.user in every single protected route handler. A custom parameter decorator, conventionally named @CurrentUser(), built using NestJS's createParamDecorator() function, cleans this up significantly. Internally, it accesses the ExecutionContext, switches to the HTTP context to get the underlying request object, and simply returns request.user, letting any protected route handler declare @CurrentUser() user: AuthenticatedUser directly as a parameter, exactly as naturally as @Body() or @Param(), rather than digging into a raw request object every time.

Combining both patterns, a global authentication guard with a @Public() exemption decorator, plus a @CurrentUser() parameter decorator, produces the clean, boilerplate-free authentication experience found in most well-architected, real-world NestJS codebases: routes are secure by default, explicitly and visibly marked when they're not, and accessing the authenticated user inside a handler feels like a completely natural part of NestJS's parameter decoration system rather than an afterthought.

Interview-Friendly Explanation

A strong interview or viva answer for protect routes guards custom decorators nestjs must go beyond a one-line definition. Interviewers evaluating BCA, MCA, B.Tech, and self-taught backend developers reward answers that combine a precise definition, a clear working explanation, a real-world example, and a conclusion that highlights why it matters for securing production Node.js applications. Use the four-point framework below when answering under time pressure.

  • Definition point: Global guard: A guard registered using app.useGlobalGuards(), applied to every route in the application by default.
  • Working point: Build a custom @Public() decorator to exempt specific routes from global authentication.
  • Example point: Production NestJS codebases overwhelmingly favor this exact global-guard-plus-@Public()-exemption pattern over manually applying @UseGuards() to every individual controller, specifically to reduce the risk of a newly added route accidentally being left unprotected.
  • Conclusion point: Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.

How to Answer This in a Technical Interview

  1. Give a two-to-three sentence definition using correct authentication and security terminology.
  2. Add one specific example drawn from a real backend scenario such as a banking, SaaS, or e-commerce login flow.
  3. Mention any security trade-offs (e.g. JWT vs sessions, access vs refresh tokens) since interviewers often probe this contrast.
  4. Close with one benefit, limitation, or production security consideration.
  5. Avoid vague answers like "it just checks if the user is logged in" — interviewers filter these out immediately.

Practical Scenario

Imagine you are securing a production SaaS backend, similar to systems used at companies like Razorpay, Freshworks, or Zomato. Every lesson in this module maps directly to a decision you will make while protecting that backend: how users prove who they are, how long that proof stays valid, who is allowed to do what, and how you defend against abuse.

Protect Routes Guards Custom Decorators NestJS: Architecture and Flow Diagram

Visualize the combined pattern:

[main.ts: app.useGlobalGuards(new JwtAuthGuard())] --> Every route protected by default
|
[Route decorated with @Public()] --> [JwtAuthGuard checks Reflector for 'isPublic' metadata] --> [true: skip auth check, allow through]
[Route NOT decorated with @Public()] --> [JwtAuthGuard performs full JWT verification via AuthGuard('jwt') logic]

[Inside any protected handler] --> [@CurrentUser() user] --> [Returns request.user directly, no manual @Req() needed]

Protect Routes Guards Custom Decorators NestJS: Pattern Reference Table

PatternPurpose
Global guard (app.useGlobalGuards())Protects every route by default, reducing the risk of forgetting to secure a new endpoint
@Public() decoratorExplicitly and visibly exempts specific routes (login, health checks) from the global guard
@CurrentUser() decoratorCleanly extracts the authenticated user without repetitive @Req() access in every handler

Protect Routes Guards Custom Decorators NestJS: NestJS Code Example

// public.decorator.ts
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

// jwt-auth.guard.ts — a global guard that respects @Public()
import { Injectable, ExecutionContext } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './public.decorator';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private reflector: Reflector) {
    super();
  }

  canActivate(context: ExecutionContext) {
    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (isPublic) {
      return true; // skip authentication entirely for this route
    }
    return super.canActivate(context); // otherwise, defer to the standard JWT guard logic
  }
}

// current-user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const CurrentUser = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

// main.ts — applying the guard globally
import { NestFactory } from '@nestjs/core';
import { Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
import { JwtAuthGuard } from './auth/jwt-auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalGuards(new JwtAuthGuard(app.get(Reflector)));
  await app.listen(3000);
}
bootstrap();

// example usage across two controllers
import { Controller, Post, Get, Body } from '@nestjs/common';
import { Public } from './auth/public.decorator';
import { CurrentUser } from './auth/current-user.decorator';

@Controller('auth')
export class AuthController {
  @Public() // exempt from the global guard
  @Post('login')
  login(@Body() body: { email: string; password: string }) {
    return { message: 'Login logic here' };
  }
}

@Controller('profile')
export class ProfileController {
  @Get() // protected by default via the global guard
  getProfile(@CurrentUser() user: any) {
    return { message: 'Your profile', user };
  }
}

JwtAuthGuard extends AuthGuard('jwt') rather than replacing it, overriding canActivate() to first check, via Reflector.getAllAndOverride(), whether the current route or its controller is marked with @Public() metadata; if so, it returns true immediately, bypassing JWT verification entirely, and otherwise defers to the parent class's standard behavior with super.canActivate(context). Registering this guard globally in main.ts means every route across the entire application is protected unless explicitly marked otherwise. AuthController's login route uses @Public() to remain accessible without a token, exactly as it must be, while ProfileController's getProfile route is protected by default (no decorator needed) and uses @CurrentUser() to cleanly receive the authenticated user directly as a parameter, with none of the manual @Req() and req.user boilerplate seen in earlier lessons.

Real-World Protect Routes Guards Custom Decorators NestJS Industry Examples

  • Production NestJS codebases overwhelmingly favor this exact global-guard-plus-@Public()-exemption pattern over manually applying @UseGuards() to every individual controller, specifically to reduce the risk of a newly added route accidentally being left unprotected.
  • Health-check and monitoring endpoints (like GET /health, used by load balancers and uptime monitors) are almost universally marked @Public(), since they must remain accessible without authentication for infrastructure tooling to function.
  • Teams building internal style guides or NestJS boilerplate templates for their organization typically include a @CurrentUser() decorator exactly like this one as a standard, shared utility across every project.
  • Code review checklists at security-conscious companies often specifically flag any new controller or route that lacks either implicit global protection or an explicit @Public() marker, treating unprotected routes as a default red flag requiring justification.

Protect Routes Guards Custom Decorators NestJS Interview Questions and Answers

Q1. What is the advantage of applying an authentication guard globally instead of individually on every controller?

Short answer: A global guard, applied via app.useGlobalGuards(), protects every route by default, meaning a newly added controller or route is automatically secured without requiring a developer to remember to add @UseGuards() manually, reducing the risk of accidentally shipping an unprotected endpoint.

Detailed explanation: Applying an authentication guard individually to every single controller, as shown in earlier lessons, works but doesn't scale well as an application grows to dozens of controllers; it's easy to forget to protect a new route. A common, more robust pattern instead applies the authentication guard globally, using app.useGlobalGuards(new JwtAuthGuard()) (typically a thin custom guard wrapping AuthGuard('jwt')) in main.ts, meaning every route is protected by default, and developers must make a deliberate choice to expose anything publicly, rather than accidentally forgetting to protect something. This global-by-default approach creates an obvious problem: some routes, like login, registration, or a public health-check endpoint, genuinely need to remain accessible without authentication. The standard solution mirrors the RBAC pattern from earlier in this module: a custom @Public() decorator, built using SetMetadata('isPublic', true), marks specific routes as exempt. Your global guard's canActivate() method then uses Reflector to check for this metadata before enforcing authentication, immediately returning true (allowing the request through) if a route is marked public, and only performing the actual JWT verification otherwise. The second practical pattern this lesson covers addresses a smaller but very common annoyance: repeatedly writing @Req() req and then manually accessing req.user in every single protected route handler. A custom parameter decorator, conventionally named @CurrentUser(), built using NestJS's createParamDecorator() function, cleans this up significantly. Internally, it accesses the ExecutionContext, switches to the HTTP context to get the underlying request object, and simply returns request.user, letting any protected route handler declare @CurrentUser() user: AuthenticatedUser directly as a parameter, exactly as naturally as @Body() or @Param(), rather than digging into a raw request object every time. Combining both patterns, a global authentication guard with a @Public() exemption decorator, plus a @CurrentUser() parameter decorator, produces the clean, boilerplate-free authentication experience found in most well-architected, real-world NestJS codebases: routes are secure by default, explicitly and visibly marked when they're not, and accessing the authenticated user inside a handler feels like a completely natural part of NestJS's parameter decoration system rather than an afterthought.

Practical example: Production NestJS codebases overwhelmingly favor this exact global-guard-plus-@Public()-exemption pattern over manually applying @UseGuards() to every individual controller, specifically to reduce the risk of a newly added route accidentally being left unprotected.

Interview tip: app.useGlobalGuards() applies a guard to every route by default, reducing risk of accidentally unprotected endpoints.

Revision hook: Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.

Q2. How would you exempt a specific route, like a login endpoint, from a globally-applied authentication guard?

Short answer: You build a custom @Public() decorator using SetMetadata to attach an 'isPublic' flag to that specific route, and modify the global guard's canActivate() method to check for this metadata using Reflector first, immediately allowing the request through if the route is marked public, before performing any actual authentication check.

Detailed explanation: JwtAuthGuard extends AuthGuard('jwt') rather than replacing it, overriding canActivate() to first check, via Reflector.getAllAndOverride(), whether the current route or its controller is marked with @Public() metadata; if so, it returns true immediately, bypassing JWT verification entirely, and otherwise defers to the parent class's standard behavior with super.canActivate(context). Registering this guard globally in main.ts means every route across the entire application is protected unless explicitly marked otherwise. AuthController's login route uses @Public() to remain accessible without a token, exactly as it must be, while ProfileController's getProfile route is protected by default (no decorator needed) and uses @CurrentUser() to cleanly receive the authenticated user directly as a parameter, with none of the manual @Req() and req.user boilerplate seen in earlier lessons.

Practical example: Health-check and monitoring endpoints (like GET /health, used by load balancers and uptime monitors) are almost universally marked @Public(), since they must remain accessible without authentication for infrastructure tooling to function.

Interview tip: @Public() (via SetMetadata) plus Reflector checking inside the guard is the standard exemption pattern for global guards.

Revision hook: The @Public() plus Reflector pattern mirrors the RBAC pattern from earlier in this module — the same technique solves both problems.

Q3. What is the purpose of a custom @CurrentUser() decorator?

Short answer: @CurrentUser() is a custom parameter decorator, built using createParamDecorator(), that extracts request.user directly, allowing a protected route handler to declare it as a simple parameter (like @CurrentUser() user) instead of repeatedly writing @Req() req and manually accessing req.user in every single handler.

Detailed explanation: Real-world NestJS applications typically combine three practical patterns on top of the authentication and authorization concepts covered earlier in this module. A global authentication guard, registered via app.useGlobalGuards(), protects every route by default, reducing the risk of an accidentally unprotected endpoint. A custom @Public() decorator, built with SetMetadata and checked using Reflector inside the guard, explicitly and visibly exempts specific routes, like login or health checks, from this default protection. Finally, a custom @CurrentUser() parameter decorator, built with createParamDecorator(), cleanly extracts the authenticated user from the request, eliminating repetitive @Req() and req.user access across every protected handler. Together, these patterns produce the clean, secure-by-default, boilerplate-free authentication experience found throughout well-architected, production NestJS codebases.

Practical example: Teams building internal style guides or NestJS boilerplate templates for their organization typically include a @CurrentUser() decorator exactly like this one as a standard, shared utility across every project.

Interview tip: createParamDecorator() builds custom parameter decorators like @CurrentUser(), cleanly extracting request.user.

Revision hook: Custom parameter decorators like @CurrentUser() are a small investment that meaningfully cleans up every protected route handler you write afterward.

Q4. How does extending AuthGuard('jwt') in a custom JwtAuthGuard class allow for the @Public() exemption pattern?

Short answer: By extending AuthGuard('jwt') rather than using it directly, you can override its canActivate() method to add custom logic, such as checking for @Public() metadata first, and then explicitly call the parent class's original canActivate() logic via super.canActivate() only when the route isn't exempted, cleanly layering custom behavior on top of the standard strategy-based guard.

Detailed explanation: Applying an authentication guard individually to every single controller, as shown in earlier lessons, works but doesn't scale well as an application grows to dozens of controllers; it's easy to forget to protect a new route. A common, more robust pattern instead applies the authentication guard globally, using app.useGlobalGuards(new JwtAuthGuard()) (typically a thin custom guard wrapping AuthGuard('jwt')) in main.ts, meaning every route is protected by default, and developers must make a deliberate choice to expose anything publicly, rather than accidentally forgetting to protect something. This global-by-default approach creates an obvious problem: some routes, like login, registration, or a public health-check endpoint, genuinely need to remain accessible without authentication. The standard solution mirrors the RBAC pattern from earlier in this module: a custom @Public() decorator, built using SetMetadata('isPublic', true), marks specific routes as exempt. Your global guard's canActivate() method then uses Reflector to check for this metadata before enforcing authentication, immediately returning true (allowing the request through) if a route is marked public, and only performing the actual JWT verification otherwise. The second practical pattern this lesson covers addresses a smaller but very common annoyance: repeatedly writing @Req() req and then manually accessing req.user in every single protected route handler. A custom parameter decorator, conventionally named @CurrentUser(), built using NestJS's createParamDecorator() function, cleans this up significantly. Internally, it accesses the ExecutionContext, switches to the HTTP context to get the underlying request object, and simply returns request.user, letting any protected route handler declare @CurrentUser() user: AuthenticatedUser directly as a parameter, exactly as naturally as @Body() or @Param(), rather than digging into a raw request object every time. Combining both patterns, a global authentication guard with a @Public() exemption decorator, plus a @CurrentUser() parameter decorator, produces the clean, boilerplate-free authentication experience found in most well-architected, real-world NestJS codebases: routes are secure by default, explicitly and visibly marked when they're not, and accessing the authenticated user inside a handler feels like a completely natural part of NestJS's parameter decoration system rather than an afterthought.

Practical example: Code review checklists at security-conscious companies often specifically flag any new controller or route that lacks either implicit global protection or an explicit @Public() marker, treating unprotected routes as a default red flag requiring justification.

Interview tip: Extending AuthGuard('jwt') in a custom guard class lets you layer additional logic (like @Public() checks) on top of standard strategy-based verification.

Revision hook: These combined patterns represent the practical, production-grade authentication setup you'll actually build and maintain in real NestJS projects.

Protect Routes Guards Custom Decorators NestJS MCQs and Practice Questions

1. Which method is used to register a guard globally across an entire NestJS application?

  1. @UseGuards() on AppModule
  2. app.useGlobalGuards()
  3. app.useGlobalPipes()
  4. @Global() decorator

Answer: B. app.useGlobalGuards()

Explanation: app.useGlobalGuards(), called during bootstrapping in main.ts, registers a guard to run on every route across the entire application by default.

Concept link: app.useGlobalGuards() applies a guard to every route by default, reducing risk of accidentally unprotected endpoints.

Why this matters: Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.

2. What is the purpose of a custom @Public() decorator when using a global authentication guard?

  1. To make a route faster
  2. To explicitly exempt a specific route from the otherwise globally-applied authentication check
  3. To automatically log the user in
  4. To disable validation on that route

Answer: B. To explicitly exempt a specific route from the otherwise globally-applied authentication check

Explanation: @Public(), typically built with SetMetadata, marks specific routes (like login or health checks) as exceptions to a global authentication guard, which the guard's logic checks for using Reflector before enforcing authentication.

Concept link: @Public() (via SetMetadata) plus Reflector checking inside the guard is the standard exemption pattern for global guards.

Why this matters: The @Public() plus Reflector pattern mirrors the RBAC pattern from earlier in this module — the same technique solves both problems.

3. What NestJS function is used to build a custom parameter decorator like @CurrentUser()?

  1. SetMetadata()
  2. createParamDecorator()
  3. Reflector.get()
  4. UseGuards()

Answer: B. createParamDecorator()

Explanation: createParamDecorator() is the NestJS utility specifically used to build custom decorators that extract data from the current request and inject it directly as a method parameter.

Concept link: createParamDecorator() builds custom parameter decorators like @CurrentUser(), cleanly extracting request.user.

Why this matters: Custom parameter decorators like @CurrentUser() are a small investment that meaningfully cleans up every protected route handler you write afterward.

4. In the JwtAuthGuard example, what does calling super.canActivate(context) do?

  1. It skips authentication entirely
  2. It defers to the original AuthGuard('jwt') logic for actual JWT verification
  3. It always returns false
  4. It logs the request without checking authentication

Answer: B. It defers to the original AuthGuard('jwt') logic for actual JWT verification

Explanation: Since JwtAuthGuard extends AuthGuard('jwt'), calling super.canActivate(context) invokes the original, standard JWT verification logic provided by the parent class, used whenever a route isn't exempted via @Public().

Concept link: Extending AuthGuard('jwt') in a custom guard class lets you layer additional logic (like @Public() checks) on top of standard strategy-based verification.

Why this matters: These combined patterns represent the practical, production-grade authentication setup you'll actually build and maintain in real NestJS projects.

Common Protect Routes Guards Custom Decorators NestJS Mistakes to Avoid

  • Applying a global guard without any exemption mechanism, accidentally locking legitimately public routes like login or health checks behind authentication.
  • Forgetting to inject Reflector into a custom guard that needs to read metadata like @Public(), causing the exemption check to fail.
  • Repeatedly writing @Req() req and manually accessing req.user across many controllers instead of building a single, reusable @CurrentUser() decorator.
  • Placing @Public() only on a controller class when a more granular, method-level exemption was actually intended, accidentally exposing every route in that controller.

Protect Routes Guards Custom Decorators NestJS: Interview Notes and Exam Tips

  • app.useGlobalGuards() applies a guard to every route by default, reducing risk of accidentally unprotected endpoints.
  • @Public() (via SetMetadata) plus Reflector checking inside the guard is the standard exemption pattern for global guards.
  • createParamDecorator() builds custom parameter decorators like @CurrentUser(), cleanly extracting request.user.
  • Extending AuthGuard('jwt') in a custom guard class lets you layer additional logic (like @Public() checks) on top of standard strategy-based verification.

Key Protect Routes Guards Custom Decorators NestJS Takeaways

  • Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.
  • The @Public() plus Reflector pattern mirrors the RBAC pattern from earlier in this module — the same technique solves both problems.
  • Custom parameter decorators like @CurrentUser() are a small investment that meaningfully cleans up every protected route handler you write afterward.
  • These combined patterns represent the practical, production-grade authentication setup you'll actually build and maintain in real NestJS projects.

Protect Routes Guards Custom Decorators NestJS: Summary

Real-world NestJS applications typically combine three practical patterns on top of the authentication and authorization concepts covered earlier in this module. A global authentication guard, registered via app.useGlobalGuards(), protects every route by default, reducing the risk of an accidentally unprotected endpoint. A custom @Public() decorator, built with SetMetadata and checked using Reflector inside the guard, explicitly and visibly exempts specific routes, like login or health checks, from this default protection. Finally, a custom @CurrentUser() parameter decorator, built with createParamDecorator(), cleanly extracts the authenticated user from the request, eliminating repetitive @Req() and req.user access across every protected handler. Together, these patterns produce the clean, secure-by-default, boilerplate-free authentication experience found throughout well-architected, production NestJS codebases.

Frequently Asked Questions

Applying an authentication guard globally is generally considered a safer default for most applications, since it protects new routes automatically, reducing the risk of human error. Individual, per-controller application still has its place for more specialized guards (like role-based ones) that shouldn't apply universally. In interviews, tie this back to: app.useGlobalGuards() applies a guard to every route by default, reducing risk of accidentally unprotected endpoints. In real applications, consider this example: Production NestJS codebases overwhelmingly favor this exact global-guard-plus-@Public()-exemption pattern over manually applying @UseGuards() to every individual controller, specifically to reduce the risk of a newly added route accidentally being left unprotected. Key revision takeaway: Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.

Extending AuthGuard('jwt') lets you reuse all of Passport's existing, well-tested JWT verification logic, while still being able to override canActivate() to layer additional custom behavior, like the @Public() exemption check, on top of that existing logic rather than reimplementing JWT verification manually. In interviews, tie this back to: @Public() (via SetMetadata) plus Reflector checking inside the guard is the standard exemption pattern for global guards. In real applications, consider this example: Health-check and monitoring endpoints (like GET /health, used by load balancers and uptime monitors) are almost universally marked @Public(), since they must remain accessible without authentication for infrastructure tooling to function. Key revision takeaway: The @Public() plus Reflector pattern mirrors the RBAC pattern from earlier in this module — the same technique solves both problems.

Yes, createParamDecorator() supports an optional 'data' argument, so you could implement @CurrentUser('email') to extract just the email property from request.user specifically, rather than always returning the entire user object, depending on how you implement the decorator's internal logic. In interviews, tie this back to: createParamDecorator() builds custom parameter decorators like @CurrentUser(), cleanly extracting request.user. In real applications, consider this example: Teams building internal style guides or NestJS boilerplate templates for their organization typically include a @CurrentUser() decorator exactly like this one as a standard, shared utility across every project. Key revision takeaway: Custom parameter decorators like @CurrentUser() are a small investment that meaningfully cleans up every protected route handler you write afterward.

The login route itself would become protected by the global guard, meaning a request to log in would first need to already be authenticated, which is a contradiction, effectively locking users out entirely, since they'd need a valid token to reach the very route meant to issue one. In interviews, tie this back to: Extending AuthGuard('jwt') in a custom guard class lets you layer additional logic (like @Public() checks) on top of standard strategy-based verification. In real applications, consider this example: Code review checklists at security-conscious companies often specifically flag any new controller or route that lacks either implicit global protection or an explicit @Public() marker, treating unprotected routes as a default red flag requiring justification. Key revision takeaway: These combined patterns represent the practical, production-grade authentication setup you'll actually build and maintain in real NestJS projects.

Yes, if implemented using reflector.getAllAndOverride() checking both context.getHandler() and context.getClass() (as shown in this lesson's example), @Public() can be applied at either the method level for a single route or the class level to exempt every route within that controller. In interviews, tie this back to: app.useGlobalGuards() applies a guard to every route by default, reducing risk of accidentally unprotected endpoints. In real applications, consider this example: Production NestJS codebases overwhelmingly favor this exact global-guard-plus-@Public()-exemption pattern over manually applying @UseGuards() to every individual controller, specifically to reduce the risk of a newly added route accidentally being left unprotected. Key revision takeaway: Global-by-default protection, with explicit, visible exemptions, is safer than remembering to protect every route individually.

This pattern generalizes well beyond JWT specifically; the same global-guard-with-metadata-based-exemption approach can be applied to any authentication or authorization guard, including session-based authentication or custom API key checks, wherever a 'protected by default, explicit exceptions' model makes sense. In interviews, tie this back to: @Public() (via SetMetadata) plus Reflector checking inside the guard is the standard exemption pattern for global guards. In real applications, consider this example: Health-check and monitoring endpoints (like GET /health, used by load balancers and uptime monitors) are almost universally marked @Public(), since they must remain accessible without authentication for infrastructure tooling to function. Key revision takeaway: The @Public() plus Reflector pattern mirrors the RBAC pattern from earlier in this module — the same technique solves both problems.