NestJS Configuration 完 整指南:從 .env 到進階配置管理
本文內容基於 NestJS v10 與 @nestjs/config v3 撰寫,並適用於目前最新版本的 NestJS。ConfigModule 的核心 API 在 v2 以後保持穩定,大多數概念在未來版本中應能持續適用。
在開發後端應用程式時,資料庫連線字串、第三方服務的 API Key,到不同環境的行為開關,這些敏感且多變的資訊不會被寫死在程式碼裡,因此環境變數管理是一個繞不開的課題。
NestJS 提供了 @nestjs/config 套件,讓環境變數的管理變得結構化且型別安全。這篇筆記會從最基礎的 .env 檔案觀念開始,逐步深入到命名空間配置、自動驗證與型別轉換等進階主題。
開始之前:理解 .env 與 process.env
在深入 NestJS 的 ConfigModule 之前,我們需要先釐清一個常被忽略的基礎觀念:Node.js 本身並不認識 .env 檔案。
Node.js 如何讀取環境變數?
Node.js 只會讀取作業系統層級的環境變數,也就是透過 shell 設定的變數。例如在終端機執行:
# 透過 shell 設定環境變數
export PORT=3000
export DATABASE_URL="postgres://localhost:5432/mydb"
# 啟動 Node.js 應用程 式
node app.js
這時在程式碼中就可以透過 process.env 這個全域物件來存取這些值:
console.log(process.env.PORT); // '3000'
console.log(process.env.DATABASE_URL); // 'postgres://localhost:5432/mydb'
但這種方式在實務上非常不便——每次換環境都要重新設定 shell 變數,而且這些設定容易遺失、難以版控。
為什麼需要 .env 檔案?
所以社群發展出 .env 檔案這種作法,把環境變數都寫在同一個檔案裡,方便管理:
PORT=3000
DATABASE_URL=postgres://localhost:5432/mydb
JWT_SECRET=my-super-secret-key
但這裡有個關鍵:.env 只是一個純文字檔案,Node.js 不會自動去讀它。要讓這些值進入 process.env,必須透過一個解析器(Parser)——最常見的就是 dotenv 套件。
// 傳統作法:手動使用 dotenv
import * as dotenv from 'dotenv';
dotenv.config(); // 讀取 .env 並寫入 process.env
console.log(process.env.PORT); // '3000'
ConfigModule 幫我們做了什麼?
NestJS 的 ConfigModule 在背後封裝了 dotenv,並且額外提供了許多便利功能。當我們呼叫 ConfigModule.forRoot() 時,它會完成以下步驟:
- 載入(Loading): 尋找專案根目錄下的
.env檔案 - 解析(Parsing): 把
.env裡的KEY=VALUE轉成 JavaScript 物件 - 寫入(Assigning): 將這些值賦予給
process.env - 封裝(Encapsulation): 把這些值存進
ConfigService,提供更安全的存取方式
雖然 ConfigModule 會把 .env 的值寫入 process.env,但比起直接在程式碼中使用 process.env,比較建議使用 ConfigService 來存取,原因包括:
- 型別安全:
process.env的值永遠是string | undefined,容易出錯 - 統一管理: 透過 ConfigService 可以搭配驗證、轉型、預設值等功能
- 更好的測試性: ConfigService 可以在測試時輕鬆 mock
如果沒有用 ConfigModule 呢?
那麼如果我不使用 ConfigModule,少了上面的載入、解析、寫入等步驟,還能讀取環境變數嗎?可以,但只能讀取作業系統層級的變數,無法讀取 .env 檔案。
// 沒有使用任何套件
console.log(process.env.PORT); // undefined(除非在 shell 中設定過)
這就是為什麼在 NestJS 專案中,ConfigModule 幾乎是必備的基礎設施之一。