typescript
72 lines · 8 steps
How guards chain auth in NestJS
Three NestJS guards run in sequence to authenticate, authorize by role, and check workspace membership on each request.
Explained by
highlit
1import {
2 CanActivate,
3 ExecutionContext,
4 Injectable,
5 ForbiddenException,
6} from '@nestjs/common';
7import { Reflector } from '@nestjs/core';
8import { Request } from 'express';
9
10@Injectable()
11export class JwtAuthGuard implements CanActivate {
12 canActivate(context: ExecutionContext): boolean {
13 const req = context.switchToHttp().getRequest<Request>();
14 const token = req.headers.authorization?.replace('Bearer ', '');
15
16 if (!token) {
17 throw new ForbiddenException('Missing bearer token');
18 }
19
20 req['user'] = this.decode(token);
21 return true;
22 }
23
24 private decode(token: string) {
25 const [, payload] = token.split('.');
26 return JSON.parse(Buffer.from(payload, 'base64url').toString());
27 }
28}
29
30@Injectable()
31export class RolesGuard implements CanActivate {
32 constructor(private readonly reflector: Reflector) {}
33
34 canActivate(context: ExecutionContext): boolean {
35 const required = this.reflector.getAllAndOverride<string[]>('roles', [
36 context.getHandler(),
37 context.getClass(),
38 ]);
39
40 if (!required?.length) {
41 return true;
42 }
43
44 const { user } = context.switchToHttp().getRequest<Request>();
45 const granted = required.some((role) => user?.roles?.includes(role));
46
47 if (!granted) {
48 throw new ForbiddenException('Insufficient role');
49 }
50
51 return true;
52 }
53}
54
55@Injectable()
56export class WorkspaceGuard implements CanActivate {
57 constructor(private readonly memberships: MembershipService) {}
58
59 async canActivate(context: ExecutionContext): Promise<boolean> {
60 const req = context.switchToHttp().getRequest<Request>();
61 const isMember = await this.memberships.exists(
62 req['user'].sub,
63 req.params.workspaceId,
64 );
65
66 if (!isMember) {
67 throw new ForbiddenException('Not a workspace member');
68 }
69
70 return true;
71 }
72}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A guard is any class implementing canActivate that returns true to allow the request or throws to reject it.
- 2Chaining small single-purpose guards keeps authentication, role checks, and resource access independent and composable.
- 3Guards can attach data to the request object so later guards and handlers build on earlier work.
Related explainers
typescript
import { registerLocaleData } from '@angular/common'; import localeFr from '@angular/common/locales/fr'; import localeFrExtra from '@angular/common/locales/extra/fr'; import localeDe from '@angular/common/locales/de';
Locale-aware bootstrapping in Angular
i18n
localization
dependency-injection
Intermediate
8 steps
typescript
import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import * as Joi from 'joi';
Validating env config at boot in NestJS
configuration
schema-validation
environment-variables
Intermediate
8 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { InjectRepository } from '@nestjs/typeorm';
A cache-aside country lookup in NestJS
cache-aside
dependency-injection
batch-lookup
Intermediate
8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 steps
typescript
import { useEffect, useState } from "react"; interface Section { id: string;
Building a scroll-spy hook in React
custom-hooks
intersectionobserver
dom-observation
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-guards-chain-auth-in-nestjs-explained-typescript-5e24/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.