What is a Guard

Allow or Deny

A guard decides whether a request may reach the handler. It returns true (continue) or false (NestJS throws ForbiddenException, 403). It can also throw its own exception, for example UnauthorizedException (401).

Unlike middleware, a guard gets an ExecutionContext, so it knows which class and method will run and can read their metadata.

src/common/guards/api-key.guard.ts

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { Request } from 'express';

@Injectable()
export class ApiKeyGuard implements CanActivate {
  constructor(private readonly config: ConfigService) {}

  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest<Request>();
    const key = req.headers['x-api-key'];
    if (key !== this.config.get('INTERNAL_API_KEY')) throw new UnauthorizedException('Invalid API key');
    return true;
  }
}

Using It

@Controller('admin/reports')
@UseGuards(ApiKeyGuard)
export class ReportsController {
  @Get('daily-sales')
  dailySales() { return { date: '2026-01-15', orders: 1842, revenue: 2457300 }; }
}

Example

curl -i http://localhost:3000/admin/reports/daily-sales
curl -H "x-api-key: s3cr3t" http://localhost:3000/admin/reports/daily-sales

Example output

HTTP/1.1 401 Unauthorized
{"message":"Invalid API key","error":"Unauthorized","statusCode":401}

{"date":"2026-01-15","orders":1842,"revenue":2457300}

Tip: canActivate may also return a Promise<boolean> or an Observable, so a guard can check the database.