Trong NestJS, Custom Providers đóng vai trò quan trọng trong việc quản lý và cung cấp các dependency một cách linh hoạt, cho phép lập trình viên tùy chỉnh và mở rộng ứng dụng một cách hiệu quả. Bài viết này sẽ đi sâu vào các khái niệm liên quan đến Custom Providers, cung cấp những ví dụ cụ thể và hướng dẫn cách sử dụng.
1. Custom Providers là gì?
Custom Providers là một cơ chế trong NestJS cho phép bạn xác định cách mà các dependency được tạo ra và quản lý. Điều này giúp lập trình viên có thể thay đổi logic mặc định của Dependency Injection (DI) để phục vụ cho nhu cầu cụ thể của ứng dụng.
2. Các kiểu Custom Providers
Dưới đây là bốn dạng Custom Providers phổ biến trong NestJS:
2.1. Sử dụng Class (Use Class)
Mô tả:
Bạn có thể định nghĩa một provider bằng cách liên kết một token cụ thể đến một class.
Ví dụ:
typescript
// logger.service.ts
export class LoggerService {
log(message: string) {
console.log('Log:', message);
}
}\n
// app.module.ts
import { Module } from '@nestjs/common';
import { LoggerService } from './logger.service';
@Module({
providers: [
{
provide: 'Logger',
useClass: LoggerService,
},
],
exports: ['Logger'],
})
export class AppModule {}
Sử dụng:
typescript
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
constructor(@Inject('Logger') private readonly logger: LoggerService) {}
logMessage() {
this.logger.log('Hello, Custom Provider!');
}
}
Giải thích:
Tại đây, token 'Logger'
được ánh xạ đến class LoggerService
. Khi inject 'Logger'
, NestJS sẽ cung cấp một instance của LoggerService
.
2.2. Sử dụng Giá Trị (Use Value)
Mô tả:
Bạn có thể cung cấp một giá trị cụ thể thay vì ánh xạ đến một class.
Ví dụ:
typescript
@Module({
providers: [
{
provide: 'CONFIG',
useValue: { appName: 'MyApp', version: '1.0.0' },
},
],
})
export class AppModule {}
Sử dụng:
typescript
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
constructor(@Inject('CONFIG') private readonly config: any) {}
getConfig() {
return this.config;
}
}
Giải thích:
Token 'CONFIG'
ánh xạ đến một object chứa cấu hình, và khi inject token này, bạn sẽ nhận được giá trị đã chỉ định.
2.3. Sử dụng Factory (Use Factory)
Mô tả:
Bạn có thể tạo provider bằng cách sử dụng một hàm factory function, có thể nhận các dependency khác.
Ví dụ:
typescript
@Module({
providers: [
{
provide: 'DATABASE_CONNECTION',
useFactory: async () => {
const connection = await createConnection();
return connection;
},
},
],
})
export class AppModule {}
Sử dụng:
typescript
import { Inject, Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
constructor(@Inject('DATABASE_CONNECTION') private readonly db: any) {}
getDbStatus() {
return this.db.status;
}
}
Giải thích:
Factory function trong ví dụ này cung cấp logic để tạo một instance kết nối đến database, và token 'DATABASE_CONNECTION'
sẽ nhận được instance này.
2.4. Sử dụng Provider có Sẵn (Use Existing)
Mô tả:
Bạn có thể ánh xạ một token đến một provider đã được định nghĩa trước đó.
Ví dụ:
typescript
@Module({
providers: [
LoggerService,
{
provide: 'AppLogger',
useExisting: LoggerService,
},
],
})
export class AppModule {}
Sử dụng:
typescript
@Injectable()
export class AppService {
constructor(@Inject('AppLogger') private readonly logger: LoggerService) {}
logMessage() {
this.logger.log('Using existing provider!');
}
}
Giải thích:
AppLogger
sẽ tái sử dụng cùng một instance với LoggerService
, giúp bạn tiết kiệm tài nguyên và bảo trì code dễ dàng hơn.
3. Lợi ích của Custom Providers
- Linh hoạt:
Cho phép tùy chỉnh cách các dependency được cung cấp trong ứng dụng. - Tái sử dụng:
Có khả năng chia sẻ logic giữa nhiều module mà không cần phải viết lại. - Khả năng kiểm tra (Testability):
Giúp dễ dàng mock các dependency khi thực hiện kiểm thử.
4. Một số câu hỏi thường gặp
-
Phân biệt giữa useValue, useFactory và useExisting:
useValue
thường dùng cho các giá trị tĩnh (như cấu hình).useFactory
cho phép tạo dependency với logic tùy chỉnh và có thể nhận parameters.useExisting
tái sử dụng các provider đã được định nghĩa trước đó.
-
Cú pháp của useFactory là gì?
Dạng cấu trúc củauseFactory
thường bao gồm một hàm trả về giá trị hoặc instance mà bạn muốn cung cấp. -
Từ khóa provide và inject có ý nghĩa gì?
provide
xác định tên token mà bạn sử dụng để ánh xạ đến một provider.inject
cho phép bạn lấy các dependency đã được định nghĩa thông qua token.
Tóm tắt
- useClass: Phù hợp khi cần class cho chiến lược logic phức tạp.
- useValue: Thích hợp cho các giá trị tĩnh như cấu hình ứng dụng.
- useFactory: Tốt nhất cho logic tùy chỉnh, đặc biệt là khi cần khởi tạo hoặc xử lý trước khi cung cấp.
- useExisting: Giúp tiết kiệm tài nguyên và tổ chức mã nguồn một cách hiệu quả.
Bạn có thể kết hợp các loại provider này để tạo ra các ứng dụng NestJS linh hoạt và dễ mở rộng hơn.
Liên hệ thêm thông tin
Tham khảo thêm tại các nguồn tài liệu chính thức và blog để có cái nhìn sâu hơn về các Custom Providers trong NestJS:
#nestjs #nodejs #backend
source: viblo