typescript 53 lines · 7 steps

How a GraphQL auth guard works in NestJS

A NestJS guard extracts a JWT from GraphQL requests, verifies it, and enforces role-based access before a resolver runs.

Explained by highlit
1import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
2import { GqlExecutionContext } from '@nestjs/graphql';
3import { Reflector } from '@nestjs/core';
4import { JwtService } from '@nestjs/jwt';
5 
6type GqlContext = {
7 req: {
8 headers: Record<string, string | undefined>;
9 user?: { id: string; roles: string[] };
10 };
11};
12 
13@Injectable()
14export class GqlAuthGuard implements CanActivate {
15 constructor(
16 private readonly reflector: Reflector,
17 private readonly jwtService: JwtService,
18 ) {}
19 
20 async canActivate(context: ExecutionContext): Promise<boolean> {
21 const gqlContext = GqlExecutionContext.create(context);
22 const ctx = gqlContext.getContext<GqlContext>();
23 const { req } = ctx;
24 
25 const token = this.extractToken(req.headers.authorization);
26 if (!token) {
27 throw new UnauthorizedException('Missing bearer token');
28 }
29 
30 try {
31 const payload = await this.jwtService.verifyAsync<{ sub: string; roles: string[] }>(token);
32 req.user = { id: payload.sub, roles: payload.roles ?? [] };
33 } catch {
34 throw new UnauthorizedException('Invalid or expired token');
35 }
36 
37 const requiredRoles = this.reflector.getAllAndOverride<string[]>('roles', [
38 gqlContext.getHandler(),
39 gqlContext.getClass(),
40 ]);
41 
42 if (requiredRoles?.length && !requiredRoles.some((role) => req.user!.roles.includes(role))) {
43 throw new UnauthorizedException('Insufficient permissions');
44 }
45 
46 return true;
47 }
48 
49 private extractToken(header?: string): string | null {
50 const [scheme, value] = header?.split(' ') ?? [];
51 return scheme === 'Bearer' && value ? value : null;
52 }
53}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A guard is the single choke point where you can reject a request before any resolver logic runs.
  2. 2GraphQL contexts must be unwrapped with GqlExecutionContext before you can reach the underlying HTTP request.
  3. 3Reading metadata off both the handler and class lets one guard enforce declarative role requirements.

Related explainers

typescript
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';

How a JWT strategy authenticates in NestJS

authentication jwt passport
Intermediate 7 steps
typescript
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, take, switchMap } from 'rxjs/operators';

Refreshing auth tokens in an Angular interceptor

http-interceptor token-refresh rxjs
Advanced 8 steps
go
package auth
 
import (
	"net/http"

Setting and reading secure session cookies in Go

cookies session-management security
Intermediate 6 steps
typescript
type ResizeCallback = (size: { width: number; height: number }) => void;
 
export function observeResize(callback: ResizeCallback, delay = 150) {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

Debouncing window resize in TypeScript

debounce closures event-listeners
Intermediate 6 steps
typescript
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { DataSource } from 'typeorm';

Graceful shutdown hooks in NestJS

graceful-shutdown lifecycle-hooks dependency-injection
Intermediate 7 steps
typescript
import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron, CronExpression } from '@nestjs/schedule';
 

A self-refreshing feature flags provider in NestJS

caching scheduled-tasks dependency-injection
Intermediate 8 steps

Share this explainer

Here's the card — post it anywhere.

How a GraphQL auth guard works in NestJS — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code