はじめに
Web系のプロジェクトで開発をしていると.env
.env.development.
.env.test
などにシークレット情報や、環境ごとに異なる設定を書くことが多い
環境変数からデータを読み込めために(言語やフレームワークに関わらず)おおよそ下記のようなファイルを用意する
1 機密情報を格納するためのファイル(.env)の準備
API_KEY='xs9djq4iq'
DEBUG='TRUE'
2 当然.gitignoreで.envをバージョン管理から除外
.env
3 バージョン管理から除外すると.env
にどのような値が存在するかの管理が難しくなるので.env.sample
を用意
API_KEY='CHANGE ME'
DEBUG='true'
4 環境変数を取得するコードを書く(下記はnode.jsの場合)
const API_KEY: string = process.env.API_KEY
const DEBUG : boolean = process.env.DEBUG === true
.env
に値を追加するとき
.env
とenv.ts
と.env.sample
の3箇所に同じような記述をする必要がある。
API_KEY='xs9djq4iq'
DEBUG='TRUE'
NEW_ENV='xxx'
const API_KEY: string = process.env.API_KEY
const DEBUG : boolean = process.env.DEBUG === true
const NEW_ENV: string = xxx
API_KEY='CHANGE ME'
DEBUG='true'
NEW_ENV='Change ME'
辛い点1 タイポ
↓こういうタイポが起きる(ことがある)
NEW_EMV='Change ME'
環境変数周りは、エディターがタイポを検出するのが難しいので、目検になる。辛い。
辛い点2 煩わしい
3箇所に似たような記述したくない。
作ったもの1
.env
からTypescriptのコードを自動生成する→煩わしさやタイポリスクが減る
Usage
# this is a comment
str="value"
nospace=nospace
# a few blank lines
is_true=true
is_false=false
num=123
zero=0
str_start_zero=0123
$ deno run cli -A -i 'path to env file' -o 'path to output file' -t 'deno or node'
/************************************
* This file is generated by deno-env-codegen
************************************/
const getEnv = (key: string) => {
const val = process.env[key];
if (val === undefined) {
throw new Error(`Environment variable "\${key}" is not defined`);
}
return val;
};
const toNumber = (val: string) => {
return Number(val);
};
const toBoolean = (val: string) => {
return val === "true";
};
// this is a comment
export const str = getEnv("str");
export const nospace = getEnv("nospace");
// a few blank lines
export const is_true = toBoolean(getEnv("is_true"));
export const is_false = toBoolean(getEnv("is_false"));
export const num = toNumber(getEnv("num"));
export const zejro = toNumber(getEnv("zero"));
export const str_start_zero = getEnv("str_start_zero");
作ったもの2
.env
と.env.sample
のdiffを表示する→タイポミスの防止& .env
と.env.sample
の乖離防止
env='env'
env2='env2'
env3='env3'
env_and_sample='env_and_sample-in-env'
env_and_sample2='env_and_sample-in-env2'
sample='sample'
sample2='sample2'
sample3='sample3'
env_and_sample='env-and-sample-in-sample'
env_and_sample2='env_and_sample-in-sample2'
$ deno run --allow-read=. cli.ts .env .env.sample
Keys that exist in .env.sample but not in .env: sample, sample2, sample3
Keys that exist in .env but not in .env.sample: env, env2, env3
課題
-
.env
にはあるが、コード中には必要ない値もenv.ts
に生成されてしまう。 -
.env
と.env.sample
の整合性チェックは、結局のところ人間の目に頼ってしまっている - cli形式でパッと読み込める形でパッケージ化していない
- 整合性チェック弱い※
※整合性チェックが弱い件について
「作ったもの2」ではtsのコードを自動生成するという方法を取ったけど、zod
みたいなものを利用したスキーマ定義を.env
を自動生成する形の方が安全で使いやすいかもしれない。(たとえば、「作ったもの2」だと DEBUG=ture
みたいな明らかなタイポでさえも検知できないsrc/env.ts
ではbooleanのfalse
になってしまう)
例)
$ env-gen -i '.env' -o './src/env.ts'
PORT=8080
HOST=localhost
NODE_ENV=development
DEBUG=true
import z from 'zod'
import process from 'process'
export type Env = typeof env;
export const env = z.object({
PORT: z.number(),
HOST: z.string(),
NODE_ENV: z.enum(["development", "production"]).default('development']),
DEBUG: z.boolean(),
}).parse(process.env);
みたいに使えた方が安全でメンテナンスしやすいかもしれない(開発の要件にもよる)