Node.js Framework

NestJS

NestJS é um framework Node.js opinativo e inspirado no Angular. Módulos, controllers e providers oferecem a grandes equipes uma arquitetura compartilhada e injeção de dependência nativamente.

advanced16 min readUpdated 16 de set. de 2026
cats.controller.ts
ts
// cats.controller.ts
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
import { CatsService } from "./cats.service";
import { CreateCatDto } from "./dto/create-cat.dto";

@Controller("cats")
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll() {
    return this.catsService.findAll();
  }

  @Get(":id")
  findOne(@Param("id") id: string) {
    return this.catsService.findOne(id);
  }

  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return this.catsService.create(createCatDto);
  }
}
Lançado
2017
Roda em
Express ou Fastify
Linguagem
TypeScript
Estilo
Opinativo, modular
Ideia central
DI e decorators
Versão
11.x

Por que importa

Por que equipes escolhem NestJS

Estrutura para grandes equipes

Módulos, camadas e decorators dão a todos o mesmo mapa. Novos desenvolvedores conseguem encontrar onde uma funcionalidade reside sem precisar ler todo o código-fonte.

Injeção de dependência nativa

Um container de inversão de controle real resolve construtores, gerencia ciclos de vida e torna cada dependência substituível em testes.

Preocupações transversais como código

Guards, interceptors, pipes e filters lidam com autenticação, logging, validação e erros de forma declarativa, na camada correta.

O panorama completo

Módulos, providers, controllers

Um módulo define a fronteira de uma funcionalidade, um provider detém a lógica e um controller transforma HTTP em chamadas de método.

Modules

Fronteira

Cada funcionalidade é um módulo que declara o que importa, provê e exporta, tornando as dependências explícitas.

Providers

Injetar

Services, repositories e factories são providers que o container instancia e injeta via construtor.

Controllers

Rota

Classes decoradas mapeiam métodos e caminhos HTTP para métodos manipuladores, com parâmetros extraídos por decorators.

HTML5 de uma olhada

A caixa de ferramentas do NestJS

Modules

@Module agrupa controllers e providers em uma fronteira de funcionalidade.

Controllers

@Controller e @Get transformam uma classe em um conjunto de rotas.

Providers

Classes @Injectable são resolvidas e compartilhadas pelo container de DI.

DTOs

Formatos de requisição tipados, validados por pipes antes da execução do manipulador.

Guards

Decidem se uma requisição pode prosseguir, utilizando roles ou políticas.

Interceptors

Envolvem a execução do manipulador para logging, caching e mapeamento de resposta.

O guia completo

NestJS: Tudo que voce precisa saber

O que é NestJS?

NestJS é um framework progressivo de Node.js que traz estrutura para o JavaScript no lado do servidor. Enquanto o Express fornece primitivos e deixa a escolha para você, o NestJS oferece uma arquitetura: módulos, controllers, providers, decorators e um container de injeção de dependência. Ele se inspira fortemente no Angular e em frameworks Java corporativos, rodando sobre o Express ou Fastify.

Essa postura opinativa é a base de sua proposta de valor. Em uma equipe de cinco pessoas construindo uma API robusta, os problemas mais difíceis raramente são relacionados ao HTTP. Eles envolvem nomenclatura, delimitação de fronteiras, testes e a manutenção de padrões consistentes à medida que a base de código cresce. O NestJS resolve isso com convenções que você pode aceitar ou substituir, e já entrega toda a infraestrutura — validação, guards, interceptors, configuração, utilitários de teste — para que você não precise montar tudo do zero.

Módulos definem as fronteiras

Tudo no NestJS é organizado em módulos. Um módulo é uma classe decorada com @Module que declara seus controllers, seus providers e o que ele exporta para o restante da aplicação.

@Module({
  imports: [DatabaseModule],
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

imports traz outros módulos cujos providers exportados você necessita. providers são as classes que este módulo possui. exports é a API pública: apenas o que um módulo exporta pode ser injetado em outro lugar. Isso torna as dependências explícitas — se OrdersModule precisa de CatsService, ele deve importar CatsModule, e o container (que funciona como um compilador) avisará quando não for possível resolver algo.

O AppModule raiz importa cada módulo de funcionalidade (feature module). Módulos de funcionalidade podem ser agrupados em módulos compartilhados (shared) ou core, e @Global() marca um módulo que deve estar disponível em todo lugar sem a necessidade de importações repetidas. Use módulos globais com moderação, apenas para infraestruturas genuinamente transversais, como configuração ou logging.

Controllers: roteamento como decorators

Um controller mapeia requisições HTTP para métodos. O class decorator define o caminho base, e os method decorators definem o verbo e o sub-caminho.

@Controller("cats")
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll() {
    return this.catsService.findAll();
  }

  @Get(":id")
  findOne(@Param("id") id: string) {
    return this.catsService.findOne(id);
  }

  @Post()
  create(@Body() dto: CreateCatDto) {
    return this.catsService.create(dto);
  }
}

Os parameter decorators realizam a extração: @Param, @Query, @Body, @Headers, @Req e @Res. O handler retorna um valor simples ou uma promise, e o NestJS o serializa para JSON com o status correto. Lance um HttpException, como o NotFoundException, e o framework o transformará em uma resposta de erro adequada.

Controllers devem ser “thin” (magros). Eles traduzem HTTP em uma chamada de método e nada mais. Toda a lógica pertence aos providers, o que mantém o controller testável e as preocupações de HTTP isoladas.

Providers e injeção de dependência

Um provider é qualquer classe que possa ser injetada. Marque-a como @Injectable() e declare-a em um módulo, e o container cuida do resto.

@Injectable()
export class CatsService {
  constructor(private readonly db: DatabaseService) {}

  findAll() {
    return this.db.query("SELECT * FROM cats");
  }
}

A injeção é feita via construtor e por tipo. O container lê os metadados emitidos, resolve cada parâmetro e passa a instância. Você também pode injetar por token quando o valor não for uma classe, utilizando @Inject("CONFIG") com um provider correspondente.

Providers podem ser customizados com factories, valores e aliases. Um factory provider é a maneira usual de injetar algo que precise de configuração assíncrona, como uma conexão de banco de dados:

@Module({
  providers: [
    {
      provide: "DATABASE",
      useFactory: async (config: ConfigService) => {
        return createPool(config.getOrThrow("DATABASE_URL"));
      },
      inject: [ConfigService],
    },
  ],
  exports: ["DATABASE"],
})
export class DatabaseModule {}

O escopo também é importante. O escopo singleton padrão compartilha uma única instância em todo o app, que é o ideal para serviços stateless. O escopo REQUEST cria uma nova instância por requisição e é útil para contextos específicos da requisição, mas impacta a performance porque propaga por todo o grafo de dependências.

DTOs, pipes e validação

A validação de entrada é uma prioridade fundamental. Um DTO (data transfer object) é uma classe que descreve o formato de uma requisição, decorada com regras de class-validator.

export class CreateCatDto {
  @IsString()
  @MinLength(1)
  name: string;

  @IsInt()
  @Min(0)
  age: number;
}

Um pipe transforma ou valida o valor antes que ele chegue ao handler. Ative o ValidationPipe nativo globalmente e todo DTO decorado será verificado automaticamente:

app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }),
);

O whitelist remove propriedades desconhecidas, o forbidNonWhitelisted as rejeita sumariamente, e o transform converte os payloads em instâncias reais de DTO para que os tipos sejam reais em tempo de execução. Entradas inválidas geram um 400 estruturado sem a necessidade de escrever uma única linha de código de validação no handler. Pipes também podem ser aplicados por parâmetro ou por controller utilizando @UsePipes.

Guards, interceptors e filters

Esses três blocos de construção cobrem a maioria das preocupações transversais (cross-cutting concerns) e são executados em uma ordem fixa.

Guards são executados antes do handler e decidem se a requisição deve prosseguir.

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>("roles", context.getHandler());
    if (!roles) return true;
    const { user } = context.switchToHttp().getRequest();
    return roles.includes(user?.role);
  }
}

Interceptors envolvem a chamada do handler e podem transformar o resultado, medir o tempo de execução ou adicionar cache.

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
    const start = Date.now();
    return next.handle().pipe(
      tap(() => console.log(`took ${Date.now() - start}ms`)),
    );
  }
}

Exception filters capturam erros lançados e formatam a resposta. Sem um filtro, o NestJS utiliza um padrão razoável; um filtro customizado mantém o formato dos erros consistente em toda a API.

@Catch(HttpException)
export class HttpErrorFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();
    const status = exception.getStatus();
    response.status(status).json({
      statusCode: status,
      message: exception.message,
      timestamp: new Date().toISOString(),
    });
  }
}

Registre-os globalmente, por controller ou por rota. Guards, interceptors e filters globais são a maneira como uma aplicação NestJS madura centraliza autenticação, observabilidade e tratamento de erros.

Configuração e ambiente

O pacote @nestjs/config encapsula as variáveis de ambiente e as torna injetáveis. Importe-o uma única vez como um módulo global.

@Module({
  imports: [ConfigModule.forRoot({ isGlobal: true })],
})
export class AppModule {}

Em seguida, injete ConfigService onde for necessário:

@Injectable()
export class DatabaseService {
  constructor(private readonly config: ConfigService) {}

  connect() {
    const url = this.config.getOrThrow<string>("DATABASE_URL");
    return createPool(url);
  }
}

Dê preferência ao getOrThrow para valores obrigatórios, para que a ausência de uma variável cause uma falha na inicialização, em vez de ocorrer na primeira requisição. Combine isso com um esquema de validação para que a configuração seja verificada apenas uma vez, e mantenha os segredos totalmente fora do repositório.

Testando com TestingModule

As utilidades de teste do NestJS constroem um container real de forma isolada. Você declara apenas os providers que serão testados e, em seguida, substitui suas dependências por mocks.

describe("CatsController", () => {
  let controller: CatsController;
  let service: CatsService;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      controllers: [CatsController],
      providers: [CatsService],
    }).compile();

    controller = module.get(CatsController);
    service = module.get(CatsService);
  });

  it("returns all cats", () => {
    jest.spyOn(service, "findAll").mockReturnValue([]);
    expect(controller.findAll()).toEqual([]);
  });
});

Para testes end-to-end, construa a aplicação completa e utilize supertest contra app.getHttpServer(). Como o container de DI é o mesmo utilizado em produção, esses testes exercitam guards, pipes e filters exatamente como eles são implantados.

Melhores práticas

  • Mantenha os controllers enxutos e coloque toda a lógica em services injetáveis.
  • Defina limites de módulos em torno das funcionalidades e exporte apenas o que outros módulos precisarem.
  • Valide cada requisição com um DTO e um ValidationPipe global.
  • Use guards para autenticação e autorização, e interceptors para a formatação de respostas.
  • Registre filtros de exceção globais para que as respostas de erro tenham um formato padronizado.
  • Injete a configuração em vez de ler process.env diretamente.
  • Prefira o escopo singleton; utilize o escopo de requisição apenas quando for realmente necessário.
  • Teste com TestingModule e sobrescreva as dependências em vez de acessar serviços reais.

Erros comuns

  • Construir providers com new em vez de injetá-los, o que quebra o container.
  • Esquecer de adicionar um provider a um módulo e depois perder tempo tentando resolver um erro de “Nest can’t resolve dependencies”.
  • Importar um módulo, mas esquecer de export o provider que ele deveria compartilhar.
  • Colocar lógica de negócio em controllers, tornando-os impossíveis de testar via unit test.
  • Usar request scope em todo lugar e degradar a performance silenciosamente.
  • Confiar em @Body() sem um DTO e um validation pipe.
  • Espalhar a formatação de erros em vez de centralizá-la em um filter.
  • Tratar módulos como pastas em vez de fronteiras de dependência.

Próximos passos

O NestJS é a maneira mais estruturada de construir um backend em Node.js e funciona diretamente sobre o Express ou Fastify. Leia esses guias para entender a camada HTTP que você está abstraindo. Um domínio sólido de TypeScript é essencial, pois os decorators e a injeção de dependência dependem de tipos, e os fundamentos de Node.js explicam o runtime onde tudo é executado. Depois, construa um módulo de funcionalidade de ponta a ponta: controller, service, DTO e teste.

Na pratica

Os quatro arquivos de uma funcionalidade

Um controller roteia, um service detém a lógica, um módulo os conecta e um DTO define e valida a entrada.

cats.controller.ts
import {
  Body, Controller, Delete, Get, Param, Post,
} from "@nestjs/common";
import { CatsService } from "./cats.service";
import { CreateCatDto } from "./dto/create-cat.dto";

@Controller("cats")
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll() {
    return this.catsService.findAll();
  }

  @Get(":id")
  findOne(@Param("id") id: string) {
    return this.catsService.findOne(id);
  }

  @Post()
  create(@Body() dto: CreateCatDto) {
    return this.catsService.create(dto);
  }

  @Delete(":id")
  remove(@Param("id") id: string) {
    return this.catsService.remove(id);
  }
}

Obtendo uma dependência

A injeção via construtor permite que o container crie e compartilhe o serviço, e permite que testes substituam por um fake. Instanciá-lo manualmente anula todo o propósito do framework.

Preferir
@Controller("cats")
export class CatsController {
  constructor(private readonly catsService: CatsService) {}
}
Evitar
@Controller("cats")
export class CatsController {
  // Bypasses DI, breaks the lifecycle and
  // cannot be replaced in a test.
  private catsService = new CatsService();
}

Validando entrada

Um DTO com decorators do class-validator mais um ValidationPipe global rejeita requisições inválidas antes do manipulador e produz respostas 400 consistentes.

Preferir
export class CreateCatDto {
  @IsString()
  @MinLength(1)
  name: string;

  @IsInt()
  age: number;
}

@Post()
create(@Body() dto: CreateCatDto) {
  return this.catsService.create(dto);
}
Evitar
@Post()
create(@Body() body: any) {
  if (typeof body.name !== "string") {
    throw new BadRequestException();
  }
  // every handler repeats the same checks
}

Trade-offs

O NestJS é a escolha certa por padrão?

O NestJS troca liberdade por estrutura. As convenções valem a pena conforme a base de código cresce, mas podem parecer pesadas quando não crescem.

Strengths

  • Estrutura desde o primeiro dia

    Módulos, controllers e providers dão a cada funcionalidade um lugar, para que uma equipe em crescimento compartilhe uma arquitetura em vez de inventar uma.

  • Injeção de dependência nativa

    Um container real gerencia ciclos de vida e torna cada dependência substituível, mantendo os serviços testáveis sem estado global.

  • Baterias inclusas

    Validação, guards, interceptors, configuração e utilitários de teste já vêm com o framework, então você monta muito menos coisas manualmente.

Trade-offs

  • Muita cerimônia

    Decorators, módulos e DTOs adicionam arquivos e conceitos antes mesmo do primeiro endpoint funcionar, fazendo com que serviços pequenos pareçam super-engenheirados.

  • Você adota as opiniões dele

    O NestJS decide como o código é organizado. Lutar contra essas convenções custa mais caro do que escolher um framework mais leve.

  • TypeScript é pressuposto

    Os decorators e a injeção de dependência dependem de tipos e metadados, então uma equipe de JavaScript puro enfrentará uma curva de aprendizado real.

Perguntas frequentes

Perguntas frequentes

Keep learning

Related topics from the roadmap.

$ comecar a aprender

Pronto para aprender NestJS?

Nosso tutorial interativo te guia por NestJS passo a passo — com quizzes e codigo real que voce pode executar no navegador.