詳解Nest.Js config 模組

前言

大約在2020年左右,Nest.Js官方推出了nestjs/config模組
早期的官方文章僅是很單純的自建config Module、ConfigService
直接在constructor用fs.readFileSync讀env檔,然後加個get function供其他模組使用

新推出的nestjs/config模組,完成度就很高了
讓參數檔可依模組劃分成獨立檔案,散落在各模組底下
可以直接注入單一參數檔來取得參數。甚至為了開發便利,注入的參數檔也能有語法糖帶出參數名
讓ide來除錯,大大降低了人為輸入錯誤的可能性

不過官方寫的相當粗淺,有點難以理解該如何使用…
僅就分享自己研究出來的使用方式

重點摘要

  1. 使用forRoot()forFeature()來載入參數檔
  2. 與TypeORM 或其他常見Module不同,forFeature()載入的參數,不需要在forRoot()全部填上
  3. forRoot()僅需要載入AppModule自己需要用到的就好
  4. 以載入參數來使用這件事forRoot()forFeature()使用體感是同樣的。當然底層forRoot()還有其他用途(如cache),但就使用參數這件事較無感
  5. 官方文件是在forRoot()傳入validationSchema來驗證參數。但沒有提到若多個參數檔該如何寫…看不太懂這塊該怎麼寫。若期望參數by模組畫分的話,驗證這塊個人偏好就直接寫在registerAs裡了
  6. 若希望採用官方寫法@Inject(databaseConfig.KEY),在開發時可以直接.來取得參數,在registerAs裡時會寫的比較醜一點,因讀檔時,當下不會知道到底讀到了什麼內容,除非自己又再加訂interface規範。但就有會有多餘…算是可妥協的小缺點
  7. 若要讀取參數實體檔(如.yaml),官方有提到nest-cli.json須增加compilerOptions#assets將靜態檔案拷貝至dist底下,但官方範例又簡單到僅有單一目錄…。若參數檔想要by module置放的話,直接抄官方寫法會失敗。解決方式如後文參考

nest-cli.json增加靜態參數

yaml檔是一個很方便的格式,推薦大家使用此格式做為實體參數檔
官方亦有範例,不在贅述
打開nest-cli.json,加入下列參數

"compilerOptions": {
    "assets": [{"include": "**/*.yaml", "outDir": "./dist/src" }]
  }

之後開發不論將.yaml放在哪裡,打包後、開發期的runtime都會自動將yaml檔放置對應位置

若副檔名習慣用.yml,上述片段記得改成.yml

使用registorAs注入config

依照官方教學,會是直接回傳yaml.load()

const YAML_CONFIG_FILENAME = `my-config.yaml`;
export default registerAs('myConfig', () => { 
 return yaml.load(
     readFileSync(join(__dirname, YAML\_CONFIG\_FILENAME), 'utf8'),
    ) as Record<String, any>
});

但這樣做的缺點就是,無法使用官方推薦的@Inject(myConfig.KEY)寫法,享有開發期的ConfigType保護

constructor(
  @Inject(myConfig.KEY)
  private myConfig: ConfigType<typeof myConfig>,
) {}

若想要同時保有讀.yaml檔 及 ConfigType保護,則使用下列寫法

const YAML_CONFIG_FILENAME = `my-config.yaml`;

export default registerAs('myConfig', () => { 
    const yamlConfig = yaml.load(
        // readFileSync(resolve(`config`, YAML_CONFIG_FILENAME), 'utf8'),  // 若希望是放根目錄的config下,可以使用這個寫法
        readFileSync(join(__dirname, YAML_CONFIG_FILENAME), 'utf8'),
      );
      return {
        myConfig1: yamlConfig['myConfig1'],
        myConfig2: yamlConfig['myConfig2'],
      }
});

若有需要,亦可於此比照官方教學,增加Joi等相關驗證功能

雖然仍有缺點

  1. return的物件仍沒有型別保護
  2. 參數異動時,要記得來這裡調整return物件

但於service注入使用時,可以很舒服的用.來取參數,也大大降低了拼錯的風險

參考資料

Nest.Js 官方文件 / configuration
stack overflow / NestJS copy assets files