Installation & Setup
Getting a NestJS project running takes about a minute once your tooling is in place. The recommended path is the Nest CLI — a command-line tool that scaffolds a fully configured TypeScript project with sensible defaults, a build pipeline, and a test harness, so you write features instead of boilerplate. This page walks through the prerequisites, installing the CLI, generating a project with nest new, exploring what gets created, and verifying everything works in both development and production modes.
Prerequisites
NestJS targets modern Node.js. You need an active LTS release of Node.js (Node 18, 20, or 22 at the time of writing) and a package manager. The CLI works with npm, pnpm, and Yarn interchangeably. Verify your toolchain before starting:
node --version
npm --version
Output:
v20.11.1
10.2.4
If your Node version is older than the current LTS, install nvm and run
nvm install --lts. NestJS uses recent ECMAScript and TypeScript features, and older runtimes can fail at build or startup.
Installing the Nest CLI
The CLI is published as @nestjs/cli. Installing it globally gives you the nest command everywhere, which is convenient for scaffolding new projects:
npm install -g @nestjs/cli
Confirm the install and check the version:
nest --version
Output:
11.0.0
You do not strictly need a global install — you can scaffold with npx @nestjs/cli new — but a global CLI makes day-to-day commands like nest generate shorter.
Scaffolding a project with nest new
Create a new application with the new (alias n) command. The CLI prompts you to choose a package manager, then generates the project, installs dependencies, and initializes a Git repository:
nest new my-app
Output:
⚡ We will scaffold your app in a few seconds..
? Which package manager would you ❤️ to use? (Use arrow keys)
❯ npm
yarn
pnpm
CREATE my-app/src/app.controller.ts (274 bytes)
CREATE my-app/src/app.service.ts (142 bytes)
CREATE my-app/src/app.module.ts (249 bytes)
CREATE my-app/src/main.ts (228 bytes)
...
✔ Installation in progress... ☕
🚀 Successfully created project my-app
To pick the package manager non-interactively, pass --package-manager (alias -p):
nest new my-app --package-manager pnpm
| Flag | Alias | Purpose |
|---|---|---|
--package-manager <pm> | -p | Choose npm, yarn, or pnpm without the prompt |
--skip-install | Scaffold files but skip dependency installation | |
--skip-git | -g | Do not initialize a Git repository |
--strict | Enable TypeScript strict mode in tsconfig.json | |
--language <lang> | -l | Generate a ts (default) or js project |
Use
--strictfor new projects. It turns onstrictNullChecksand related flags, catching whole classes of bugs at compile time before they reach production.
Exploring the generated structure
The starter is intentionally small. Everything lives under src/, with configuration files at the root:
my-app/
├── src/
│ ├── app.controller.ts # A basic controller with one route
│ ├── app.controller.spec.ts # Unit test for the controller
│ ├── app.service.ts # A sample provider (service)
│ ├── app.module.ts # The root module
│ └── main.ts # Application entry point
├── test/ # End-to-end tests
├── nest-cli.json # Nest CLI configuration
├── tsconfig.json # TypeScript compiler options
├── package.json
└── eslint.config.mjs
The entry point bootstraps the application by creating an instance from the root module and listening on a port:
// src/main.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();
AppModule is the root of the dependency graph. It declares the controllers Nest should register and the providers its DI container should manage:
// src/app.module.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 {}
The controller injects the service through its constructor and exposes a single GET / route:
// src/app.controller.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();
}
}
Running the application
The generated package.json ships with ready-made scripts for every stage. For day-to-day work use start:dev, which compiles the app and reloads automatically whenever you save a file:
cd my-app
npm run start:dev
Output:
[Nest] 12345 - 06/14/2026, 10:02:11 AM LOG [NestFactory] Starting Nest application...
[Nest] 12345 - 06/14/2026, 10:02:11 AM LOG [InstanceLoader] AppModule dependencies initialized
[Nest] 12345 - 06/14/2026, 10:02:11 AM LOG [RoutesResolver] AppController {/}: +2ms
[Nest] 12345 - 06/14/2026, 10:02:11 AM LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 12345 - 06/14/2026, 10:02:11 AM LOG [NestApplication] Nest application successfully started
Verify it by hitting the default route:
curl http://localhost:3000
Output:
Hello World!
The full set of scripts covers the whole lifecycle:
| Script | Command | Use |
|---|---|---|
start | npm start | Run once without watch mode |
start:dev | npm run start:dev | Watch mode with hot reload for development |
start:debug | npm run start:debug | Watch mode with the Node inspector attached |
build | npm run build | Compile TypeScript to dist/ |
start:prod | npm run start:prod | Run the compiled dist/main.js in production |
For production you compile first, then run the JavaScript output — never ts-node in production:
npm run build
npm run start:prod
Best Practices
- Stick to an active LTS Node.js release and pin it with an
.nvmrcfile so the whole team runs the same runtime. - Scaffold with
nest new --strictto enable TypeScript strict mode from day one. - Use
start:devfor local work andstart:prodagainst the compileddist/output for deployments — never shipts-node. - Commit the generated
nest-cli.jsonandtsconfig.json; they define how the project builds and should stay in version control. - Read the bind port from
process.env.PORT(as the starter does) so platforms can override it. - Run
npm auditand keep@nestjs/*packages on the same major version to avoid peer-dependency mismatches.