Choorai
Lv.4 Production Node.js TypeScript

Learn Production Architecture with NestJS

NestJS is an enterprise-grade Node.js framework inspired by Angular. It uses modules, dependency injection, and decorator patterns.

Try Hono First

NestJS has a complex structure. If you're new to backend development, start with Hono (Lv.1) first.

Why NestJS?

Pros

  • Clear structure (Module/Controller/Service)
  • Dependency Injection (DI) support
  • Popular in the job market
  • Well-suited for large teams

Cons

  • Steep learning curve
  • Lots of boilerplate code
  • Overkill for small projects
  • Must be familiar with decorator syntax

Project Structure

NestJS uses a module-based architecture. Each feature is separated into modules, and modules consist of controllers and services.

Project Structure
src/
├── main.ts                    # Entry point
├── app.module.ts              # Root module
├── health/
│   ├── health.module.ts       # Health module
│   └── health.controller.ts   # Health controller
└── projects/
    ├── projects.module.ts     # Projects module
    ├── projects.controller.ts # Handles routing
    ├── projects.service.ts    # Business logic
    └── dto/
        ├── create-project.dto.ts
        └── update-project.dto.ts

Core Concepts

  • Module: A unit that groups related features. Manages dependencies via imports/exports
  • Controller: Handles HTTP requests. Responsible for routing and responses
  • Service: Business logic. Injected into controllers
  • DTO: Data Transfer Object. Used for input validation

Code Examples

1. Main Entry Point

src/main.ts
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import { AppModule } from './app.module';

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

  // CORS configuration
  app.enableCors({
    origin: ['http://localhost:5173'],
    credentials: true,
  });

  // Global validation pipe
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    transform: true,
  }));

  await app.listen(8000);
  console.log('🚀 NestJS running on http://localhost:8000');
}

bootstrap();

2. Root Module

src/app.module.ts
import { Module } from '@nestjs/common';
import { HealthModule } from './health/health.module';
import { ProjectsModule } from './projects/projects.module';

@Module({
  imports: [HealthModule, ProjectsModule],
})
export class AppModule {}

3. DTO (Input Validation)

src/projects/dto/create-project.dto.ts
import { IsString, IsOptional, MinLength, MaxLength } from 'class-validator';

export class CreateProjectDto {
  @IsString()
  @MinLength(1)
  @MaxLength(100)
  name: string;

  @IsOptional()
  @IsString()
  @MaxLength(500)
  description?: string;
}

4. Controller

src/projects/projects.controller.ts
import { Controller, Get, Post, Body, Param, Delete, HttpCode } from '@nestjs/common';
import { ProjectsService } from './projects.service';
import { CreateProjectDto } from './dto/create-project.dto';

@Controller('api/v1/projects')  // Route prefix
export class ProjectsController {
  // Dependency injection
  constructor(private readonly projectsService: ProjectsService) {}

  @Post()
  @HttpCode(201)
  create(@Body() createProjectDto: CreateProjectDto) {
    return this.projectsService.create(createProjectDto);
  }

  @Get()
  findAll() {
    return this.projectsService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.projectsService.findOne(id);
  }

  @Delete(':id')
  @HttpCode(204)
  remove(@Param('id') id: string) {
    return this.projectsService.remove(id);
  }
}

5. Service

src/projects/projects.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateProjectDto } from './dto/create-project.dto';

@Injectable()  // Register in DI container
export class ProjectsService {
  private projects = new Map();

  create(dto: CreateProjectDto) {
    const project = {
      id: crypto.randomUUID(),
      name: dto.name,
      description: dto.description || null,
      created_at: new Date().toISOString(),
    };
    this.projects.set(project.id, project);
    return project;
  }

  findAll() {
    return {
      items: Array.from(this.projects.values()),
      total: this.projects.size,
    };
  }

  findOne(id: string) {
    const project = this.projects.get(id);
    if (!project) throw new NotFoundException('Project not found');
    return project;
  }

  remove(id: string) {
    if (!this.projects.delete(id)) {
      throw new NotFoundException('Project not found');
    }
  }
}

Hono vs NestJS Comparison

Category Hono (Lv.1) NestJS (Lv.4)
File Count 5 12
Lines of Code ~150 lines ~300 lines
Learning Curve Low High
Structure Flexible Opinionated
Team Size 1-3 people 5+ people
Best For Quick prototypes, simple APIs Complex domains, long-term maintenance

Full Example Code

Check out the NestJS version of the B2B Admin API: examples/b2b-admin/api-nest

Next Steps

Once backend deployment is complete, learn how to connect PostgreSQL in Cycle 4: Database.

Last updated: February 22, 2026 · Version: v0.0.1

Send Feedback

Opens a new issue page with your message.

Open GitHub Issue