🍕
NestJSのConfigModuleを型付きで安全に取得したい!
きっかけ
NestJSの環境変数の取得方法は、何も工夫しなければenv.process
を使うか、↓のようにconfigServiceを使うかになってくると思います。
const dbUser = this.configService.get<string>('DATABASE_USER');
しかし、僕としてはタイポしたらアウトな文字列を使うのではなく、なんとかして型付きで安全に取得したい!というわけで色々考えてみました。
解決策
主なファイルはこの3つです。
- config.interface.ts
- config.module.ts
- config.service.ts
- まず環境変数用のクラスを作成します。
バリデーションもしたいと思ってるのでNestJSではお馴染みのclass-validator
を使ってます。
config.interface.ts
import { IsNotEmpty, IsString } from 'class-validator';
export class EnvClass {
@IsNotEmpty()
@IsString()
DATABASE_URL!: string;
}
- ここが主な実装部分で、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;
}
- 最後に
@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 {}
- 使用する場合はAppModuleでimportし...
@Module({
imports: [
AppConfigModule,
],
})
export class AppModule {}
- こんな感じで使ってあげれば完璧です!
@Injectable()
export class PiyoService {
constructor(private readonly configService: AppConfigService) {}
find(): string {
return this.configService.env.DATABASE_URL;
}
}
感想
いろんな記事見てるうちに、ConfigModule
使わなくてもいいんじゃないか?って思ってこの形になりました(*´-`)
ConfigModule
使ったほうがいいよ!とかこの機能実現できないじゃん!とかあったらコメントください!
参考記事
バリデーションまわり
AppConfigModuleまわり
Discussion