☰ 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ữ
GraphQL

Extensions

Bản dịch từ docs.nestjs.com/graphql/extensions

Cảnh báo Chương này chỉ áp dụng cho cách tiếp cận code first.

Extensions là một tính năng nâng cao, ở mức thấp (low-level) cho phép bạn định nghĩa dữ liệu tùy ý ngay trong phần cấu hình type. Việc gắn metadata tùy chỉnh vào một field nào đó giúp bạn xây dựng những giải pháp tinh vi và tổng quát hơn. Ví dụ, với extensions, bạn có thể định nghĩa role (vai trò) cần có ở cấp field để truy cập field đó. Các role này có thể được đọc lại (reflect) lúc runtime để xác định caller (bên gọi) có đủ quyền lấy field cụ thể hay không.

Thêm metadata tùy chỉnh

Để gắn metadata tùy chỉnh cho một field, sử dụng decorator @Extensions() được export từ package @nestjs/graphql.

@Field()
@Extensions({ role: Role.ADMIN })
password: string;

Trong ví dụ trên, ta gán cho thuộc tính metadata role giá trị Role.ADMIN. Role là một enum TypeScript đơn giản, gom nhóm tất cả các role người dùng có trong hệ thống.

Lưu ý, ngoài việc gắn metadata trên field, bạn cũng có thể dùng decorator @Extensions() ở cấp class và cấp method (ví dụ, trên query handler).

Sử dụng metadata tùy chỉnh

Logic tận dụng metadata tùy chỉnh này có thể phức tạp tùy ý. Ví dụ, bạn có thể tạo một interceptor đơn giản để lưu/log sự kiện mỗi lần method được gọi, hoặc một field middleware đối chiếu role cần thiết để lấy một field với quyền của caller (hệ thống phân quyền ở cấp field).

Để minh họa, hãy định nghĩa một checkRoleMiddleware so sánh role của user (ở đây hardcode sẵn) với role cần có để truy cập field mục tiêu:

export const checkRoleMiddleware: FieldMiddleware = async (
  ctx: MiddlewareContext,
  next: NextFn,
) => {
  const { info } = ctx;
  const { extensions } = info.parentType.getFields()[info.fieldName];

  /**
   * In a real-world application, the "userRole" variable
   * should represent the caller's (user) role (for example, "ctx.user.role").
   */
  const userRole = Role.USER;
  if (userRole === extensions.role) {
    // or just "return null" to ignore
    throw new ForbiddenException(
      `User does not have sufficient permissions to access "${info.fieldName}" field.`,
    );
  }
  return next();
};

Với đoạn code này, ta có thể đăng ký một middleware cho field password, như sau:

@Field({ middleware: [checkRoleMiddleware] })
@Extensions({ role: Role.ADMIN })
password: string;

Ghi chú người dịch Có thể hình dung @Extensions() như việc gắn thêm một “custom tag” vào định nghĩa field trong GraphQL schema — tương tự cách bạn gắn struct tag trong Go (json:"...") hay annotation trong Laravel để mô tả metadata không ảnh hưởng logic type, rồi đọc lại metadata đó ở nơi khác (middleware, guard, interceptor) để ra quyết định runtime. Field middleware ở đây đóng vai trò tương tự middleware Express bọc quanh req/res, chỉ khác là nó bọc quanh việc resolve một field GraphQL cụ thể thay vì bọc quanh cả request HTTP.

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.