☰ Mục lục series
Tài liệu NestJS tiếng Việt
  1. Nhập môn (Overview)
    1. Giới thiệu
    2. First steps
    3. Controllers
    4. Providers
    5. Modules
    6. Middleware
    7. Exception filters
    8. Pipes
    9. Guards
    10. Interceptors
    11. Custom decorators
  2. Nền tảng (Fundamentals)
    1. Custom providers
    2. Asynchronous providers
    3. Dynamic modules
    4. Injection scopes
    5. Circular dependency
    6. Module reference
    7. Lazy-loading modules
    8. Execution context
    9. Lifecycle events
    10. Discovery service
    11. Platform agnosticism
    12. Testing
  3. Kỹ thuật (Techniques)
    1. Configuration
    2. Database (SQL)
    3. MongoDB
    4. Validation
    5. Caching
    6. Serialization
    7. Versioning
    8. Task scheduling
    9. Queues
    10. Logging
    11. Cookies
    12. Events
    13. Compression
    14. File upload
    15. Streaming files
    16. HTTP module
    17. Session
    18. MVC
    19. Performance (Fastify)
    20. Server-Sent Events
  4. Bảo mật (Security)
    1. Authentication
    2. Authorization
    3. Encryption & Hashing
    4. Helmet
    5. CORS
    6. CSRF
    7. Rate limiting
    8. Vòng đời request
  5. OpenAPI (Swagger)
    1. Introduction
    2. Types and parameters
    3. Operations
    4. Security
    5. Mapped types
    6. Decorators
    7. CLI plugin
    8. Other features
  6. Recipes
    1. REPL
    2. CRUD generator
    3. SWC (fast compiler)
    4. Passport (auth)
    5. Hot reload
    6. MikroORM
    7. Router module
    8. Health checks (Terminus)
    9. CQRS
    10. Prisma
    11. Serve static
    12. Commander
    13. Async local storage
  7. WebSockets
    1. Gateways
    2. Exception filters
    3. Pipes
    4. Guards
    5. Interceptors
    6. Adapters
  8. Microservices
    1. Overview
    2. Redis
    3. MQTT
    4. NATS
    5. RabbitMQ
    6. Kafka
    7. gRPC
    8. Custom transporters
    9. Exception filters
    10. Pipes
    11. Pre-request hooks
    12. Guards
    13. Interceptors
  9. GraphQL
    1. Quick start
    2. Resolvers
    3. Mutations
    4. Subscriptions
    5. Scalars
    6. Directives
    7. Interfaces
    8. Unions and enums
    9. Field middleware
    10. Mapped types
    11. Plugins
    12. Complexity
    13. Extensions
    14. CLI plugin
    15. Generating SDL
    16. Sharing models
    17. Other features
    18. Federation
  10. CLI & triển khai
    1. CLI overview
    2. Workspaces
    3. Libraries
    4. Usage
    5. Scripts
    6. Deployment
    7. Standalone apps
  11. FAQ
    1. Serverless
    2. HTTP adapter
    3. Keep-Alive connections
    4. Global path prefix
    5. Raw body
    6. Hybrid application
    7. HTTPS & multiple servers
    8. Common errors
  12. Phụ lục
    1. Bảng thuật ngữ
Recipes

CRUD generator (chỉ dành cho TypeScript)

Bản dịch từ docs.nestjs.com/recipes/crud-generator

Trong suốt quá trình sống của một project, khi xây các feature mới, chúng ta thường cần thêm resource mới vào ứng dụng. Các resource này thường yêu cầu nhiều thao tác lặp đi lặp lại, mà ta phải làm lại mỗi khi định nghĩa một resource mới.

Giới thiệu

Hãy tưởng tượng một tình huống thực tế, khi ta cần expose các endpoint CRUD cho 2 entity, giả sử là User và Product. Theo best practice, với mỗi entity ta sẽ phải thực hiện nhiều thao tác, như sau:

  • Sinh một module (nest g mo) để giữ code có tổ chức và thiết lập ranh giới rõ ràng (nhóm các component liên quan)
  • Sinh một controller (nest g co) để định nghĩa route CRUD (hoặc query/mutation cho ứng dụng GraphQL)
  • Sinh một service (nest g s) để triển khai & tách biệt business logic
  • Sinh một entity class/interface để đại diện cho cấu trúc dữ liệu của resource
  • Sinh Data Transfer Object (hoặc input cho ứng dụng GraphQL) để định nghĩa cách dữ liệu được gửi qua network

Đúng là rất nhiều bước!

Để giúp tăng tốc quá trình lặp đi lặp lại này, Nest CLI cung cấp một generator (schematic) tự động sinh toàn bộ code khung (boilerplate), giúp bạn không phải làm tất cả những việc trên, và làm cho trải nghiệm phát triển đơn giản hơn nhiều.

Lưu ý Schematic này hỗ trợ sinh controller HTTP, controller Microservice, resolver GraphQL (cả code first và schema first), và WebSocket Gateway.

Sinh một resource mới

Để tạo một resource mới, chỉ cần chạy lệnh sau ở thư mục gốc của project:

$ nest g resource

Lệnh nest g resource không chỉ sinh tất cả các building block của NestJS (class module, service, controller) mà còn sinh cả entity class, DTO class cũng như các file test (.spec).

Dưới đây là file controller được sinh ra (cho REST API):

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() createUserDto: CreateUserDto) {
    return this.usersService.create(createUserDto);
  }

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

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

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    return this.usersService.update(+id, updateUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.usersService.remove(+id);
  }
}

Ngoài ra, nó tự động tạo sẵn placeholder cho tất cả endpoint CRUD (route cho REST API, query và mutation cho GraphQL, message subscribe cho cả Microservice và WebSocket Gateway) - tất cả mà không cần bạn động tay.

Lưu ý Các class service được sinh ra không gắn với bất kỳ ORM (hay nguồn dữ liệu) cụ thể nào. Điều này giúp generator đủ tổng quát để đáp ứng nhu cầu của mọi project. Theo mặc định, tất cả phương thức sẽ chứa placeholder, cho phép bạn tự điền nguồn dữ liệu phù hợp với project của mình.

Ghi chú người dịch Đây là điểm khác biệt lớn so với generator của các framework khác (ví dụ php artisan make:model của Laravel thường sinh sẵn code gắn với Eloquent, hay một số scaffold tool của Go gắn cứng với GORM). NestJS CLI cố tình sinh service “rỗng” (chỉ có placeholder), vì Nest không ép bạn dùng ORM nào cụ thể — bạn phải tự viết phần gọi TypeORM/Prisma/Mongoose vào trong các phương thức đó. Nếu bạn mong đợi code sinh ra chạy được ngay như của Laravel, đây là điều dễ gây bất ngờ.

Tương tự, nếu bạn muốn sinh resolver cho ứng dụng GraphQL, chỉ cần chọn GraphQL (code first) (hoặc GraphQL (schema first)) làm transport layer.

Trong trường hợp này, NestJS sẽ sinh ra một resolver class thay vì controller REST API:

$ nest g resource users

> ? What transport layer do you use? GraphQL (code first)
> ? Would you like to generate CRUD entry points? Yes
> CREATE src/users/users.module.ts (224 bytes)
> CREATE src/users/users.resolver.spec.ts (525 bytes)
> CREATE src/users/users.resolver.ts (1109 bytes)
> CREATE src/users/users.service.spec.ts (453 bytes)
> CREATE src/users/users.service.ts (625 bytes)
> CREATE src/users/dto/create-user.input.ts (195 bytes)
> CREATE src/users/dto/update-user.input.ts (281 bytes)
> CREATE src/users/entities/user.entity.ts (187 bytes)
> UPDATE src/app.module.ts (312 bytes)

Gợi ý Để tránh sinh file test, bạn có thể truyền flag --no-spec, như sau: nest g resource users --no-spec

Có thể thấy, không chỉ toàn bộ mutation và query khung được tạo ra, mà mọi thứ còn được liên kết chặt chẽ với nhau. Chúng ta đang dùng UsersService, entity User, và các DTO của mình.

import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql';
import { UsersService } from './users.service.js';
import { User } from './entities/user.entity.js';
import { CreateUserInput } from './dto/create-user.input.js';
import { UpdateUserInput } from './dto/update-user.input.js';

@Resolver(() => User)
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}

  @Mutation(() => User)
  createUser(@Args('createUserInput') createUserInput: CreateUserInput) {
    return this.usersService.create(createUserInput);
  }

  @Query(() => [User], { name: 'users' })
  findAll() {
    return this.usersService.findAll();
  }

  @Query(() => User, { name: 'user' })
  findOne(@Args('id', { type: () => Int }) id: number) {
    return this.usersService.findOne(id);
  }

  @Mutation(() => User)
  updateUser(@Args('updateUserInput') updateUserInput: UpdateUserInput) {
    return this.usersService.update(updateUserInput.id, updateUserInput);
  }

  @Mutation(() => User)
  removeUser(@Args('id', { type: () => Int }) id: number) {
    return this.usersService.remove(id);
  }
}

Nội dung gốc: NestJS Documentation © Kamil Myśliwiec, giấy phép MIT. Bản dịch và ghi chú: Lê Văn Dũng.