NestJSで環境変数をアプリケーションモジュールで使用する方法
問題
NestJSアプリケーションでデータベース接続情報を環境変数から読み込みたい場合、app.module.ts
ファイル内で.env
変数を直接使用しようとすると問題が発生します。元のコードではハードコーディングされた接続文字列が動作していましたが、環境変数を使用するように変更すると接続に失敗します。
元の動作していたコード:
@Module({
imports: [
MongooseModule.forRoot(`mongodb+srv://myusername:mypassword@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' }),
ProductModule,
CategoryModule,
],
})
環境変数を使用した非動作コード:
@Module({
imports: [
ConfigModule.forRoot({ envFilePath: `${process.env.NODE_ENV}.env` }),
MongooseModule.forRoot(`mongodb+srv://${ConfigModule.get('DB_USER')}:${ConfigModule.get('DB_PASS')}@myhost.net?retryWrites=true&w=majority&db=dbname`, { useNewUrlParser: true, dbName: 'dbname' }),
ProductModule,
CategoryModule,
],
})
解決策
方法1: ConfigModuleとforRootAsyncの使用(推奨)
最も一般的で推奨される方法は、ConfigModule
とデータベースモジュールの非同期設定(forRootAsync
)を使用する方法です。
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`
}),
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URI'),
}),
inject: [ConfigService],
}),
ProductModule,
CategoryModule,
],
})
export class AppModule {}
INFO
環境変数ファイルは通常、.env
という名前ですが、NODE_ENV
環境変数に基づいて異なるファイルを使用できます(例: .env.development
, .env.production
)。
方法2: データベース専用モジュールの作成
データベース接続を分離したモジュールを作成する方法です。
database.module.ts:
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
uri: configService.get<string>('MONGODB_URI'),
dbName: configService.get<string>('DB_NAME'),
}),
inject: [ConfigService],
}),
],
exports: [MongooseModule],
})
export class DatabaseModule {}
app.module.ts:
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { DatabaseModule } from './database/database.module';
import { ProductModule } from './product/product.module';
import { CategoryModule } from './category/category.module';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`
}),
DatabaseModule,
ProductModule,
CategoryModule,
],
})
export class AppModule {}
方法3: 環境変数の直接使用
シンプルなケースでは、環境変数を直接使用することもできます。
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
ConfigModule.forRoot(),
MongooseModule.forRootAsync({
useFactory: () => ({
uri: process.env.MONGODB_URI,
}),
}),
],
})
WARNING
この方法はシンプルですが、ConfigService
を使用した方法に比べて型安全性や設定の柔軟性が低くなります。
環境変数の設定
.env
ファイルの例:
NODE_ENV=development
MONGODB_URI=mongodb+srv://username:password@host.net/dbname?retryWrites=true&w=majority
DB_USER=username
DB_PASS=password
DB_HOST=host.net
DB_NAME=dbname
package.jsonのスクリプト設定
環境に応じて異なる環境変数ファイルを読み込むためのスクリプト設定:
{
"scripts": {
"start:dev": "cross-env NODE_ENV=development npm run start",
"start:prod": "cross-env NODE_ENV=production npm run start",
"start:local": "cross-env NODE_ENV=local npm run start"
}
}
TIP
cross-env
パッケージを使用すると、異なるOS間で環境変数の設定を統一できます。インストールにはnpm install -D cross-env
を実行してください。
トラブルシューティング
ConfigModuleが環境変数を読み込まない場合
ConfigModule.forRoot()
を複数回呼び出すことで解決する場合があります:
@Module({
imports: [
ConfigModule.forRoot(), // 最初に環境変数を読み込む
ConfigModule.forRoot({ // 設定を適用する
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`
}),
// 他のモジュール
],
})
環境変数がundefinedの場合
- 環境変数ファイルが正しい場所にあることを確認
- 環境変数名が正しいことを確認
NODE_ENV
が正しく設定されていることを確認
まとめ
NestJSで環境変数をアプリケーションモジュールで使用するには、ConfigModule
と非同期設定(forRootAsync
)を組み合わせる方法が最も効果的です。データベース接続を専用モジュールに分離することで、コードの保守性とテストの容易性を向上させることができます。
適切な環境変数管理は、アプリケーションのセキュリティと設定の柔軟性を確保する上で重要です。本番環境では、環境変数の管理にさらに注意を払い、機密情報がコードベースにハードコーディングされないようにしましょう。