Skip to content
Go back

dotenv-safe を試してみた - .env.example で環境変数の設定漏れを防ぐ

はと🐤テック

Disclaimer: This article was written by AI and may contain inaccuracies or errors.

Table of contents

Open Table of contents

はじめに

Node.js アプリケーションで環境変数を管理する際、dotenv は広く使われているライブラリです。しかし、dotenv 単体では「必要な環境変数が本当に全て設定されているか」をチェックする機能はありません。

アプリケーションが起動してから「あれ、この環境変数が足りない」と気づくよりも、起動時に確実にチェックできたら安心です。

そんなニーズに応えてくれるのが dotenv-safe です。このライブラリは dotenv と同じように動作しますが、.env.example ファイルに定義された必要な環境変数が全て揃っているかを自動的に検証してくれます。

今回、実際に動かして試してみたので、その体験をレポートします。

dotenv-safe とは

dotenv-safe は、dotenv と同じインターフェースを持ちながら、環境変数の存在チェック機能を追加したライブラリです。

主な特徴

.env.example はバージョン管理にコミットし、実際の .env.gitignore に含める、という運用が想定されています。

セットアップ

dotenv-safe は dotenv の peer dependency として定義されているため、両方をインストールする必要があります。

npm install dotenv dotenv-safe

または

pnpm install dotenv dotenv-safe

使い方は dotenv とほぼ同じです:

require("dotenv-safe").config();

ES Modules の場合:

import { config } from "dotenv-safe";
config();

実際に試してみる

実際にどのように動作するか確かめるため、デモアプリケーションを作成して試してみました。

1. .env.example を用意

まず、必要な環境変数を定義する .env.example を作成します。

# .env.example - Template for required environment variables
# This file should be committed to version control

# Application settings
APP_NAME=
PORT=

# Database configuration
DATABASE_HOST=
DATABASE_PORT=
DATABASE_USER=
DATABASE_PASSWORD=

# API keys
API_KEY=
SECRET_TOKEN=

# Optional feature flags
ENABLE_LOGGING=

このファイルはバージョン管理にコミットします。値は空でも構いません。重要なのは「この変数が必要である」という宣言です。

2. .env ファイルを作成

実際の環境変数を含む .env ファイルを作成します(このファイルは .gitignore に含めます)。

# .env - Actual environment variables (should NOT be committed)

APP_NAME=MyDemoApp
PORT=3000

DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_USER=devuser
DATABASE_PASSWORD=secret123

API_KEY=my-api-key-12345
SECRET_TOKEN=super-secret-token

ENABLE_LOGGING=true

3. アプリケーションを作成

シンプルなテストアプリケーションを作成しました。

// index.js
try {
  const result = require("dotenv-safe").config();

  console.log("✓ Environment variables loaded successfully!");
  console.log("Parsed from .env:", result.parsed);
  console.log("Required by .env.example:", result.required);
} catch (error) {
  console.error("✗ Error loading environment variables!");
  console.error(error.message);
  console.error("Missing variables:", error.missing);
  process.exit(1);
}

4. 正常なケースを実行

全ての環境変数が揃っている状態で実行してみます。

$ node index.js
✓ Environment variables loaded successfully!

Parsed from .env:
{
  "APP_NAME": "MyDemoApp",
  "PORT": "3000",
  "DATABASE_HOST": "localhost",
  "DATABASE_PORT": "5432",
  "DATABASE_USER": "devuser",
  "DATABASE_PASSWORD": "secret123",
  "API_KEY": "my-api-key-12345",
  "SECRET_TOKEN": "super-secret-token",
  "ENABLE_LOGGING": "true"
}

Required by .env.example:
{
  "APP_NAME": "MyDemoApp",
  "PORT": "3000",
  // ... (同じ内容)
}

問題なく起動しました。result.parsed には .env から読み込んだ値が、result.required には .env.example で要求された変数の現在の値が含まれています。

5. エラーケース - 必要な変数が欠けている場合

次に、意図的にいくつかの変数を欠かせた .env を用意して試してみます。

# .env.incomplete

APP_NAME=MyDemoApp
PORT=3000

DATABASE_HOST=localhost
# Missing: DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD

API_KEY=my-api-key-12345
# Missing: SECRET_TOKEN

ENABLE_LOGGING=true

この状態で実行すると:

$ node index.js
✗ Error loading environment variables!

The following variables were defined in .env.example but are not present in the environment:
  DATABASE_PORT, DATABASE_USER, DATABASE_PASSWORD, SECRET_TOKEN
Make sure to add them to .env or directly to the environment.

If you expect any of these variables to be empty, you can use the allowEmptyValues option:
require('dotenv-safe').config({
  allowEmptyValues: true
});

Missing variables: [
  'DATABASE_PORT',
  'DATABASE_USER',
  'DATABASE_PASSWORD',
  'SECRET_TOKEN'
]

欠けている変数がはっきりと示され、アプリケーションは起動しません。このエラーメッセージは非常に分かりやすく、どの変数が足りないかが一目瞭然です。

6. allowEmptyValues オプション

場合によっては、環境変数が空の値("" や未設定)でも許可したいことがあります。そのようなケースでは allowEmptyValues オプションが使えます。

// test-empty-values.js

// Test 1: Without allowEmptyValues (should fail)
try {
  require("dotenv-safe").config({
    path: ".env.with-empty",
  });
  console.log("✓ Loaded successfully");
} catch (error) {
  console.log("✗ Failed as expected");
  console.log("Error:", error.message.split("\n")[0]);
}

// Test 2: With allowEmptyValues: true
try {
  require("dotenv-safe").config({
    path: ".env.with-empty",
    allowEmptyValues: true,
  });
  console.log("✓ Loaded successfully!");
} catch (error) {
  console.log("✗ Failed:", error.message);
}

空の値を含む .env.with-empty:

DATABASE_PASSWORD=
SECRET_TOKEN=

実行結果:

Test 1: Without allowEmptyValues (should fail)
------------------------------------------------
✗ Failed as expected
Error message: The following variables were defined in .env.example but are not present in the environment:

Test 2: With allowEmptyValues: true
------------------------------------------------
✓ Loaded successfully!

allowEmptyValues: true を指定すると、空の値でもエラーにならないことが確認できました。

7. 外部から環境変数を渡す場合

dotenv-safe は、.env ファイルだけでなく、シェルやCI環境から渡される環境変数も検証対象にします。

部分的な .env.partial:

# .env.partial
APP_NAME=MyDemoApp
PORT=3000
DATABASE_HOST=localhost
DATABASE_PORT=5432

この状態で、残りの変数をシェルから渡してみます:

$ DATABASE_USER=external_user \
  DATABASE_PASSWORD=external_pass \
  API_KEY=external_key \
  SECRET_TOKEN=external_token \
  ENABLE_LOGGING=false \
  node test-external-vars.js
// test-external-vars.js
const result = require("dotenv-safe").config({
  path: ".env.partial",
});

console.log("✓ All required variables are present!");
console.log("DATABASE_USER:", process.env.DATABASE_USER, "(from shell)");
console.log("DATABASE_PASSWORD:", "******", "(from shell)");

結果:

✓ All required variables are present!

Final values:
APP_NAME: MyDemoApp (from .env.partial)
PORT: 3000 (from .env.partial)
DATABASE_HOST: localhost (from .env.partial)
DATABASE_PORT: 5432 (from .env.partial)
DATABASE_USER: external_user (from shell)
DATABASE_PASSWORD: ****** (from shell)
API_KEY: ****** (from shell)
SECRET_TOKEN: ****** (from shell)
ENABLE_LOGGING: false (from shell)

.env.partial にない変数も、シェルから渡されていれば検証をパスしました。これは CI/CD 環境で環境変数を外部から注入する場合に便利だと思います。

8. プリロード機能

Node.js の -r フラグを使うことで、コード内で require() を書かなくても環境変数を読み込めます。

// app-without-require.js
// No require('dotenv-safe') call

console.log("APP_NAME:", process.env.APP_NAME);
console.log("DATABASE_HOST:", process.env.DATABASE_HOST);
$ node -r dotenv-safe/config app-without-require.js
Environment variables (loaded via -r flag):
APP_NAME: MyDemoApp
PORT: 3000
DATABASE_HOST: localhost
DATABASE_USER: devuser

コードに何も書かなくても、環境変数が読み込まれ、検証も行われます。既存のアプリケーションに導入を試す際に便利だと感じました。

良かった点

1. 設定漏れを早期に発見できる

アプリケーションが起動してから「あれ、この環境変数が足りない」と気づくのではなく、起動時に確実にチェックできるのは安心感があります。特に本番環境へのデプロイ前に検証できる点が良いと思いました。

2. ドキュメントとして機能する

.env.example を見れば、このアプリケーションにどんな環境変数が必要なのかが分かります。新しいメンバーがプロジェクトに参加したときや、環境を新しく構築する際のガイドになると感じました。

3. dotenv との互換性

API が dotenv と同じなので、既に dotenv を使っているプロジェクトへの導入が容易です。require('dotenv')require('dotenv-safe') に変えて、.env.example を追加するだけで動作します。

4. エラーメッセージが分かりやすい

何が足りないかが明確に表示され、さらに allowEmptyValues オプションの使い方まで提案してくれます。このユーザーフレンドリーな設計が印象的でした。

5. CI/CD との相性が良い

外部から渡される環境変数も検証してくれるため、GitHub Actions や GitLab CI などで環境変数を Secrets として管理している場合も安心して使えます。

6. シンプルな設計

複雑な設定は必要なく、基本的な使い方は非常にシンプルです。学習コストが低いと感じました。

気づいた点

1. .env.example の管理

.env.example を最新の状態に保つ必要があります。新しい環境変数を追加したときは、.env.example の更新を忘れないようにする運用が必要だと思いました。

ただ、これは dotenv-safe の問題というよりも、環境変数を適切に管理するために必要なプラクティスだと感じます。

2. dotenv が peer dependency

dotenv-safe 単体ではなく、dotenv も一緒にインストールする必要があります。これにより、使いたい dotenv のバージョンを柔軟に選べる反面、両方をインストールし忘れるとエラーになります。

ただし、README にも明記されていますし、npm/pnpm が警告を出してくれるので、実際には問題になることは少ないと思います。

3. 空の値の扱い

デフォルトでは、環境変数が空("")だとエラーになります。これは安全側に倒した設計だと思いますが、意図的に空の値を許可したい場合は allowEmptyValues: true を設定する必要があります。

この挙動を知らないと、最初は戸惑うかもしれません。ただ、エラーメッセージにヒントが含まれているので、すぐに解決できました。

まとめ

私が感じたこと

dotenv-safe は、シンプルながら実用的なライブラリだと感じました。特に以下の点が印象的でした:

どんな場合に使えそうか

以下のような場面で特に有用だと思います:

逆に、環境変数が1〜2個程度の小規模なプロジェクトでは、dotenv-safe を導入するほどではないかもしれません。

個人的な感想

実際に動かしてみて、エラーケースも含めて挙動を確認できたのは良かったです。特に、外部から渡される環境変数も検証してくれる点は、ドキュメントを読むだけでは気づきにくい部分だと思いました。

環境変数の管理に課題を感じている方や、設定漏れによるトラブルを経験したことがある方には、試してみる価値があると思います。

参考リンク


dotenv-safe を実際に試してみて、環境変数の設定漏れを防ぐ仕組みがシンプルかつ効果的に実現されていることが分かりました。

この記事が、環境変数の管理方法を検討している方の参考になれば幸いです。


Share this post on:

Previous Post
nuqs を試してみた - URL のクエリパラメータを React の state のように扱えるライブラリ
Next Post
dotenv-flow を試してみた - 環境ごとに .env ファイルを使い分ける