Dev Study
← 解説「環境変数と ConfigModule」に戻る

サンプルコードで身につける: 環境変数と ConfigModule

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1forRoot の最小設定

ConfigModule を有効化する最小の設定です。forRoot() が .env を読み込み、isGlobal: true でどのモジュールからも ConfigService を注入できるようになる、という導入の2点セットを示します。

import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";

// .env ファイル(プロジェクト直下、Git にはコミットしない):
// PORT=3000
// DATABASE_URL=postgres://localhost:5432/app

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }), // .env を読み込む
  ],
})
export class AppModule {}
// isGlobal: true で、どのモジュールからも ConfigService を使える

2ConfigService で値を読む

サービスに ConfigService を注入し、API キーや送信元アドレスを取り出す基本形です。秘密情報や環境依存の値をコードに直書きせず、すべて config.get() 経由にするのが実務の大原則です。

import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";

@Injectable()
export class MailService {
  constructor(private readonly config: ConfigService) {}

  send(to: string) {
    // API キーや送信元は直書きせず環境変数から取り出す
    const apiKey = this.config.get<string>("MAIL_API_KEY");
    const sender = this.config.get<string>("MAIL_FROM");
    console.log(sender + " から " + to + " へ送信準備 (key設定: " +
      (apiKey !== undefined) + ")");
  }
}

3既定値と数値への変換

get() の第2引数で「未設定のときの既定値」を渡し、文字列から数値へ自分で変換する例です。ローカルでは既定値で動かし本番だけ環境変数で上書きする、という実務の運用パターンがこの形に集約されます。

import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";

@Injectable()
export class CacheService {
  constructor(private readonly config: ConfigService) {}

  ttlSeconds(): number {
    // 第2引数は「未設定のときの既定値」
    const raw = this.config.get<string>("CACHE_TTL", "60");
    // 環境変数は常に文字列なので、数値が欲しければ自分で変換する
    return parseInt(raw, 10);
  }
}
// ローカル: .env に書かず既定値の 60 で動かす
// 本番:    CACHE_TTL=300 を設定して上書きする

4「環境変数は文字列」を確かめる

環境変数がすべて文字列であることの落とし穴をプレーン TypeScript で再現した例です。文字列のまま計算すると静かに壊れること、変換とフォールバックを関数にまとめると安全なことを実行して確かめられます。

TypeScript

5環境ごとの .env 切り替えと必須チェック

NODE_ENV に応じて読み込む .env ファイルを切り替え、必須キーの欠落を起動時に検出する実務設定です。設定ミスを「最初のリクエストが来てから」ではなく「デプロイ直後の起動時」に気づけるのが大きな利点です。

import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      // NODE_ENV=production なら .env.production を読む
      envFilePath: ".env." + (process.env.NODE_ENV ?? "development"),
      validate: (env) => {
        // 必須キーが欠けていたら「起動時に」落とす
        if (!env.DATABASE_URL) {
          throw new Error("DATABASE_URL が設定されていません");
        }
        return env;
      },
    }),
  ],
})
export class AppModule {}