Next.jsのCI/CDビルドで「DATABASE_URL is not defined」エラーが発生する原因と解決策
Published on 2026年1月2日
Next.jsはじめに
Next.js + GitHub Actionsの環境でCI/CDパイプラインを構築した際、ローカルでは問題なくビルドできるのに、CI/CD環境では以下のエラーが発生しました。
Error: DATABASE_URL is not defined
at .next/server/app/api/[[...route]]/route.js
GitHub ActionsのRepository VariablesにはDATABASE_URLを設定済みなのに、なぜ?
最初の仮説環境変数の設定漏れ?
最初に疑ったのは、GitHub Actionsへの環境変数の設定漏れでした。
しかし、Repository Variablesには確かにDATABASE_URLが設定されています。
ここで重要な事実Repository Variablesは自動的にprocess.envに入らない
ワークフロー内で明示的に設定が必要です。
jobs:
build:
env:
DATABASE_URL: ${{ vars.DATABASE_URL }}
しかし、これは対症療法でした。本当の問題は別にありました。
真の原因Next.jsのビルド時モジュール評価
エラーログをよく見ると、.next/server/app/api/[[...route]]/route.jsでエラーが発生しています。ビルド後のAPIルートです。
問題のコードを見てみましょう。
// infrastructure/database/index.ts
const databaseUrl = process.env.DATABASE_URL!;
export const db = createDatabaseClient(databaseUrl);
// api/users/create/index.ts
import { db } from '../infrastructure/database';
// ...
const userRepository = createUserRepository(db);
何が起きているか
Next.js ビルド時
↓
route.ts がインポートされる
↓
users/create/index.ts がインポートされる
↓
import { db } from ".../infrastructure/database"
↓
モジュールのトップレベルが即座に評価される
↓
process.env.DATABASE_URL が undefined
↓
エラー発生
Next.jsはビルド時にモジュールのトップレベルを評価します。
ローカルでは.envファイルがあるので問題ありませんが、CI/CD環境では.envはgitignoreされているため存在しません。
問題の本質を理解する
ここで考えるべきは
- DBクライアントが必要なタイミング: APIが実行される時(実行時)
- DBクライアントが不要なタイミング: ビルド時
APIルートのコードはビルド時には実行されません。にもかかわらず、トップレベルで初期化しているため、ビルド時に評価されてしまっています。
解決策遅延初期化パターン
「本当に必要なときだけ初期化する」パターンを採用します。
Before(問題のあるコード)
// モジュール読み込み時に即座に実行される
const databaseUrl = process.env.DATABASE_URL!;
export const db = createDatabaseClient(databaseUrl);
After(遅延初期化)
import { createDatabaseClient, DatabaseClient } from '...';
let _db: DatabaseClient | null = null;
export function getDb(): DatabaseClient {
if (!_db) {
const databaseUrl = process.env.DATABASE_URL;
if (!databaseUrl) {
throw new Error('DATABASE_URL is not defined');
}
_db = createDatabaseClient(databaseUrl);
}
return _db;
}
なぜこれで解決するか
| タイミング | Before | After |
|---|---|---|
| ビルド時 | createDatabaseClient(undefined)が実行される | 関数定義のみ、中身は実行されない |
| API実行時 | 正常動作 | getDb()が呼ばれて初期化される |
関数の中に入れることで、実際に使われるまで実行を遅延させています。
これは公式に推奨されているのか?
Next.js公式ドキュメントには直接的な記載はありませんが、この問題は広く知られており、多くのGitHub Discussionで議論されています。
- https://github.com/vercel/next.js/discussions/38164
- https://github.com/vercel/next.js/discussions/44628
今後の実装で気をつけること
環境変数に依存するクライアントをNext.jsで実装する際は、遅延初期化パターンを使用しましょう。
対象となるクライアント
- Database(PostgreSQL、MySQL等)
- Auth0 / 認証サービス
- Redis / キャッシュサービス
- S3 / ストレージサービス
- Stripe / 決済サービス
- その他、環境変数でAPIキー・接続情報を設定するもの
テンプレート
// infrastructure/xxx/index.ts
import { XxxClient } from 'xxx-sdk';
let _client: XxxClient | null = null;
export function getXxxClient(): XxxClient {
if (!_client) {
const apiKey = process.env.XXX_API_KEY;
if (!apiKey) {
throw new Error('XXX_API_KEY is not defined');
}
_client = new XxxClient({ apiKey });
}
return _client;
}
まとめ
| ポイント | 内容 |
|---|---|
| 問題 | Next.jsのビルド時にモジュールのトップレベルが評価される |
| 原因 | 環境変数に依存するクライアントをトップレベルで初期化していた |
| 解決策 | 遅延初期化パターンで、API実行時にのみ初期化する |
| 本質 | 「ビルド時にはDBに接続しないのだから、ビルド時に初期化する必要はない」 |