Autorização por permissões no NestJS usando decorators e guards
Por John Pires · 19 de março de 2026

Autorização por permissões no NestJS usando decorators e guards
Contexto: quando você vai usar isso
Autorização por permissões é a etapa que decide se um usuário autenticado pode executar uma ação ou acessar um recurso. Em aplicações reais você pode ter usuários com papéis (admin, editor, leitor) e permissões finas (post:create, post:edit, user:delete). O NestJS fornece ferramentas (decorators, guards, Reflector) que permitem implementar esse controle de forma organizada e testável.
Para quem está começando, pense em permissões como chaves físicas: autenticação é provar que você é uma pessoa conhecida (você entrou no prédio); autorização é verificar se você tem a chave para abrir a porta da sala. O NestJS permite colocar essa checagem em portas (guards) e rotular portas com plaquinhas de quais chaves são necessárias (decorators).
Este artigo foca em um caminho prático e incremental: primeiro um exemplo mínimo que já funciona, depois um exemplo mais realista integrando JWT (via AuthGuard) e guard compartilhado. Tudo em linguagem clara, passo a passo, com trechos de código práticos.
Você vai aprender a criar:
- Um decorator @Permissions(...) que marca rotas com permissões necessárias.
- Um PermissionsGuard que lê essas permissões com Reflector e compara com o usuário da requisição.
- Como combinar com autenticação (ex.: AuthGuard('jwt')).
Quando escolher permissões em vez de roles
Roles são boas para regras amplas; permissions servem para granularidade. Use permissions quando ações precisam de controles finos (criar/editar/excluir recursos diferentes). Roles podem mapear para conjuntos de permissions.
Resumo do fluxo na request
O fluxo típico é: middleware/strategy autentica e injeta request.user -> controller/route tem metadata (permissions) definida via decorator -> guard lê metadata e compara com request.user -> permite ou bloqueia.
Explicação visual com diagramas mentais
Antes de codificar, visualize o fluxo. Imagine um diagrama mental em três colunas: Request -> Auth -> Permissão. Cada coluna tem passos claros e responsabilidade única. Essa separação evita lógica espalhada.
Diagrama simplificado (conceito):
Request -> (AuthStrategy) -> request.user -> (PermissionsGuard + Reflector) -> Checar request.user.permissions -> Allow / Deny
Outra visão é por camadas do NestJS:
- Transport layer (HTTP)
- Middleware / Strategy (autenticação)
- Guards (autorização)
- Controllers / Handlers (execução)
O decorator funciona como etiqueta no controller, e o guard lê essa etiqueta no momento em que a request chega ao controller.
Metadados e Reflector
No NestJS usamos SetMetadata para criar metadados ligados a rotas ou controllers. O Reflector é a ferramenta para ler esses metadados dentro de um guard. Pense nele como uma lupa que lê as plaquinhas coladas nas portas.
Com isso em mente, vamos ao passo a passo prático.
Primeiro exemplo simples
Este exemplo foca no mínimo necessário para ver o mecanismo funcionando. Não integrará JWT — assumimos que request.user já existe (por middleware ou por teste). A ideia é começar pequeno e entender o fluxo.
Passo 1: criar decorator @Permissions(...) que aplica metadata na rota.
O código abaixo cria um decorator simples usando SetMetadata. Contexto: esse decorator adiciona a chave 'permissions' no metadata da rota ou controller.
import { SetMetadata } from '@nestjs/common';
export const PERMISSIONS_KEY = 'permissions';
export const Permissions = (...permissions: string[]) => SetMetadata(PERMISSIONS_KEY, permissions);
O que faz: define um decorator que marca a rota com uma lista de permissões. Depois de aplicado, o Reflector pode ler essa lista.
Passo 2: criar um PermissionsGuard que consume o metadata e compara com request.user.permissions.
import { CanActivate, ExecutionContext, Injectable, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { PERMISSIONS_KEY } from './permissions.decorator';
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<string[]>(PERMISSIONS_KEY, [
context.getHandler(),
context.getClass(),
]);
if (!required || required.length === 0) return true; // rota sem proteção por permissões
const request = context.switchToHttp().getRequest();
const user = request.user;
if (!user || !user.permissions) throw new ForbiddenException('Usuário sem permissões');
const has = required.every(p => user.permissions.includes(p));
if (!has) throw new ForbiddenException('Permissão negada');
return true;
}
}
O que acontece: o guard lê metadata (permissoes), pega request.user e confirma se o usuário possui todas as permissões requeridas. Se algo falhar, lança ForbiddenException.
Passo 3: aplicar em um controller. Exemplo mínimo:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { Permissions } from './permissions.decorator';
import { PermissionsGuard } from './permissions.guard';
@Controller('posts')
@UseGuards(PermissionsGuard)
export class PostsController {
@Get()
@Permissions('post:read')
findAll() {
return [{ id: 1, title: 'Meu post' }];
}
}
O que acontece: a rota GET /posts exige a permissão 'post:read'. Se request.user.permissions inclui esse valor, a requisição prossegue.
Teste rápido sem JWT
Para testar localmente sem autenticação, você pode usar um middleware que injeta request.user com um conjunto de permissões. Isso facilita entender antes de integrar JWT.
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class FakeAuthMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
req.user = { id: 1, username: 'dev', permissions: ['post:read', 'post:create'] };
next();
}
}
Coloque esse middleware no AppModule apenas para desenvolvimento ou testes rápidos.
Segundo exemplo mais realista
Aqui vamos integrar com autenticação JWT (passport-jwt). A ideia é que a strategy populates request.user a partir do token e o PermissionsGuard continua responsável apenas pela autorização.
Passo 1: criar JwtStrategy (resumido). Contexto: essa strategy verifica token, busca usuário e retorna objeto com permissions.
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'SOME_SECRET',
});
}
async validate(payload: any) {
// payload pode vir do token. Normalmente buscaríamos usuário no BD.
// Exemplo simplificado: retornamos um objeto user com permissions
return { id: payload.sub, username: payload.username, permissions: payload.permissions || [] };
}
}
O que acontece: ao usar @UseGuards(AuthGuard('jwt')) a passport strategy valida o token e injeta request.user com o retorno de validate().
Passo 2: combinar AuthGuard e PermissionsGuard na rota.
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Permissions } from './permissions.decorator';
import { PermissionsGuard } from './permissions.guard';
@Controller('admin')
export class AdminController {
@Get('reports')
@UseGuards(AuthGuard('jwt'), PermissionsGuard)
@Permissions('reports:view')
getReports() {
return { stats: 'dados sensíveis' };
}
}
O que acontece: primeiro o AuthGuard autentica; depois o PermissionsGuard lê request.user e o metadata da rota. Assim respeitamos a separação de responsabilidades.
Mapeamento role -> permissions
Em aplicações reais, muitas vezes as roles mapeiam para sets de permissions. Você pode traduzir roles em permissions no ponto de validação (ex.: JwtStrategy ou no serviço de usuário) para deixar o guard simples.
Exemplo de mapeamento rápido:
const rolePermissions = {
admin: ['*'],
editor: ['post:create', 'post:edit', 'post:read'],
reader: ['post:read'],
};
// No validate payload:
const permissions = new Set();
(payload.roles || []).forEach(r => (rolePermissions[r] || []).forEach(p => permissions.add(p)));
O que acontece: convertendo roles em permissions no momento da autenticação você mantém o PermissionsGuard puro e fácil de testar.
Erros comuns de iniciante e como evitar
Vou listar armadilhas frequentes e como corrigi-las. São problemas simples que causam perda de tempo.
- Assumir que request.user sempre existe: se o AuthGuard não foi aplicado, request.user pode ser indefinido. Solução: trate cada caso no guard ou combine AuthGuard + PermissionsGuard na rota.
- Colocar lógica de autorização espalhada: evitar checks manuais dentro de controllers. Centralize em guards para reutilização e testes.
- Esquecer de sincronizar decorator e guard: usar nomes diferentes para metadata impede leitura. Use constantes para a chave (PERMISSIONS_KEY) para evitar typos.
- Usar includes vs escopos complexos: se você precisa de curinga ('*') ou hierarquia, implemente a lógica no guard (ex.: se permissions inclui '*' aceita tudo) em vez de tratar em cada rota.
- Testar sem middleware de autenticação: quando testar manualmente, use um middleware fake para injetar request.user; ao migrar para produção, remova o fake.
Dicas práticas
Teste guards isoladamente com unit tests: crie ExecutionContext simulado e injete Reflector mock.
Ao escrever testes unitários para PermissionsGuard, crie contextos com handler/class que retornam metadata e simule request.user com diferentes sets de permissions. Isso é mais rápido do que testar via e2e inicialmente.
Outra dica: registre o PermissionsGuard globalmente com cuidado. Em muitos casos, é melhor aplicar por controller/rota para permitir exceções e rotas públicas.
Conclusão prática
Permissões no NestJS com decorators e guards seguem um padrão simples e poderoso: etiquetar rotas com metadata e criar guards que leem essa metadata para decidir acesso. A separação entre autenticação (quem é você) e autorização (o que você pode fazer) torna a aplicação mais modular e testável.
Comece implementando um decorator + guard simples, teste com middleware fake, depois integre com JWT/Passport populando request.user com permissions. Use Reflector para manter o código limpo e constants para a chave de metadata.
Resumo prático:
- Decorator: define quais permissões a rota exige.
- Guard: consulta request.user e decide permitir/negação.
- Auth strategy: preenche request.user (preferencialmente com permissions resolvidas).
Seguindo esse padrão você terá um controle de acesso claro, escalável e fácil de testar. Adapte a granularidade (roles vs permissions) conforme a necessidade do seu domínio.
Se precisar, posso gerar exemplos de testes unitários para o PermissionsGuard ou uma versão completa de JwtStrategy + módulo Auth para integração passo a passo.
Assuntos: NestJS, guards, decorators, authorization, jwt
Leia também
Implementando Circuit Breaker e Bulkhead em microservices NestJS com Cockatiel
Guia prático para integrar circuit breaker e bulkhead com Cockatiel em NestJS: módulo centralizado, wrapper Http resiliente, tratamento de erros e otimizações…
Interceptor global de logs e tempo no NestJS: implementar e testar
Implemente um interceptor global no NestJS para logs e medição de tempo, registre-o no bootstrap e teste com Jest e supertest. Artigo prático passo a passo…
Limitador de requisições no NestJS com Throttler + Redis: passo a passo para iniciantes
Guia prático para iniciantes: configure rate limiting no NestJS usando @nestjs/throttler e Redis. Passo a passo com exemplos funcionais, Docker, dicas de…
Rate limiting por usuário no NestJS com Guard + Redis (sliding window)
Aprenda a implementar rate limiting por usuário no NestJS usando Guard + Redis com sliding window. Guia passo a passo, exemplos práticos (Lua + ioredis)…
Testando Guards e Interceptors no NestJS com Jest e SuperTest — do jeito errado ao certo
Guards controlam acesso e Interceptors transformam respostas. Aprenda a testar ambos no NestJS: mostro o jeito errado, corrijo e explico testes unitários e…
API REST com NestJS, Prisma e PostgreSQL: JWT, testes e otimização de performance
Guia avançado para APIs com NestJS, Prisma e PostgreSQL. Cobre autenticação JWT, testes automáticos (unit/e2e) e otimizações de performance para produção…