nestjssecuritytypescriptapi-design

Layered Security in NestJS: Composing Guards for Real-World APIs

Building secure APIs requires more than JWT validation. This article walks through composing multiple NestJS guards for authentication, email verification, role-based access, and resource ownership—patterns from a production academic project API.

ZakariaPublished April 15, 202510 min read
Layered Security in NestJS: Composing Guards for Real-World APIs — 1

Why Layered Guards?

A single auth middleware can't express the nuanced access rules real APIs need: verified email, specific roles, and resource ownership all vary by endpoint.

NestJS guards are the right abstraction—they compose cleanly and run in declaration order.

The Guard Stack

A typical protected endpoint uses four guards in sequence: AuthGuard validates JWT, VerifiedGuard checks email verification, RolesGuard checks role membership, and OwnershipGuard verifies resource ownership.

@UseGuards(AuthGuard, VerifiedGuard, RolesGuard, OwnershipGuard)
@Roles(Role.OWNER)
@Patch('projects/:id')
updateProject(@Param('id') id: string, @Body() dto: UpdateProjectDto) {
  return this.projectService.update(id, dto);
}

Ownership Guard Pattern

OwnershipGuard extracts the resource ID from route params, loads the entity from the database, and compares its ownerId field to the authenticated user's ID. This prevents users from modifying resources they don't own, even if they have the correct role.

Related Articles

Want to discuss this topic or collaborate on a project?