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.
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:
node --version
npm --versionSe 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.
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:
Entre na pasta e confira as versões realmente resolvidas pelo npm:
cd agenda-mentorias
npm ls --depth=0 @nestjs/core @nestjs/common @nestjs/platform-expressEssa 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:
src/
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.tsmain.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:
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.
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():
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:
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:
npm run start:devAbra outro terminal e consulte a aplicação:
curl -i http://localhost:3000/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:
npm run startEADDRINUSE 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:
PORT=3001 npm run start
curl http://localhost:3001/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:
getHello(): string {
return 'Agenda de mentorias no ar!';
}Com start:dev, salve e consulte novamente:
curl http://localhost:3000/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:
npm run build
curl http://localhost:3000/
curl http://localhost:3000/statusO 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.
Perguntas frequentes
Preciso instalar Nest CLI globalmente?
Qual versão do Node o NestJS exige?
NestJS usa Express ou Fastify?
Qual a diferença entre start e start:dev?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
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
- NestJS — primeiros passos — docs.nestjs.com
- NestJS — documentação do CLI — docs.nestjs.com
- npm — pacote @nestjs/core — npmjs.com
- npm — pacote @nestjs/cli — npmjs.com


