できない.dev

.env ファイルを読み込むには

.env ファイルの内容を環境変数として読み込む基本形を各言語で示す。
ライブラリを使う書き方と、ランタイム側に標準機能がある場合の使い分けまでを扱う。

公開:

各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。

Python 実行確認済み

import os
 
from dotenv import load_dotenv  # pip install python-dotenv
 
load_dotenv()                       # カレントから上へ .env を探して読む
load_dotenv("config/.env")          # 位置を明示することもできる
 
print(os.getenv("PORT", "3000"))    # 読み込み後は通常の環境変数として扱う

python-dotenv の load_dotenv は .env を読んで os.environ に流し込む。
読み込み後の取り出し方は通常の環境変数と変わらない。
既に同名の環境変数が設定されている場合はそちらが優先されるため、.env で上書きしたいときは override=True を渡す。

JavaScript 実行確認済み

// Node.js 20.10 以降: 追加ライブラリ不要
process.loadEnvFile();            // 既定で ./.env を読む
process.loadEnvFile("./config/.env");
 
console.log(process.env.PORT);
 
// 起動時に読ませる方法(Node.js 20.6 以降)
//   node --env-file=.env app.js

Node.js は 20.6.0 で --env-file フラグ、20.10.0 で process.loadEnvFile() を取り込んだため、新しめの環境では dotenv パッケージなしで読める。
どちらも 20.x LTS へバックポートされている点に注意する。
古い Node を対象にするなら require("dotenv").config() を使う。
いずれの方法でも、読み込みは環境変数を参照する側より先に実行する必要がある。

うまくいかない時: Node.js で dotenv の値が読み込めないNext.js で環境変数 (process.env) が読み込めないVite で環境変数(import.meta.env)が読み込めない

TypeScript 実行確認済み

process.loadEnvFile();
 
// process.env のプロパティは string | undefined
const port: string = process.env.PORT ?? "3000";
console.log(port);

読み込み自体は JavaScript と同じで、型の扱いだけが変わる。
process.env のプロパティは string | undefined 型なので、?? で既定値を与えるか存在を確認して絞り込む。
process や loadEnvFile の型定義には @types/node が要る。

うまくいかない時: Node.js で dotenv の値が読み込めない

Go 静的確認

package main
 
import (
	"fmt"
	"log"
	"os"
 
	"github.com/joho/godotenv" // go get github.com/joho/godotenv
)
 
func main() {
	if err := godotenv.Load(); err != nil { // 既定で ./.env
		log.Println(".env が見つからない:", err)
	}
	fmt.Println(os.Getenv("PORT"))
}

godotenv.Load は .env を読んで os.Setenv 相当の反映を行う。
ファイルが無いと error を返すので、.env を任意扱いにするなら err をログに落とすだけにして処理を続ける。
godotenv は既存の環境変数を上書きしない点も python-dotenv と同じである。

Rust 静的確認

use std::env;
 
fn main() {
    // Cargo.toml: dotenvy = "0.15"
    dotenvy::dotenv().ok(); // .env が無くても続行する
 
    let port = env::var("PORT").unwrap_or_else(|_| "3000".to_string());
    println!("{port}");
}

dotenvy はメンテナンスが止まった dotenv クレートの後継である。
dotenv() は Result を返すので、.env を必須にしないなら ok() で握りつぶす。
読み込み後は std::env::var で通常どおり取り出せる。

Java 静的確認

// build.gradle: implementation 'io.github.cdimascio:dotenv-java:3.0.0'
import io.github.cdimascio.dotenv.Dotenv;
 
public class Main {
    public static void main(String[] args) {
        Dotenv dotenv = Dotenv.load();          // 既定で ./.env
        System.out.println(dotenv.get("PORT", "3000"));
    }
}

dotenv-java は読み込んだ値を Dotenv オブジェクト経由で返し、System.getenv には流し込まない。
したがって取得は dotenv.get() を使う。
.env が無くても落としたくない場合は Dotenv.configure().ignoreIfMissing().load() を使う。

C# 静的確認

// dotnet add package DotNetEnv
using System;
using DotNetEnv;
 
class Program {
    static void Main() {
        Env.Load();                 // 既定で ./.env
        Env.Load("config/.env");    // 位置の明示
 
        string port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
        Console.WriteLine(port);
    }
}

DotNetEnv の Env.Load は読み込んだ値をプロセスの環境変数へ設定するため、以後は Environment.GetEnvironmentVariable で取り出せる。
ASP.NET Core では appsettings.json と環境変数を束ねる構成システムが標準で用意されているので、.env を使うのはコンソールアプリや開発時に限るとよい。

つまずき

.env が読めない原因で最も多いのは、読み込み処理より先に環境変数を参照してしまっていることである。
モジュールのトップレベルで設定値を定数に固定していると、読み込みが後から走っても既に評価が終わっている。
読み込みはエントリポイントの最上部で行うか、ランタイムの起動オプションでプロセス開始前に済ませる。
次に多いのが探索位置のずれで、多くのライブラリはカレントディレクトリを基準にするため、プロジェクトルート以外から起動すると .env が見つからない。

既存の環境変数との優先順位

python-dotenv・godotenv・dotenv パッケージはいずれも、既に設定されている環境変数を .env の値で上書きしない。
ローカルでは .env が効くのに本番では効かない、という挙動はこの仕様どおりであり、本番のプラットフォーム側で設定した値が優先されている。
意図的に上書きしたい場合だけ override 相当のオプションを明示する。

.env をリポジトリに含めない

.env には API キーや接続文字列を書くため、.gitignore に追加してコミットしない。
代わりにキー名だけを並べた .env.example を配置しておくと、必要な変数が一覧でき、新しく参加した開発者が何を用意すべきか分かる。

この記事は役立ちましたか?