🍕

NestJSのConfigModuleを型付きで安全に取得したい!

2022/05/22に公開

きっかけ

NestJSの環境変数の取得方法は、何も工夫しなければenv.processを使うか、↓のようにconfigServiceを使うかになってくると思います。

const dbUser = this.configService.get<string>('DATABASE_USER');

https://docs.nestjs.com/techniques/configuration

しかし、僕としてはタイポしたらアウトな文字列を使うのではなく、なんとかして型付きで安全に取得したい!というわけで色々考えてみました。

解決策

主なファイルはこの3つです。

  • config.interface.ts
  • config.module.ts
  • config.service.ts
  1. まず環境変数用のクラスを作成します。
    バリデーションもしたいと思ってるのでNestJSではお馴染みのclass-validatorを使ってます。
config.interface.ts
import { IsNotEmpty, IsString } from 'class-validator';

export class EnvClass {
  @IsNotEmpty()
  @IsString()
  DATABASE_URL!: string;
}
  1. ここが主な実装部分で、env.processで環境変数を取得し、plainToClassメソッドでEnvClassに変換します。
    その後バリデーションして、NGだったらエラー、OKだったら変数に代入します。
config.service.ts
import { Injectable } from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validateSync } from 'class-validator';
import { EnvClass } from './config.interface';

@Injectable()
export class AppConfigService {
  constructor() {
    this.env = validate(process.env);
  }

  public env!: EnvClass;
}

export function validate(config: Record<string, unknown>): EnvClass {
  const validatedConfig = plainToClass(EnvClass, config, {
    enableImplicitConversion: true,
  });

  if (config['NODE_ENV'] === 'test') {
    return validatedConfig;
  }

  const errors = validateSync(validatedConfig, {
    skipMissingProperties: false,
  });

  if (errors.length > 0) {
    throw new Error(errors.toString());
  }

  return validatedConfig;
}
  1. 最後に@Glocal()をつけてModule定義すれば終わりです!
config.module.ts
import { Global, Module } from '@nestjs/common';
import { AppConfigService } from './config.service';

@Global()
@Module({
  providers: [AppConfigService],
  exports: [AppConfigService],
})
export class AppConfigModule {}
  1. 使用する場合はAppModuleでimportし...
@Module({
  imports: [
    AppConfigModule,
  ],
})
export class AppModule {}
  1. こんな感じで使ってあげれば完璧です!
@Injectable()
export class PiyoService {
  constructor(private readonly configService: AppConfigService) {}

  find(): string {
    return this.configService.env.DATABASE_URL;
  }
}

感想

いろんな記事見てるうちに、ConfigModule使わなくてもいいんじゃないか?って思ってこの形になりました(*´-`)

ConfigModule使ったほうがいいよ!とかこの機能実現できないじゃん!とかあったらコメントください!

参考記事

バリデーションまわり
https://zenn.dev/waddy/articles/nestjs-configuration-service

AppConfigModuleまわり
https://zenn.dev/dove/articles/2990f0e1eba07e

Discussion