Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIniciantecódigo testado

Primeiro projeto NestJS: instalar, executar e entender

Crie seu primeiro projeto com Nest CLI, entenda os arquivos gerados e confirme a rota Hello World no NestJS 11 com Node 24 e Express padrão.

Rodolfo Mori5 min de leitura

Seu primeiro projeto NestJS pode estar respondendo uma rota HTTP em poucos comandos, mas o objetivo desta lição é maior: você vai criar o projeto com uma versão fixada, entender cada arquivo inicial e comprovar qual servidor está por baixo do framework.

O NestJS 11 exige Node 20 ou superior segundo a documentação oficial. O teste desta aula usou Node 24.16.0, Nest CLI 11.0.24 e os pacotes Nest 11.2.1. Se você ainda não tem o runtime, siga antes a lição de instalação do Node com NVM.

A montagem antes de abrir as portas

Pense numa companhia de teatro antes da apresentação. O diretor reúne elenco, cenário e roteiro; depois a casa abre para o público. No nosso mapa, NestFactory é quem coordena a montagem, AppModule declara as peças da aplicação e listen abre uma porta para receber requisições.

Tecnicamente, o processo de bootstrap inicializa o contêiner de dependências, instancia os componentes registrados e mapeia as rotas. A analogia não significa que tudo roda em sequência como uma peça: depois da inicialização, o Node atende muitas operações pelo event loop. Ela serve para separar “montar a aplicação” de “receber tráfego”.

Confira o Node antes de criar o projeto

Abra o terminal e leia as versões, sem instalar nada ainda:

bash
node --version
npm --version
v24.16.0 11.13.0

Se node não for encontrado, resolva o runtime primeiro. Se a versão for menor que 20, troque para uma linha suportada. A versão do Node é a fundação; atualizar o CLI não corrige um runtime incompatível.

Crie o projeto com uma versão reproduzível do CLI

Em vez de instalar o CLI globalmente, a gente usa npx com a versão explícita. O nome agenda-mentorias vira a pasta e também o nome do pacote.

bash
npx -y @nestjs/cli@11.0.24 new agenda-mentorias \
  --package-manager npm \
  --skip-git \
  --strict

--strict ativa regras mais cuidadosas do TypeScript. --skip-git apenas evita criar outro repositório dentro do exercício. O CLI gerou os arquivos e instalou as dependências; as linhas finais do teste foram:

CREATE agenda-mentorias/src/main.ts CREATE agenda-mentorias/src/app.module.ts CREATE agenda-mentorias/src/app.controller.ts CREATE agenda-mentorias/src/app.service.ts Successfully created project agenda-mentorias

Entre na pasta e confira as versões realmente resolvidas pelo npm:

bash
cd agenda-mentorias
npm ls --depth=0 @nestjs/core @nestjs/common @nestjs/platform-express
agenda-mentorias@0.0.1 ├── @nestjs/common@11.2.1 ├── @nestjs/core@11.2.1 └── @nestjs/platform-express@11.2.1

Essa verificação evita uma confusão comum: a versão do CLI cria e administra o projeto, enquanto @nestjs/core, @nestjs/common e o adapter são dependências da aplicação. Elas podem ter números diferentes.

Leia a pasta src como um caminho, não como burocracia

O início do projeto tem esta estrutura:

text
src/
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts

main.ts inicia a aplicação. app.module.ts é o módulo raiz. O controller recebe a requisição. O service produz o texto. O arquivo .spec.ts testa esse comportamento. Em vez de decorar nomes, siga a pergunta: quem inicia, quem organiza, quem recebe e quem executa?

main.ts cria a aplicação e abre a porta

O arquivo gerado contém o bootstrap:

ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}

bootstrap();

NestFactory.create recebe a classe do módulo raiz. await espera a montagem terminar. process.env.PORT ?? 3000 usa a porta informada pelo ambiente ou 3000 quando ela não existe. Isso importa no deploy, porque a plataforma costuma escolher a porta.

AppModule declara o ponto de partida

O módulo raiz usa o decorator @Module. Decorator é uma marcação que adiciona metadados lidos pelo Nest.

ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

controllers registra quem recebe HTTP. providers registra dependências que o Nest pode criar e injetar. imports ficará útil quando a gente dividir a API em módulos de funcionalidade. A próxima lição de módulos, controllers e providers faz essa divisão.

Service produz o valor; controller expõe a rota

O service é um provider porque recebeu @Injectable():

ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

O controller pede essa dependência no constructor e liga o método GET à raiz:

ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

Quando chega GET /, o roteador chama getHello; o controller delega ao service; o valor retornado vira a resposta. A injeção no constructor não cria o service manualmente: o contêiner usa o registro de AppModule.

Inicie, observe os logs e faça uma requisição

O script de desenvolvimento recompila quando você salva:

bash
npm run start:dev
[NestFactory] Starting Nest application... [InstanceLoader] AppModule dependencies initialized [RoutesResolver] AppController {/} [RouterExplorer] Mapped {/, GET} route [NestApplication] Nest application successfully started

Abra outro terminal e consulte a aplicação:

bash
curl -i http://localhost:3000/
HTTP/1.1 200 OK X-Powered-By: Express Content-Type: text/html; charset=utf-8 Content-Length: 12

Hello World!

X-Powered-By: Express confirma o adapter padrão. Nest organiza o fluxo; Express é a plataforma HTTP escolhida pelo scaffold. Fastify é uma opção oficial, mas não precisa entrar no primeiro projeto.

Erro real: a porta já está ocupada

Com o primeiro servidor ainda na 3000, tente iniciar outra cópia na mesma porta:

bash
npm run start
Error: listen EADDRINUSE: address already in use :::3000 code: 'EADDRINUSE' syscall: 'listen' address: '::' port: 3000 Node.js v24.16.0

EADDRINUSE significa “endereço em uso”. A aplicação conseguiu montar os módulos, mas o sistema operacional recusou uma segunda escuta na mesma porta. Encerre o processo anterior com Ctrl+C ou escolha outra porta:

bash
PORT=3001 npm run start
curl http://localhost:3001/
Hello World!

Não troque arquivos do controller para resolver esse erro; a mensagem aponta para a etapa de rede, não para a rota.

Faça uma mudança pequena e observável

Troque apenas o retorno do service:

ts
getHello(): string {
  return 'Agenda de mentorias no ar!';
}

Com start:dev, salve e consulte novamente:

bash
curl http://localhost:3000/
Agenda de mentorias no ar!

Você alterou o provider, não a rota. O caminho HTTP permaneceu /, enquanto o comportamento chamado pelo controller mudou. Esse contraste é a primeira pista de por que o Nest separa as responsabilidades.

Missão: crie uma rota de status

No AppController, acrescente @Get('status') e retorne um objeto com aplicacao: 'agenda-mentorias' e status: 'ok'. Não remova a rota inicial. Depois rode o build e as duas requisições:

bash
npm run build
curl http://localhost:3000/
curl http://localhost:3000/status

O critério de sucesso é: build sem erro, / continua devolvendo a frase e /status devolve JSON com os dois campos. Confira também se o log mostra Mapped {/status, GET} route. Quando isso acontecer, você já consegue ler o bootstrap, registrar uma rota e verificar o resultado sem depender do navegador.

O próximo passo é transformar esse começo genérico em uma funcionalidade real, separando a agenda num módulo próprio. O guia completo de NestJS continua disponível como mapa para DTO, autenticação e banco.

  • nestjs
  • nest cli
  • node
  • typescript
  • api
  • express

Perguntas frequentes

Preciso instalar Nest CLI globalmente?
Não. Você pode executar uma versão específica com npx, como neste artigo. Isso evita depender de uma instalação global e deixa a criação do projeto mais reproduzível.
Qual versão do Node o NestJS exige?
A documentação oficial atual pede Node.js 20 ou superior. O projeto desta lição foi executado com Node 24.16.0.
NestJS usa Express ou Fastify?
O scaffold padrão usa @nestjs/platform-express. Fastify é uma plataforma opcional; você precisa instalar e selecionar o adapter correspondente.
Qual a diferença entre start e start:dev?
start inicia a aplicação uma vez. start:dev observa os arquivos e reinicia o processo durante o desenvolvimento quando o código muda.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, npm 11.13.0, NestJS 11.2.1, Nest CLI 11.0.24 e TypeScript 5.9.3, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. NestJS — primeiros passos — docs.nestjs.com
  2. NestJS — documentação do CLI — docs.nestjs.com
  3. npm — pacote @nestjs/core — npmjs.com
  4. npm — pacote @nestjs/cli — npmjs.com

Continue por aqui