☰ 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

Directives

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

Một directive có thể được gắn vào một field hoặc một fragment inclusion, và có thể ảnh hưởng đến việc thực thi query theo bất kỳ cách nào mà server mong muốn (đọc thêm tại đây). Đặc tả GraphQL cung cấp sẵn vài directive mặc định:

  • @include(if: Boolean) - chỉ đưa field này vào kết quả nếu argument là true
  • @skip(if: Boolean) - bỏ qua field này nếu argument là true
  • @deprecated(reason: String) - đánh dấu field là deprecated kèm thông điệp giải thích

Một directive là một định danh đứng sau ký tự @, có thể theo sau bởi một danh sách named argument, và có thể xuất hiện sau hầu như bất kỳ phần tử nào trong ngôn ngữ query và schema của GraphQL.

Custom directives

Để chỉ định điều gì sẽ xảy ra khi Apollo/Mercurius gặp directive của bạn, bạn có thể tạo một transformer function. Function này dùng hàm mapSchema để duyệt qua các vị trí trong schema (định nghĩa field, định nghĩa type, v.v.) và thực hiện các phép biến đổi tương ứng.

import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils';
import { defaultFieldResolver, GraphQLSchema } from 'graphql';

export function upperDirectiveTransformer(
  schema: GraphQLSchema,
  directiveName: string,
) {
  return mapSchema(schema, {
    [MapperKind.OBJECT_FIELD]: (fieldConfig) => {
      const upperDirective = getDirective(
        schema,
        fieldConfig,
        directiveName,
      )?.[0];

      if (upperDirective) {
        const { resolve = defaultFieldResolver } = fieldConfig;

        // Thay thế resolver gốc bằng một function mà *trước tiên* gọi
        // resolver gốc, sau đó chuyển kết quả của nó sang chữ hoa
        fieldConfig.resolve = async function (source, args, context, info) {
          const result = await resolve(source, args, context, info);
          if (typeof result === 'string') {
            return result.toUpperCase();
          }
          return result;
        };
        return fieldConfig;
      }
    },
  });
}

Ghi chú người dịch Cơ chế này tương tự middleware transform schema kiểu graphql-tools mà bạn có thể đã gặp ở các dự án Apollo Server thuần Node — nó không phải tính năng riêng của Nest, Nest chỉ cung cấp chỗ để “cắm” transformer này vào (transformSchema) rồi tự động build lại schema mỗi lần khởi động app.

Bây giờ, áp dụng function transform upperDirectiveTransformer trong phương thức GraphQLModule#forRoot bằng hàm transformSchema:

GraphQLModule.forRoot({
  // ...
  transformSchema: (schema) => upperDirectiveTransformer(schema, 'upper'),
});

Sau khi đăng ký, directive @upper có thể được sử dụng trong schema của chúng ta. Tuy nhiên, cách bạn áp dụng directive sẽ khác nhau tùy theo cách tiếp cận bạn dùng (code first hay schema first).

Code first

Trong cách tiếp cận code first, dùng decorator @Directive() để áp dụng directive.

@Directive('@upper')
@Field()
title: string;

Gợi ý Decorator @Directive() được export từ package @nestjs/graphql.

Directive có thể được áp dụng trên field, field resolver, input type và object type, cũng như trên query, mutation, và subscription. Dưới đây là ví dụ directive được áp dụng ở cấp query handler:

@Directive('@deprecated(reason: "This query will be removed in the next version")')
@Query(() => Author, { name: 'author' })
async getAuthor(@Args({ name: 'id', type: () => Int }) id: number) {
  return this.authorsService.findOneById(id);
}

Cảnh báo Các directive áp dụng thông qua decorator @Directive() sẽ không được phản ánh trong file định nghĩa schema được sinh ra tự động.

Cuối cùng, đảm bảo khai báo các directive trong GraphQLModule, như sau:

GraphQLModule.forRoot({
  // ...,
  transformSchema: schema => upperDirectiveTransformer(schema, 'upper'),
  buildSchemaOptions: {
    directives: [
      new GraphQLDirective({
        name: 'upper',
        locations: [DirectiveLocation.FIELD_DEFINITION],
      }),
    ],
  },
}),

Gợi ý Cả GraphQLDirective lẫn DirectiveLocation đều được export từ package graphql.

Schema first

Trong cách tiếp cận schema first, áp dụng directive trực tiếp trong SDL.

directive @upper on FIELD_DEFINITION

type Post {
  id: Int!
  title: String! @upper
  votes: Int
}

Ghi chú người dịch Nếu bạn từng viết middleware kiểu graphql-middleware (phổ biến trong hệ sinh thái Apollo Server thuần), directive ở đây đóng vai trò tương tự: một lớp xử lý chạy quanh resolver của field, chỉ khác là được khai báo ngay trong SDL thay vì code, và schema-first approach của Nest sẽ tự map nó vào transformer bạn đăng ký.

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.