Skip to content

nest_01_intro - Installation & First App

Table of Contents


TypeScript is non-negotiable here If you're still writing raw JavaScript in 2026 , close this tab and go take a long look at your life choices NestJS is built on decorators , interfaces , and type metadata - all TypeScript features - and fighting the framework by using JS means you're missing the entire point

what's in here

  • Installing NestJS CLI (@nestjs/cli)
  • Scaffolding your first project
  • The generated project structure explained
  • Understanding decorators (the magic that holds Nest together)
  • Running the app and seeing it work

installing the CLI

Global install or use npx , doesn't matter

npm install -g @nestjs/cli
# or
npx @nestjs/cli new my-first-api

The CLI asks about package manager Pick npm unless you have strong opinions about yarn vs pnpm ( and you probably shouldn't yet ) It generates a full project structure with TypeScript config , test setup , and a working "Hello World" endpoint

project structure breakdown

my-first-api/
  src/
    main.ts              # entry point , bootstraps the app
    app.module.ts        # root module , imports everything
    app.controller.ts    # root controller , handles /
    app.service.ts       # root service , business logic
    app.controller.spec.ts # unit test for the controller
  test/
    app.e2e-spec.ts      # end-to-end test
  nest-cli.json          # NestJS CLI configuration
  tsconfig.json          # TypeScript config (strict mode enabled)
  tsconfig.build.json    # build-specific TS config
  package.json

This looks like ceremony until your app has 40 files and you can still find anything in under 5 seconds The structure is predictable , and predictable is secure because you audit the right places

the main.ts boilerplate

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  // security headers right at bootstrap
  // you'll learn about helmet in the security section
  // app.use(helmet())

  await app.listen(3000)
  console.log('API running on http://localhost:3000')
}

bootstrap()
flowchart TD
    subgraph Request["HTTP Request Flow"]
        Req["Incoming Request"] --> Guard["Guard
        @UseGuards(AuthGuard)
        authorization check"]
        Guard -->|allowed| Pipe["Pipe
        @UsePipes(ValidationPipe)
        transform + validate"]
        Pipe -->|valid| Interceptor["Interceptor
        @UseInterceptors(LogInterceptor)
        pre-processing"]
        Interceptor --> Controller["Controller
        @Controller('users')
        route handling"]
        Controller --> Service["Service / Provider
        @Injectable()
        business logic"]
        Service --> DB["Database / External API"]
        DB --> Service --> Controller
        Controller --> Interceptor2["Interceptor
        post-processing"]
        Interceptor2 --> Response["HTTP Response"]

        Guard -->|denied| Forbidden["401 / 403 Forbidden"]
        Pipe -->|invalid| BadReq["400 Bad Request"]
    end

    subgraph Modules["Module Organization"]
        M1["UsersModule
        imports: []
        controllers: [UsersController]
        providers: [UsersService]"]
        M2["AuthModule
        imports: [JwtModule]
        controllers: [AuthController]
        providers: [AuthService]"]
        Root["AppModule
        imports: [UsersModule, AuthModule]"]
    end

    Root --> M1
    Root --> M2

    style Req fill:#3498db,color:#fff
    style Guard fill:#e74c3c,color:#fff
    style Pipe fill:#f1c40f,color:#000
    style Interceptor fill:#9b59b6,color:#fff
    style Controller fill:#2ecc71,color:#fff
    style Service fill:#1abc9c,color:#fff
    style Response fill:#3498db,color:#fff
    style Root fill:#e67e22,color:#fff
    style M1 fill:#95a5a6,color:#fff
    style M2 fill:#95a5a6,color:#fff

NestFactory.create() does a lot under the hood It creates an Express instance (default) , wires up DI , registers pipes/guards/interceptors from global declarations , and returns a configured NestApplication ready to accept connections

The AppModule parameter tells Nest which module is the root Everything else is discovered through imports - Nest traverses the module graph at bootstrap

decorators are everywhere

NestJS uses decorators for basically everything If you've never seen @ syntax before , it looks weird but it's just functions that modify the behavior of the class or method below them

import { Controller, Get } from '@nestjs/common'

@Controller('users')
export class UsersController {
  @Get()
  findAll(): string {
    return 'this endpoint exists'
  }
}

@Controller('users') tells Nest: "this class handles routes under /users" @Get() tells Nest: "this method handles GET /users"

No manual route registration , no express.Router() , no app.get() scattered through bootstrap The decorators declare intent , and Nest wires it up during module initialization

running the app

npm run start:dev

This starts the app with file watching (ts-node under the hood) Change a file , Nest recompiles and restarts For production you'll use npm run build then node dist/main.js , but for development this is fine

Hit http://localhost:3000 and you should see "Hello World!" or whatever the generated controller returns

what's actually happening

When you call NestFactory.create(AppModule):

  1. Nest reads the @Module decorator metadata from AppModule
  2. It walks imports , controllers , and providers recursively
  3. It builds the dependency injection container with all providers
  4. It registers all controllers with the underlying Express instance
  5. It applies global pipes , guards , interceptors , and filters
  6. It starts the HTTP server on the specified port

This all happens in under a second for small apps Larger apps with many modules take a bit more but usually under 3 seconds

common first-timer mistakes

  • Forgetting to register a provider in the module - you'll get a Nest can't resolve dependencies error , and the fix is adding it to providers: [] in the module
  • Not understanding that @nestjs/common and @nestjs/core are different packages - common has the decorators , core has the runtime
  • Running the app without start:dev and wondering why changes don't auto-reload - that's what :dev suffix does
  • Importing express types thinking you need them - Nest has its own request/response types that wrap Express

prerequisites

nest_00_home - NestJS HOME


next -> nest_02_get_started - App Bootstrap & CLI Deep Dive