☰ 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

Interfaces

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

Giống như nhiều hệ thống kiểu dữ liệu khác, GraphQL hỗ trợ interface. Một Interface là một kiểu trừu tượng bao gồm một tập hợp field bắt buộc mà một type phải có để implement interface đó (đọc thêm tại đây).

Code first

Khi dùng cách tiếp cận code first, bạn định nghĩa một GraphQL interface bằng cách tạo một abstract class được gắn decorator @InterfaceType() export từ @nestjs/graphql.

import { Field, ID, InterfaceType } from '@nestjs/graphql';

@InterfaceType()
export abstract class Character {
  @Field(() => ID)
  id: string;

  @Field()
  name: string;
}

Cảnh báo TypeScript interface (từ khóa interface của TypeScript) không thể dùng để định nghĩa GraphQL interface.

Ghi chú người dịch Đây là điểm dễ nhầm nếu bạn quen TypeScript: interface của TS chỉ tồn tại ở compile-time, bị xóa hoàn toàn khi build (giống generic bị “erasure”), nên Nest không có gì để đọc metadata lúc runtime. Vì decorator cần một class thật (có mặt ở runtime) để gắn reflection metadata lên, nên GraphQL interface trong Nest bắt buộc phải là abstract class.

Kết quả sẽ sinh ra phần schema GraphQL sau ở dạng SDL:

interface Character {
  id: ID!
  name: String!
}

Bây giờ, để implement interface Character, dùng khóa implements:

@ObjectType({
  implements: () => [Character],
})
export class Human implements Character {
  id: string;
  name: string;
}

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

Function resolveType() mặc định do thư viện sinh ra sẽ xác định type dựa trên giá trị trả về từ phương thức resolver. Điều này có nghĩa là bạn phải trả về instance của class (bạn không thể trả về literal JavaScript object).

Để cung cấp một function resolveType() tùy chỉnh, truyền thuộc tính resolveType vào object options của decorator @InterfaceType(), như sau:

@InterfaceType({
  resolveType(book) {
    if (book.colors) {
      return ColoringBook;
    }
    return TextBook;
  },
})
export abstract class Book {
  @Field(() => ID)
  id: string;

  @Field()
  title: string;
}

Interface resolvers

Từ đầu đến giờ, khi dùng interface, bạn chỉ mới chia sẻ được định nghĩa field với các object của mình. Nếu bạn cũng muốn chia sẻ cả phần implementation thực tế của field resolver, bạn có thể tạo một interface resolver riêng, như sau:

import { Resolver, ResolveField, Parent, Info } from '@nestjs/graphql';

@Resolver((type) => Character) // Nhắc lại: Character là một interface
export class CharacterInterfaceResolver {
  @ResolveField(() => [Character])
  friends(
    @Parent() character, // Object đã resolve implement Character
    @Info() { parentType }, // Type của object implement Character
    @Args('search', { type: () => String }) searchTerm: string,
  ) {
    // Lấy danh sách bạn bè của character
    return [];
  }
}

Giờ đây field resolver friends sẽ tự động được đăng ký cho tất cả object type implement interface Character.

Cảnh báo Cách này yêu cầu thuộc tính inheritResolversFromInterfaces phải được đặt là true trong cấu hình GraphQLModule.

Ghi chú người dịch Nếu bạn từng làm việc với Go, cơ chế inheritResolversFromInterfaces gợi nhớ tới interface satisfaction của Go — struct nào có đủ method là tự động implement interface, không cần khai báo tường minh. Ở đây khác một chút: bạn khai báo implements tường minh trong @ObjectType, nhưng phần logic resolver thì được “thừa kế” tự động từ interface resolver, giúp tránh copy-paste field resolver giống hệt nhau cho từng type con.

Schema first

Để định nghĩa một interface trong cách tiếp cận schema first, chỉ cần tạo một GraphQL interface bằng SDL.

interface Character {
  id: ID!
  name: String!
}

Sau đó, bạn có thể dùng tính năng sinh typing (như đã trình bày ở chương quick start) để sinh ra định nghĩa TypeScript tương ứng:

export interface Character {
  id: string;
  name: string;
}

Interface yêu cầu thêm một field __resolveType trong resolver map để xác định interface nên resolve về type nào. Hãy tạo một class CharactersResolver và định nghĩa phương thức __resolveType:

@Resolver('Character')
export class CharactersResolver {
  @ResolveField()
  __resolveType(value) {
    if ('age' in value) {
      return Person;
    }
    return null;
  }
}

Gợi ý Tất cả decorator đều được export từ package @nestjs/graphql.

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.