Thiết kế Controller & HTTP Handlers

Trong kiến trúc NestJS, Controller đóng vai trò là “cửa ngõ” tiếp nhận toàn bộ các yêu cầu HTTP (HTTP Requests) gửi tới từ phía Client (trình duyệt, ứng dụng di động). Nhiệm vụ cốt lõi của nó là điều phối (route) yêu cầu, trích xuất tham số dữ liệu và chuyển tiếp cho tầng nghiệp vụ (Service) xử lý, sau đó trả về kết quả cho Client. Tài liệu này tổng hợp cấu trúc Controller, các decorator xử lý HTTP request thường dùng và hướng dẫn cách viết prompt để Claude Code sinh ra các Controller tối ưu nhất.

1. Cấu trúc cơ bản của một Controller

Một Controller được định nghĩa bằng cách sử dụng @Controller() decorator đặt trên một Class. Bạn có thể truyền vào một chuỗi tiền tố đường dẫn (prefix route) để gom nhóm các endpoint có liên quan.
src/products/products.controller.ts
import { Controller, Get } from '@nestjs/common';

@Controller('products') // Định nghĩa đường dẫn gốc là /products
export class ProductsController {
  @Get() // Ánh xạ GET /products
  findAll(): string {
    return 'Lấy danh sách sản phẩm';
  }
}

2. Các HTTP Method Decorator (Handlers)

Để gán một hành động HTTP cụ thể vào một phương thức trong Controller, NestJS cung cấp các decorator tương ứng cho từng phương thức truyền tin:
DecoratorHTTP MethodÝ nghĩa nghiệp vụVí dụ Endpoint
@Get()GETĐọc dữ liệu (Lấy danh sách hoặc chi tiết).GET /products/12
@Post()POSTTạo mới dữ liệu.POST /products
@Patch()PATCHCập nhật một phần dữ liệu đã tồn tại.PATCH /products/12
@Put()PUTThay thế toàn bộ thực thể dữ liệu cũ.PUT /products/12
@Delete()DELETEXóa tài nguyên khỏi hệ thống.DELETE /products/12

3. Các Decorator Trích xuất Dữ liệu Request (Payload Extractors)

Khi Client gửi Request lên Server, dữ liệu có thể nằm ở nhiều vị trí khác nhau (Body, URL, Query, Headers). NestJS cung cấp các decorator chuyên biệt để trích xuất dữ liệu này một cách an toàn và gọn gàng trực tiếp trên tham số của hàm:
Trích xuất phần Body của Request (chủ yếu dùng cho POST, PUT, PATCH). Dữ liệu sẽ tự động được ánh xạ vào kiểu DTO đã khai báo.
@Post()
create(@Body() createProductDto: CreateProductDto) {
  return this.productsService.create(createProductDto);
}

4. Kiểm soát kiểu dữ liệu bằng Pipes

Khi trích xuất tham số từ URL (@Param) hoặc Query (@Query), kiểu dữ liệu mặc định nhận về luôn là chuỗi ký tự (string). Để chuyển đổi và kiểm soát kiểu dữ liệu an toàn trước khi truyền vào Service, bạn nên sử dụng các Pipes tích hợp sẵn của NestJS:
  • ParseIntPipe: Chuyển đổi và xác thực tham số phải là số nguyên (Integer).
  • ParseUUIDPipe: Xác thực chuỗi ký tự gửi lên có đúng chuẩn định dạng UUID hay không.
@Get(':id')
// Chuyển đổi an toàn string '12' thành number 12, báo lỗi ngay nếu truyền vào chuỗi chữ cái như '/products/abc'
findOne(@Param('id', ParseIntPipe) id: number) {
  return this.productsService.findOne(id);
}

Hướng dẫn viết Prompt để Claude Code thiết kế Controller chuẩn chỉnh

Khi yêu cầu Claude Code viết Controller, bạn nên mô tả rõ ràng các endpoints, phương thức HTTP tương ứng, các tham số cần trích xuất và kiểu Pipe cần áp dụng.

Prompt mẫu thiết kế Controller chuyên nghiệp:

Hãy thiết kế một Controller 'OrdersController' trong file 'src/orders/orders.controller.ts' kế thừa nghiệp vụ từ 'OrdersService':
1. Định nghĩa đường dẫn gốc (prefix route) là 'orders'.
2. Hàm 'createOrder' (POST /orders):
   - Trích xuất '@Body' sử dụng kiểu 'CreateOrderDto'.
3. Hàm 'getOrderDetails' (GET /orders/:id):
   - Nhận vào tham số 'id' động trên URL.
   - Hãy sử dụng 'ParseUUIDPipe' để xác thực ID gửi lên bắt buộc phải đúng định dạng UUID trước khi truyền vào Service.
4. Hàm 'cancelOrder' (DELETE /orders/:id):
   - Nhận vào tham số 'id' động trên URL dạng UUID (dùng ParseUUIDPipe).
5. Hàm 'getUserOrders' (GET /orders):
   - Trích xuất các query parameters gồm 'page' và 'limit' từ '@Query'. Sử dụng 'ParseIntPipe' cho cả hai trường này để chuyển về dạng số.
Mặc định, NestJS trả về status code 200 cho mọi request thành công (ngoại trừ POST là 201). Nếu bạn muốn thay đổi mã phản hồi mặc định của một endpoint, hãy yêu cầu Claude Code thêm decorator @HttpCode(HttpStatus.NO_CONTENT) (hoặc mã mong muốn).