☰ 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ữ
Kỹ thuật (Techniques)

HTTP module

Bản dịch từ docs.nestjs.com/techniques/http-module

Axios là một package HTTP client giàu tính năng và được dùng rất rộng rãi. Nest wrap (bao) Axios lại và expose nó qua HttpModule có sẵn. HttpModule export class HttpService, class này expose các method dựa trên Axios để thực hiện HTTP request. Thư viện này cũng chuyển đổi các HTTP response nhận được thành Observables.

Gợi ý Bạn cũng có thể dùng trực tiếp bất kỳ thư viện HTTP client Node.js thông dụng nào khác, bao gồm got hoặc undici.

Cài đặt (Installation)

Để bắt đầu sử dụng, trước tiên chúng ta cài các dependency cần thiết.

$ npm i --save @nestjs/axios axios

Bắt đầu (Getting started)

Sau khi cài đặt xong, để dùng HttpService, trước tiên import HttpModule.

@Module({
  imports: [HttpModule],
  providers: [CatsService],
})
export class CatsModule {}

Tiếp theo, inject HttpService bằng constructor injection thông thường.

Gợi ý HttpModule và HttpService được import từ package @nestjs/axios.

@Injectable()
export class CatsService {
  constructor(private readonly httpService: HttpService) {}

  findAll(): Observable<AxiosResponse<Cat[]>> {
    return this.httpService.get('http://localhost:3000/cats');
  }
}
@Injectable()
@Dependencies(HttpService)
export class CatsService {
  constructor(httpService) {
    this.httpService = httpService;
  }

  findAll() {
    return this.httpService.get('http://localhost:3000/cats');
  }
}

Gợi ý AxiosResponse là một interface được export từ package axios ($ npm i axios).

Tất cả method của HttpService đều trả về một AxiosResponse được bọc (wrap) trong một object Observable.

Ghi chú người dịch Nếu bạn từng dùng axios trực tiếp trong Express hay Go/PHP (trả về Promise thẳng), đây là điểm khác biệt cần lưu ý: HttpService trả về Observable (kiểu RxJS) chứ không phải Promise. Muốn dùng như Promise thông thường (await), bạn phải convert qua firstValueFrom/lastValueFrom như ở phần “Full example” dưới đây. Đây không phải là API tùy tiện Nest bịa ra — RxJS được dùng nhất quán trong nhiều phần của Nest (ví dụ interceptor), nên HttpService theo cùng convention đó.

Cấu hình (Configuration)

Axios có thể được cấu hình với nhiều option khác nhau để tùy chỉnh hành vi của HttpService. Đọc thêm về các option này tại đây. Để cấu hình Axios instance bên dưới, truyền một object option (tùy chọn) vào method register() của HttpModule khi import nó. Object option này sẽ được truyền trực tiếp vào constructor của Axios bên dưới.

@Module({
  imports: [
    HttpModule.register({
      timeout: 5000,
      maxRedirects: 5,
    }),
  ],
  providers: [CatsService],
})
export class CatsModule {}

Cấu hình bất đồng bộ (Async configuration)

Khi bạn cần truyền module option một cách bất đồng bộ (asynchronously) thay vì tĩnh (statically), dùng method registerAsync(). Giống như hầu hết dynamic module khác, Nest cung cấp một số kỹ thuật để xử lý cấu hình bất đồng bộ.

Một kỹ thuật là dùng factory function:

HttpModule.registerAsync({
  useFactory: () => ({
    timeout: 5000,
    maxRedirects: 5,
  }),
});

Giống như các factory provider khác, factory function của chúng ta có thể là async và có thể inject dependency thông qua inject.

HttpModule.registerAsync({
  imports: [ConfigModule],
  useFactory: async (configService: ConfigService) => ({
    timeout: configService.get('HTTP_TIMEOUT'),
    maxRedirects: configService.get('HTTP_MAX_REDIRECTS'),
  }),
  inject: [ConfigService],
});

Ngoài ra, bạn có thể cấu hình HttpModule bằng một class thay vì factory, như bên dưới.

HttpModule.registerAsync({
  useClass: HttpConfigService,
});

Cấu trúc trên khởi tạo HttpConfigService bên trong HttpModule, dùng nó để tạo ra một object option. Chú ý rằng trong ví dụ này, HttpConfigService phải implement interface HttpModuleOptionsFactory, như bên dưới. HttpModule sẽ gọi method createHttpOptions() trên instance đã được khởi tạo của class được truyền vào.

@Injectable()
class HttpConfigService implements HttpModuleOptionsFactory {
  createHttpOptions(): HttpModuleOptions {
    return {
      timeout: 5000,
      maxRedirects: 5,
    };
  }
}

Nếu bạn muốn tái sử dụng một options provider đã có sẵn thay vì tạo một bản riêng bên trong HttpModule, dùng cú pháp useExisting.

HttpModule.registerAsync({
  imports: [ConfigModule],
  useExisting: HttpConfigService,
});

Bạn cũng có thể truyền cái gọi là extraProviders vào method registerAsync(). Các provider này sẽ được merge (gộp) với các provider của module.

HttpModule.registerAsync({
  imports: [ConfigModule],
  useClass: HttpConfigService,
  extraProviders: [MyAdditionalProvider],
});

Việc này hữu ích khi bạn muốn cung cấp thêm dependency cho factory function hoặc cho constructor của class.

Ghi chú người dịch Pattern registerAsync với useFactory/useClass/useExisting này lặp lại xuyên suốt các dynamic module trong Nest (ConfigModule, TypeOrmModule, JwtModule…), không riêng gì HttpModule. Nếu bạn hiểu rõ pattern này ở đây, bạn sẽ nhận ra nó ngay khi gặp ở module khác — về bản chất nó tương tự “service provider” pattern trong Laravel, nơi bạn định nghĩa cách một service được khởi tạo (có thể phụ thuộc config đọc bất đồng bộ) trước khi container build nó.

Dùng Axios trực tiếp (Using Axios directly)

Nếu bạn thấy các option của HttpModule.register không đủ cho nhu cầu, hoặc chỉ đơn giản là muốn truy cập trực tiếp Axios instance được tạo bởi @nestjs/axios, bạn có thể truy cập nó qua HttpService#axiosRef như sau:

@Injectable()
export class CatsService {
  constructor(private readonly httpService: HttpService) {}

  findAll(): Promise<AxiosResponse<Cat[]>> {
    return this.httpService.axiosRef.get('http://localhost:3000/cats');
    //                      ^ interface AxiosInstance
  }
}

Ví dụ đầy đủ (Full example)

Vì giá trị trả về của các method HttpService là một Observable, chúng ta có thể dùng rxjs - firstValueFrom hoặc lastValueFrom để lấy dữ liệu của request dưới dạng một promise.

import { catchError, firstValueFrom } from 'rxjs';

@Injectable()
export class CatsService {
  private readonly logger = new Logger(CatsService.name);
  constructor(private readonly httpService: HttpService) {}

  async findAll(): Promise<Cat[]> {
    const { data } = await firstValueFrom(
      this.httpService.get<Cat[]>('http://localhost:3000/cats').pipe(
        catchError((error: AxiosError) => {
          this.logger.error(error.response.data);
          throw 'An error happened!';
        }),
      ),
    );
    return data;
  }
}

Gợi ý Xem tài liệu của RxJS về firstValueFrom và lastValueFrom để biết sự khác nhau giữa hai method này.

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.