ADR 011: 環境変数アクセスパターン
- Status: Proposed
- Date: 2026-04-18
- Deciders: Lead Engineer
Context
Section titled “Context”バックエンド (apps/api/) で環境変数を読み取る際、TypeScript の型システムが
process.env[key] を string | undefined と扱うため、実用上は以下のいずれかの
回避策が必要になる:
// A. ?? "" フォールバック(実際には空文字列が渡る可能性を示唆してしまう)secretKey: process.env["CLERK_SECRET_KEY"] ?? "",
// B. ! アサーション(ESLint no-non-null-assertion を抑制するコメントが必要)// eslint-disable-next-line @typescript-eslint/no-non-null-assertionsecretKey: process.env["CLERK_SECRET_KEY"]!,現状は index.ts の起動時チェックで必須変数の存在を保証しているが、
ミドルウェアや DI コンテナが process.env を直接読むため、
チェックが通った後でも型上は string | undefined のままとなり
コードの意図が伝わりにくい。
Decision
Section titled “Decision”起動時バリデーションを Zod スキーマに統一し、検証済みの型付き env オブジェクトを
アプリケーション全体で利用する。
import { z } from "zod";
const envSchema = z.object({ DATABASE_URL: z.string().min(1), CLERK_SECRET_KEY: z.string().min(1), CLERK_PUBLISHABLE_KEY: z.string().min(1), PORT: z.coerce.number().default(8080),});
export const env = envSchema.parse(process.env);import { env } from "./env.js";const db = createDb(env.DATABASE_URL);import { env } from "../env.js";const client = createClerkClient({ secretKey: env.CLERK_SECRET_KEY, // string — ! も ?? も不要 publishableKey: env.CLERK_PUBLISHABLE_KEY,});バリデーション失敗時は ZodError が起動時にスローされ、
不足・不正な環境変数の一覧が明示される。
Alternatives Considered
Section titled “Alternatives Considered”-
?? ""フォールバック(現状): コード上は「空文字列でも動く」と読めてしまい、実際のセマンティクス(起動時に 必須チェック済み)と乖離する。低コストだが意図が伝わりにくいため将来的に除去する -
!アサーション: 意図は明確だが ESLintno-non-null-assertionルールを抑制するコメントが必要に なり、コードが冗長になる。変数が増えるたびに同じコメントが増殖するため却下 -
requireEnv(key)ヘルパー関数: 呼び出し側にエラーが移るためシンプルだが、起動時ではなくミドルウェア初回呼び出し時に エラーが発覚する点が Zod スキーマ案に劣後する。単純な代替としては有効 -
ミドルウェアに config を引数で渡す: ミドルウェアが
process.envを直接参照しなくなり、テスト容易性は最も高い。 ただしcreateAppなどのシグネチャ変更が波及するためリファクタコストが大きく、 Zod スキーマ案と組み合わせて段階的に導入するのが現実的
Consequences
Section titled “Consequences”Positive
Section titled “Positive”- 環境変数の型が
string(undefinedなし)となり!や?? ""が不要 - バリデーション失敗時に欠損変数名が一覧表示され、デプロイ時の問題特定が速い
- 環境変数の定義・バリデーション・デフォルト値が
env.tsに集約される - Zod の
.default()/.coerce/.url()等で制約を宣言的に記述できる
Negative
Section titled “Negative”zodの import がapps/api/に追加される(すでに依存済みのため実質コストなし)process.envを直接読んでいる既存箇所をenv.ts経由に置き換える作業が発生する
apps/api/src/env.tsを新規作成し、index.tsのrequiredEnvVarsループを削除middleware/auth.ts/di/container.ts/index.tsのprocess.env参照をenv.*に置き換える- テスト時は
env.tsをモックするか、テスト用の環境変数を.env.testで管理する