Harness-engineering nestjs-guards-pattern

NestJS Guards Pattern

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/nestjs-guards-pattern" ~/.claude/skills/intense-visions-harness-engineering-nestjs-guards-pattern && rm -rf "$T"
manifest: agents/skills/claude-code/nestjs-guards-pattern/SKILL.md
source content

NestJS Guards Pattern

Protect routes with @UseGuards, CanActivate, JWT guards, and role-based access control

When to Use

  • You need to verify authentication before a handler executes (JWT, API key, session)
  • You need role-based or permission-based access control on specific routes
  • You want to centralize authentication logic instead of repeating it in every controller method
  • You need to read the current user from the token and attach it to the request

Instructions

  1. Implement
    CanActivate
    and return
    true
    (allow) or
    false
    /throw (deny):
@Injectable()
export class JwtAuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest<Request>();
    const token = this.extractToken(request);
    if (!token) throw new UnauthorizedException();
    try {
      request['user'] = this.jwtService.verify(token, { secret: process.env.JWT_SECRET });
      return true;
    } catch {
      throw new UnauthorizedException();
    }
  }

  private extractToken(req: Request): string | undefined {
    const [type, token] = req.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
  1. Apply guards with
    @UseGuards(GuardClass)
    at the controller or handler level. Handler-level overrides controller-level.
  2. Apply globally in
    main.ts
    for app-wide auth:
    app.useGlobalGuards(new JwtAuthGuard(jwtService))
    . For DI in global guards, use
    APP_GUARD
    provider instead:
providers: [{ provide: APP_GUARD, useClass: JwtAuthGuard }];
  1. Use
    Reflector
    and custom decorators to build a roles guard:
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const required = this.reflector.getAllAndOverride<string[]>('roles', [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!required) return true; // no roles required
    const { user } = context.switchToHttp().getRequest();
    return required.some((role) => user?.roles?.includes(role));
  }
}
  1. Use
    @Public()
    with a
    SetMetadata('isPublic', true)
    decorator to bypass global auth guards on specific routes.
  2. ExecutionContext
    is transport-agnostic — use
    context.switchToWs()
    for WebSockets and
    context.switchToRpc()
    for microservices.

Details

Guards run after middleware but before interceptors and pipes in the NestJS request lifecycle. They are the correct place for authorization decisions because they have access to

ExecutionContext
, which provides the handler and class metadata.

Guard vs Middleware: Middleware runs before routing and has no knowledge of which handler will process the request. Guards run after routing with full handler context, making them the right tool for authorization logic that depends on route metadata (roles, permissions).

Throwing vs returning false: Throwing

UnauthorizedException
(or any
HttpException
) is preferred over returning
false
. Returning
false
causes NestJS to throw a generic
ForbiddenException
(403), which may not communicate the correct HTTP status.

Multiple guards:

@UseGuards(AuthGuard, RolesGuard)
applies guards in order. If
AuthGuard
throws,
RolesGuard
never runs. Compose authentication and authorization as separate guards for clean separation of concerns.

Passport integration:

@nestjs/passport
provides
AuthGuard('jwt')
,
AuthGuard('local')
, etc. that wrap Passport strategies. Use it when you need OAuth2, SAML, or complex multi-strategy auth flows.

Source

https://docs.nestjs.com/guards

Process

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. Verify your implementation against the details and edge cases listed above.

Harness Integration

  • Type: knowledge — this skill is a reference document, not a procedural workflow.
  • No tools or state — consumed as context by other skills and agents.

Success Criteria

  • The patterns described in this document are applied correctly in the implementation.
  • Edge cases and anti-patterns listed in this document are avoided.