← 解説「モジュール間の共有 — exports と imports」に戻る
サンプルコードで身につける: モジュール間の共有 — exports と imports
解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。
1exports の最小例
providers と exports の違いを示す最小例です。providers は「このモジュールの持ち物」、exports は「外に貸し出してよい物」のリストであり、両方に載せて初めて他モジュールから使えます。
import { Module } from "@nestjs/common";
import { UsersService } from "./users.service";
@Module({
providers: [UsersService], // 登録: モジュール内で使える
exports: [UsersService], // 公開: 外のモジュールからも使える
})
export class UsersModule {}
// providers = 持ち物リスト / exports = 貸し出してよい物リスト
// exports に載せない限り、外のモジュールには見えない
2利用する側の imports
公開されたサービスを別モジュールから使う側のコードです。サービス単体ではなくモジュールごと imports で取り込む、という NestJS 特有のルールを「注文がユーザー情報を参照する」典型場面で示します。
import { Injectable, Module } from "@nestjs/common";
import { UsersModule } from "../users/users.module";
import { UsersService } from "../users/users.service";
@Injectable()
export class OrdersService {
// UsersModule が export しているので注入できる
constructor(private readonly usersService: UsersService) {}
createOrder(userId: number) {
// 注文時にユーザー情報を参照する、という機能間連携
return { userId };
}
}
@Module({
imports: [UsersModule], // サービス単体ではなくモジュールごと取り込む
providers: [OrdersService],
})
export class OrdersModule {}
3公開しない内部サービス
exports に何を載せないかという設計判断の例です。パスワードハッシュ化のような内部実装は providers だけに留めることで、他の機能が内部実装へ依存する事故を防げます。exports はモジュールの公開 API そのものです。
import { Module } from "@nestjs/common";
import { UsersService } from "./users.service";
import { PasswordHashService } from "./password-hash.service";
@Module({
// PasswordHashService は users 機能の内部実装
providers: [UsersService, PasswordHashService],
exports: [UsersService], // 外に見せるのは UsersService だけ
})
export class UsersModule {}
// exports を絞る = 「他機能はここからだけ触ってよい」という宣言
// 内部実装を後から自由に作り変えられる余地を残せる
4つながらないときの3点チェック
モジュールをまたぐ注入が解決できないときの実務チェックリストです。提供側の providers・exports、利用側の imports の3点がつながって初めて成立するため、エラーが出たらこの順で確認します。
// OrdersService に UsersService を注入しようとして
// Nest can't resolve dependencies of the OrdersService (?)
// と言われたときの確認手順:
//
// ① 提供側 UsersModule の providers に UsersService があるか
// ② 提供側 UsersModule の exports に UsersService があるか ← 最頻出
// ③ 利用側 OrdersModule の imports に UsersModule があるか
import { Module } from "@nestjs/common";
import { UsersService } from "./users.service";
@Module({
providers: [UsersService], // ①
exports: [UsersService], // ② ここの足し忘れが一番多い
})
export class UsersModule {}
5共有モジュールを切り出す
多くの機能から使われるロガーを専用モジュールに切り出した実務例です。横断的な部品は「providers と exports に載せた小さなモジュール」として独立させ、使いたい側が imports する形が共有の定番パターンです。
import { Injectable, Module } from "@nestjs/common";
@Injectable()
export class AppLoggerService {
log(context: string, message: string): void {
console.log("[" + context + "] " + message);
}
}
// 多くの機能から使われる横断部品は、専用モジュールに切り出す
@Module({
providers: [AppLoggerService],
exports: [AppLoggerService],
})
export class LoggerModule {}
// 利用側は imports: [LoggerModule] と書くだけで
// どの機能からも AppLoggerService を注入できるようになる