Skip to content

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