nest_02_get_started - App Bootstrap & CLI Deep Dive¶
You installed the CLI and ran npm run start:dev Now let's actually understand what that command does and how NestJS bootstraps itself before you cargo-cult your way into a production app you don't understand
what's in here¶
- Standalone app vs Express adapter
- The main.ts bootstrap in detail
- Hot reload setup (Webpack HMR)
- CLI commands beyond
new - Configuration at startup
standalone vs Express adapter¶
By default NestFactory.create() wraps an Express application But you're not locked into Express - Nest supports Fastify as an alternative
// Express (default)
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
// Express is imported internally , no explicit import needed
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await app.listen(3000)
}
// Fastify
import { NestFactory } from '@nestjs/core'
import { FastifyAdapter } from '@nestjs/platform-fastify'
import { AppModule } from './app.module'
async function bootstrap() {
const app = await NestFactory.create(
AppModule,
new FastifyAdapter()
)
await app.listen(3000)
}
Fastify is faster in benchmarks but Express has larger ecosystem For most APIs the difference doesn't matter until you're at serious scale Pick Express for compatibility , Fastify for performance
the bootstrap sequence¶
import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe } from '@nestjs/common'
import helmet from 'helmet'
async function bootstrap() {
const app = await NestFactory.create(AppModule)
// security middleware - always first
app.use(helmet())
// CORS - configure properly for production
app.enableCors({
origin: process.env.ALLOWED_ORIGINS?.split(',') || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
credentials: true
})
// global validation - every input gets checked
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true
})
)
// global prefix - versioning your API
app.setGlobalPrefix('api/v1')
await app.listen(process.env.PORT || 3000)
console.log(`API running on port ${process.env.PORT || 3000}`)
}
bootstrap()
Every line in bootstrap matters app.use(helmet()) sets security headers before any route handler runs app.enableCors() prevents browsers from blocking legitimate requests app.useGlobalPipes() ensures validation catches malformed input before it reaches your logic app.setGlobalPrefix() means all routes are automatically prefixed , so @Get('users') becomes /api/v1/users
hot reload with Webpack HMR¶
Nest's default start:dev uses ts-node with file watching It works but gets slow as your project grows Webpack HMR is faster because it only recompiles changed files
npm install --save-dev webpack-node-externals
Then in nest-cli.json:
{
"compilerOptions": {
"webpack": true,
"webpackConfigPath": "webpack-hmr.config.js"
}
}
Create webpack-hmr.config.js:
const nodeExternals = require('webpack-node-externals')
module.exports = function (options) {
return {
...options,
externals: [nodeExternals()],
output: {
...options.output,
// preserve the original file structure
libraryTarget: 'commonjs2'
}
}
}
And update main.ts with HMR support:
declare const module: any
async function bootstrap() {
const app = await NestFactory.create(AppModule)
await app.listen(3000)
if (module.hot) {
module.hot.accept()
module.hot.dispose(() => app.close())
}
}
bootstrap()
HMR is not required but saves you 5-10 seconds per restart Over a day of development that's real time
CLI commands that matter¶
# generate a module
nest generate module users
# generates src/users/users.module.ts
# generate a controller with spec
nest g co users --no-spec
# generates src/users/users.controller.ts (no test file)
# generate a service
nest g s auth
# generates src/auth/auth.service.ts
# generate everything for a resource
nest g resource admin
# prompts for REST/GraphQL , generates module + controller + service + DTO + entities
The nest g resource command is the most underrated It generates CRUD boilerplate with proper DTOs , entities , and module setup Use it when starting new features - it's not cheating , it's being efficient
building for production¶
# build
npm run build
# outputs to dist/ directory
# run production
node dist/main.js
The build process compiles TypeScript to JS , bundles with Webpack if enabled , and outputs to dist/ For Docker deployment you'll want multi-stage builds - covered in nest_15_deploy
environment configuration at bootstrap¶
npm install @nestjs/config
// app.module.ts
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV || 'development'}`
})
]
})
export class AppModule {}
This loads your .env file at bootstrap , before any service starts Services then inject ConfigService instead of reading process.env directly Keeps your configuration testable and environment-aware
prerequisites¶
nest_01_intro - Installation & First App
next -> nest_03_controllers - Controllers & Request Handling