1. プロバイダーとは何ですか?
プロバイダーは NestJS の中核となる概念です。任意のクラスがマークされます @Injectable() サービス、リポジトリ、ファクトリー、ヘルパーなど、すべてがプロバイダーになることができます。主なアイデア: プロバイダーは次のことができます。 注射する。注入する コンストラクターを通じて他のクラスに接続し、関係を作成します 疎結合。
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat): Cat {
this.cats.push(cat);
return cat;
}
findAll(): Cat[] {
return this.cats;
}
}
2. NestJS での依存性注入 (DI)
NestJS には、依存関係の作成と注入を自動的に管理する IoC (Inversion of Control) コンテナが組み込まれています。
@Controller('cats')
export class CatsController {
// NestJS IoC container tự động:
// 1. Tìm provider CatsService trong module
// 2. Tạo instance (hoặc lấy singleton có sẵn)
// 3. Inject vào constructor parameter
constructor(private readonly catsService: CatsService) {}
@Get()
findAll() {
return this.catsService.findAll();
}
}
DIフロー
1. @Module({ providers: [CatsService] })
→ NestJS đăng ký CatsService vào IoC container
2. constructor(private readonly catsService: CatsService)
→ NestJS nhận biết CatsController cần CatsService
3. NestJS resolve dependency:
→ Kiểm tra CatsService đã tồn tại trong container?
→ Có: inject instance đã có (singleton)
→ Không: tạo instance mới, lưu vào container, inject
3. カスタムプロバイダー
useClass — スワップ実装
// Interface
interface Logger {
log(message: string): void;
}
// Implementations
@Injectable()
class ConsoleLogger implements Logger {
log(message: string) { console.log(message); }
}
@Injectable()
class FileLogger implements Logger {
log(message: string) { /* write to file */ }
}
// Module — dễ dàng swap
@Module({
providers: [
{
provide: 'LOGGER',
useClass: process.env.NODE_ENV === 'production'
? FileLogger
: ConsoleLogger,
},
],
})
export class AppModule {}
// Inject bằng @Inject() token
@Injectable()
export class AppService {
constructor(@Inject('LOGGER') private logger: Logger) {}
}
useValue — 定数またはモックを挿入する
// Inject config values
const DATABASE_CONFIG = {
host: 'localhost',
port: 5432,
database: 'mydb',
};
@Module({
providers: [
{ provide: 'DATABASE_CONFIG', useValue: DATABASE_CONFIG },
{ provide: 'API_KEY', useValue: 'sk-xxx-12345' },
],
})
export class AppModule {}
// Inject mock cho testing
const mockCatsService = {
findAll: () => [{ name: 'Test Cat' }],
create: (cat) => cat,
};
// Trong test
Test.createTestingModule({
providers: [
{ provide: CatsService, useValue: mockCatsService },
],
});
useFactory — 複雑なロジック、非同期
@Module({
providers: [
ConfigService,
{
provide: 'DATABASE_CONNECTION',
useFactory: async (configService: ConfigService) => {
const options = configService.get('database');
const connection = await createConnection(options);
return connection;
},
inject: [ConfigService], // Dependencies cho factory
},
],
})
export class DatabaseModule {}
// Factory với multiple dependencies
{
provide: 'MAILER',
useFactory: (config: ConfigService, logger: LoggerService) => {
return new MailerService(
config.get('SMTP_HOST'),
config.get('SMTP_PORT'),
logger,
);
},
inject: [ConfigService, LoggerService],
}
useExisting — エイリアスプロバイダー
@Module({
providers: [
CatsService,
// 'AliasedCatsService' trỏ tới cùng instance CatsService
{ provide: 'AliasedCatsService', useExisting: CatsService },
],
})
export class CatsModule {}
4. インジェクションスコープ
import { Injectable, Scope } from '@nestjs/common';
// DEFAULT — Singleton (một instance cho toàn app)
@Injectable({ scope: Scope.DEFAULT })
export class SingletonService {}
// REQUEST — Mỗi request tạo instance mới
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {
constructor(@Inject(REQUEST) private request: Request) {
// Truy cập request object
}
}
// TRANSIENT — Mỗi lần inject tạo instance mới
@Injectable({ scope: Scope.TRANSIENT })
export class TransientService {}
重要な注意事項: スコープは 泡立つ。コントローラーが REQUEST スコープのサービスを挿入すると、コントローラーも REQUEST スコープになります。
| 範囲 | インスタンス | ユースケース |
|---|---|---|
| デフォルト | アプリ全体の場合は 1 | ほとんどのサービス (90% 以上) |
| リクエスト | リクエストごとに 1 つ | マルチテナント、リクエストコンテキスト |
| 過渡現象 | 注入するたびに新しい | ステートフルヘルパー、ロガー |
5. オプションの依存関係
import { Optional, Inject } from '@nestjs/common';
@Injectable()
export class HttpService {
constructor(
@Optional() @Inject('HTTP_OPTIONS') private options?: HttpOptions,
) {
// Nếu HTTP_OPTIONS không được provide, options = undefined
this.options = options ?? { timeout: 5000 };
}
}
6. 実践例:オーダーサービス
// order.service.ts
@Injectable()
export class OrderService {
constructor(
private readonly productService: ProductService,
private readonly paymentService: PaymentService,
private readonly notificationService: NotificationService,
@Inject('LOGGER') private readonly logger: Logger,
) {}
async createOrder(dto: CreateOrderDto): Promise<Order> {
// 1. Kiểm tra sản phẩm
const product = await this.productService.findOne(dto.productId);
if (!product) throw new NotFoundException('Product not found');
// 2. Xử lý thanh toán
const payment = await this.paymentService.charge(dto.amount);
// 3. Tạo order
const order = { id: Date.now(), ...dto, paymentId: payment.id };
// 4. Gửi notification
await this.notificationService.sendOrderConfirmation(order);
this.logger.log(`Order ${order.id} created`);
return order;
}
}
7. まとめ
- プロバイダー: 任意のクラス
@Injectable()誰もがプロバイダーです - DIコンテナ: NestJS は依存関係を自動的に解決して挿入します。
- カスタムプロバイダー: useClass、useValue、useFactory、useExisting
- スコープ: デフォルト (シングルトン)、リクエスト、トランジェント
- 常に優先順位を付ける コンストラクターインジェクション テスト容易性のために
次の記事で詳しく説明します モジュール — NestJS の機能ごとにコードを整理する方法。