Skip to content

NestJS Template

The NestJS template provides a modern Node.js backend framework with TypeScript, perfect for building scalable server-side applications with excellent developer experience.

Technical Stack

  • NestJS - Node.js framework
  • TypeScript - Type-safe development
  • Express - Web framework
  • Docker - Containerization
  • MariaDB - Database
  • Redis - Caching
  • Jest - Testing framework

Quick Start

1. Create Project

bash
# Initialize project
vup init my-api-project
cd my-api-project

# Add NestJS template
vup add my-api

2. Install Dependencies

bash
# Install dependencies
pnpm install

3. Start Development

bash
# Start development server
cd apps/my-api
pnpm dev

The API server will be available at http://localhost:3000.

Project Structure

apps/my-api/
├── src/
│   ├── app.controller.ts   # App controller
│   ├── app.module.ts       # App module
│   ├── app.service.ts      # App service
│   └── main.ts             # Application entry point
├── test/
│   ├── app.e2e-spec.ts     # E2E tests
│   └── jest-e2e.json       # Jest E2E configuration
├── docker-compose.yml      # Docker configuration
├── nest-cli.json           # NestJS CLI configuration
├── package.json            # Dependencies and scripts
└── tsconfig.json           # TypeScript configuration

Core Features

Controller

typescript
// app.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }

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

  @Post()
  create(@Body() createDto: any): string {
    return this.appService.create(createDto);
  }
}

Service

typescript
// app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }

  findOne(id: string): string {
    return `Found item with id: ${id}`;
  }

  create(createDto: any): string {
    return `Created item: ${JSON.stringify(createDto)}`;
  }
}

Module

typescript
// 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 {}

Main Application

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

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

  // Enable CORS
  app.enableCors();

  // Set global prefix
  app.setGlobalPrefix('api');

  await app.listen(3000);
  console.log('Application is running on: http://localhost:3000');
}
bootstrap();

Development Tools

NestJS Configuration

json
// nest-cli.json
{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true
  }
}

TypeScript Configuration

json
// tsconfig.json
{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@shared/*": ["../../_shared/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
}

Docker Configuration

yaml
# docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=development
    depends_on:
      - mariadb
      - redis

  mariadb:
    image: mariadb:10.9
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: myapp
      MYSQL_USER: user
      MYSQL_PASSWORD: password
    ports:
      - '3306:3306'
    volumes:
      - mariadb_data:/var/lib/mysql

  redis:
    image: redis:7-alpine
    ports:
      - '6379:6379'
    volumes:
      - redis_data:/data

volumes:
  mariadb_data:
  redis_data:

Available Scripts

json
{
  "scripts": {
    "dev": "nest start --watch",
    "build": "nest build",
    "start": "node dist/main",
    "start:prod": "node dist/main",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json",
    "lint": "eslint src/ --ext .ts",
    "lint:fix": "eslint src/ --ext .ts --fix",
    "format": "prettier --write \"src/**/*.{ts,json}\"",
    "format:check": "prettier --check \"src/**/*.{ts,json}\""
  }
}

Best Practices

Error Handling

typescript
import { HttpException, HttpStatus } from '@nestjs/common';

@Controller()
export class AppController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    if (!id || isNaN(Number(id))) {
      throw new HttpException('Invalid ID', HttpStatus.BAD_REQUEST);
    }

    try {
      return this.appService.findOne(id);
    } catch (error) {
      throw new HttpException('Item not found', HttpStatus.NOT_FOUND);
    }
  }
}

Validation

typescript
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';

export class CreateItemDto {
  @IsString()
  @IsNotEmpty()
  name: string;

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

@Controller()
export class AppController {
  @Post()
  create(@Body() createDto: CreateItemDto) {
    return this.appService.create(createDto);
  }
}

Database Integration

typescript
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Item } from './entities/item.entity';

@Injectable()
export class AppService {
  constructor(
    @InjectRepository(Item)
    private itemRepository: Repository<Item>
  ) {}

  async findAll(): Promise<Item[]> {
    return this.itemRepository.find();
  }

  async findOne(id: number): Promise<Item> {
    return this.itemRepository.findOne({ where: { id } });
  }

  async create(createDto: CreateItemDto): Promise<Item> {
    const item = this.itemRepository.create(createDto);
    return this.itemRepository.save(item);
  }
}

Testing

Unit Tests

typescript
// app.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppService } from './app.service';

describe('AppService', () => {
  let service: AppService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [AppService],
    }).compile();

    service = module.get<AppService>(AppService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  it('should return "Hello World!"', () => {
    expect(service.getHello()).toBe('Hello World!');
  });
});

E2E Tests

typescript
// test/app.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });
});

Deployment

Build for Production

bash
# Build the application
pnpm build

# The built files will be in the dist directory

Docker Deployment

bash
# Build Docker image
docker build -t my-api .

# Run with Docker Compose
docker-compose up -d