#1. NestJS คืออะไร

NestJS เป็นเฟรมเวิร์กฝั่งเซิร์ฟเวอร์สำหรับ Node.js ที่ใช้ TypeScript เป็นหลัก มีโครงสร้างแบบโมดูล และใช้แนวคิด Dependency Injection ช่วยให้พัฒนาและทดสอบ API ได้เป็นระบบ โดยค่าเริ่มต้นใช้ Express และสามารถเลือก Fastify ได้

#2. สิ่งที่จะสร้าง

สร้าง Products API แบบ CRUD โดยเก็บข้อมูลในหน่วยความจำเพื่อเรียนรู้พื้นฐาน (ข้อมูลจะหายเมื่อรีสตาร์ตแอป)

Method Endpoint หน้าที่
GET /products ดูสินค้าทั้งหมด
GET /products/:id ดูสินค้าตาม ID
POST /products เพิ่มสินค้า
PATCH /products/:id แก้ไขสินค้า
DELETE /products/:id ลบสินค้า

#3. เตรียมเครื่องมือ

ติดตั้ง Node.js เวอร์ชัน LTS ที่ NestJS รุ่นที่ใช้งานรองรับ และ npm จากนั้นสร้างโครงการ:

npm i -g @nestjs/cli
nest new nest-products-api
cd nest-products-api
npm install class-validator class-transformer @nestjs/mapped-types
nest g module products
nest g controller products
nest g service products

#4. สร้าง DTO และตรวจสอบข้อมูล

สร้าง src/products/dto/create-product.dto.ts

import { IsNotEmpty, IsNumber, IsString, Min } from 'class-validator';

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

  @IsNumber()
  @Min(0)
  price!: number;
}

สร้าง src/products/dto/update-product.dto.ts

import { PartialType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';

export class UpdateProductDto extends PartialType(CreateProductDto) {}

#5. สร้าง Service

แก้ไข src/products/products.service.ts

import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';

type Product = { id: number; name: string; price: number };

@Injectable()
export class ProductsService {
  private products: Product[] = [];
  private nextId = 1;

  findAll(): Product[] {
    return this.products;
  }

  findOne(id: number): Product {
    const product = this.products.find((item) => item.id === id);
    if (!product) throw new NotFoundException(`Product ${id} not found`);
    return product;
  }

  create(dto: CreateProductDto): Product {
    const product: Product = { id: this.nextId++, ...dto };
    this.products.push(product);
    return product;
  }

  update(id: number, dto: UpdateProductDto): Product {
    const product = this.findOne(id);
    Object.assign(product, dto);
    return product;
  }

  remove(id: number): void {
    this.findOne(id);
    this.products = this.products.filter((item) => item.id !== id);
  }
}

#6. สร้าง Controller

แก้ไข src/products/products.controller.ts

import {
  Body, Controller, Delete, Get, HttpCode, HttpStatus,
  Param, ParseIntPipe, Patch, Post,
} from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

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

  @Get(':id')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.productsService.findOne(id);
  }

  @Post()
  create(@Body() dto: CreateProductDto) {
    return this.productsService.create(dto);
  }

  @Patch(':id')
  update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateProductDto) {
    return this.productsService.update(id, dto);
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id', ParseIntPipe) id: number) {
    this.productsService.remove(id);
  }
}

nest g จะลงทะเบียน Controller และ Service ใน ProductsModule และเพิ่มโมดูลเข้า AppModule ตามค่าเริ่มต้นของ CLI ตรวจสอบไฟล์เหล่านี้หากใช้โครงสร้างโครงการที่ปรับแต่งเอง

#7. เปิดใช้งาน ValidationPipe

แก้ไข 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);
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }));
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

#8. เรียกใช้งานและทดสอบ

npm run start:dev

เปิดเทอร์มินัลอีกหน้าต่าง:

# เพิ่มสินค้า
curl -i -X POST http://localhost:3000/products \
  -H 'Content-Type: application/json' \
  -d '{"name":"Keyboard","price":990}'

# แสดงทั้งหมด
curl -i http://localhost:3000/products

# แสดงรายการเดียว
curl -i http://localhost:3000/products/1

# แก้ไขราคา
curl -i -X PATCH http://localhost:3000/products/1 \
  -H 'Content-Type: application/json' \
  -d '{"price":890}'

# ลบสินค้า
curl -i -X DELETE http://localhost:3000/products/1

ผลลัพธ์ที่คาดหวัง: POST ได้ 201 Created, GET/PATCH สำเร็จได้ 200 OK, DELETE ได้ 204 No Content, ID ที่ไม่มีอยู่ได้ 404 Not Found, และข้อมูลไม่ผ่าน DTO ได้ 400 Bad Request

#9. สถาปัตยกรรมการทำงาน

flowchart LR
    A[Client / Postman] --> B[ProductsController]
    B --> C[ValidationPipe + DTO]
    C --> D[ProductsService]
    D --> E[(In-memory products)]

หมายเหตุ: ในการทำงานจริง ValidationPipe ตรวจสอบข้อมูลระหว่างกระบวนการรับคำขอและเรียกเมธอด Controller; แผนภาพนี้แสดงส่วนประกอบเชิงแนวคิด

#10. แนวทางต่อยอดสำหรับ Production

  • ใช้ PostgreSQL ร่วมกับ Prisma หรือ TypeORM แทนการเก็บข้อมูลในหน่วยความจำ
  • เพิ่ม JWT Authentication และ Role-based Authorization
  • เพิ่ม Swagger/OpenAPI ด้วย @nestjs/swagger
  • จัดการ Configuration ผ่าน Environment Variables และตรวจสอบค่า config
  • เพิ่ม Unit Test, Integration Test และ E2E Test
  • ใช้ Pagination, Logging, Rate Limiting และระบบจัดการข้อผิดพลาด
  • ตรวจสอบ Dependency และใช้ HTTPS ก่อนเปิดบริการสู่สาธารณะ

#แหล่งศึกษา