Utilizando Guards no Nest.js

Node.js 3 minutos
Felipe Heredia, depicted in the image

Os Guards no Nest.js possuem uma única responsabilidade, eles determinam se uma requisição será autorizada ou não, dependendo de algumas circunstâncias. Eles possuem acesso à instância do contexto da execução (ExecutionContext) e sabe exatamente o que deve ser executado caso a requisição seja autorizada.

Guards são executados depois de todos os middlewares, porém antes de qualquer interceptor e pipe.

Guards foram projetados para permitir o desenvolvedor lidar com a lógica de processamento no local correto do ciclo de requisição/resposta e fazer isso de maneira declaravita. Isso ajuda a manter o código limpo.

Autorização

Autorização é um grande e comum uso de caso para os Guards, pois determinadas rotas estar disponíveis apenas se o usuário autenticado possuir permissões suficientes, abaixo um exemplo de Guard.

import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) throw new UnauthorizedException();

    try {
      const payload = await this.jwtService.verifyAsync(
        token,
        {
          secret: jwtConstants.secret
        }
      );

      request['user'] = payload;
    } catch {
      throw new UnauthorizedException();
    }

    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}
import {
  Body,
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  Post,
  Request,
  UseGuards
} from '@nestjs/common';
import { AuthGuard } from './auth.guard';
import { AuthService } from './auth.service';

@Controller('auth')
export class AuthController {
  constructor(private authService: AuthService) {}

  @HttpCode(HttpStatus.OK)
  @Post('login')
  signIn(@Body() signInDto: Record<string, any>) {
    return this.authService.signIn(signInDto.username, signInDto.password);
  }

  @UseGuards(AuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

Com o código acima, antes do ciclo de vida chegar na função Controller em si, ocorrerá a validação do Guard. Nesse caso, se o token não for enviado na requisição ocorrerá uma resposta de “Não autorizado”. Após a validação de maneira positiva do Guards, será possível realizar o tratamento de solicitações e respostas HTTP.

Dessa maneira, na rota de visualizar o perfil, caso o token não exista, será retornado 401. Caso ele exista, as informações serão extraídas e inseridas no campo “user” na requisição, tornando possível o controle obter mais informações do user caso necessário.

A aplicação e uso de Guards é simples, como praticamente tudo dentro do Nest.js.

Contexto de Execução

A função canActivate() recebe apenas um parâmetro, a instância do ExecutionContext, quem vem de ArgumentsHost. ArgumentsHost é um objeto utilitário poderoso, através dele é possível obter informações do Request e Response.

Conclusão

Como vimos, utilizar Guards no Nest é simples e eficiente, é possível realizar validações nas informações recebidas da requisição e alterar informações contidas na requisição para que fiquem mais acessíveis em outros contextos.

Como tudo que vimos no Nest até agora, é uma funcionalidade poderosa, que quando bem utilizada trás simplicidade e agilidade a aplicação.