Trong bài viết này chúng ta sẽ đi sâu về dependency Inject và custom provider trong NestJs, mục tiêu của bài viết này là:
-
Hiểu rõ cách hoạt động của Dependency Injection (DI) trong NestJS.
-
Biết cách tạo Custom Provider với useClass, useValue, useFactory.
-
Biết xử lý circular dependency với forwardRef.
1. Dependency Injection là gì?
Dependency Injection (DI) là một thiết kế giúp tách biệt việc tạo ra phụ thuộc (dependency) ra khỏi class sử dụng chúng. Nói có vẻ khó hiểu, ví dụ thay vì tự tạo instance của một class khác ở trong 1 service hay 1 class nào đó, chúng ta sẽ "tiêm" vào thông qua constructor, setter hoặc trong chính hàm đó, điều này giúp cho ứng dụng:
-
Dễ test
-
Dễ mở rộng
-
Dễ mock
Trong NestJS, hệ thống DI hoạt động dựa trên provider và container.
2. Provider là gì?
@Injectable()
export class UserService {
constructor(private readonly db: DatabaseService) {}
}
Ở đây, DatabaseService là dependency được inject vào từ DI container. Nest sẽ tự tìm DatabaseService và inject vào UserService.
Mặc định, Nest dùng useClass để map 1 token với 1 class.
3. Custom Provider trong NestJS
Nest cho phép tùy chỉnh cách khởi tạo provider bằng 3 cách chính:
3.1. useClass – class mapping đơn giản
const myProvider = {
provide: 'DatabaseService',
useClass: MysqlDatabaseService,
};
Khi inject 'DatabaseService', Nest sẽ khởi tạo MysqlDatabaseService.
3.2. useValue – inject 1 giá trị có sẵn
const myProvider = {
provide: 'API_KEY',
useValue: 'my-secret-key-123',
};
Inject vào
constructor(@Inject('API_KEY') private readonly apiKey: string) {}
3.3. useFactory – custom logic khi khởi tạo
const myProvider = {
provide: 'DatabaseConnection',
useFactory: async () => {
const conn = await createConnection(); // custom logic
return conn;
},
};
Inject
constructor(@Inject('DatabaseConnection') private readonly conn: any) {}
4. Xử lý Circular Dependency bằng forwardRef
Khi Service A cần Service B, và ngược lại Service B cũng cần A, bạn sẽ gặp lỗi:
Error: A circular dependency has been detected...
Cách xử lý là dùng forwardRef():
Ví dụ
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
@Inject(forwardRef(() => UserService))
private readonly userService: UserService,
) {}
}
// user.service.ts
@Injectable()
export class UserService {
constructor(
@Inject(forwardRef(() => AuthService))
private readonly authService: AuthService,
) {}
}
Và trong module
@Module({
providers: [UserService, AuthService],
imports: [forwardRef(() => AuthModule)],
exports: [UserService],
})
export class UserModule {}
5. Khi nào dùng cái gì?
Loại Provider |
Khi nào nên dùng |
---|---|
useClass |
Khi bạn có 1 class triển khai cụ thể |
useValue |
Khi bạn cần inject 1 giá trị tĩnh (ví dụ API_KEY) |
useFactory |
Khi bạn cần tạo object có logic phức tạp |
forwardRef |
Khi gặp circular dependency giữa services |
5. Kết luận
Hiểu rõ Dependency Injection và các loại Custom Provider là nền tảng quan trọng để phát triển ứng dụng NestJS theo kiến trúc linh hoạt, mở rộng và dễ test.
Dong Nguyen
BÌNH LUẬN
Địa chỉ email của bạn sẽ không được công khai.