Deno Deployではdotenvを使えない
Deno公式のCDN Edgeサービス、Deno Deployを勉強中です。
Deno Deployへ上げるためのコード内で、以下のdotenvモジュールを使用して.env
ファイルに定義した環境変数にアクセスしようとしたところ、エラーが発生したので対処法をまとめておきます。
タイトルに書いていますが、Deno Deployを使いたい場合、dotenvモジュールは(通常のままでは)使用できません。
発生したエラー
Deno Deployで動くコード中にてdotenvが使用されているとこんなエラーが出ます。
❯ deployctl run ./server.ts
Check file:///Users/kawarimidoll/ghq/github.com/kawarimidoll/deno-github-contributions-api/$deno$eval.ts
error: TS2339 [ERROR]: Property 'readFileSync' does not exist on type 'typeof Deno'.
return parse(new TextDecoder("utf-8").decode(Deno.readFileSync(filepath)));
~~~~~~~~~~~~
at https://deno.land/x/dotenv@v2.0.0/mod.ts:76:55
TS2339 [ERROR]: Property 'errors' does not exist on type 'typeof Deno'.
if (e instanceof Deno.errors.NotFound) return {};
~~~~~~
at https://deno.land/x/dotenv@v2.0.0/mod.ts:78:27
Found 2 errors.
dotenvが環境変数を読み取るためにファイルを開こうとするのですが、そこで
「Deno.readFileSync(filepath)
なんて無いよ」と言われています。
Deno deploy用の対処
issueが上がっていました。
「Deno Deployではファイルシステム関連の機能はサポートしてない」とのことです。
なるほど。それならエラー出ますよね。
環境変数の設定
ということで対処法の紹介です。
まず、Deno Deployで動く可能性のあるコードからはdotenvを取り除きます。
ローカルでの設定
deployctl
のドキュメントにありますが、--env
オプションを使うことで.env
ファイルを読み込むことができます。
deployctl run --env=.env ./server.ts
Webでの設定
実際のDeno Deployに上げる場合は、Settings画面から設定を行います。
サイドバーのEnvironment Variablesから設定しましょう。
なお、こちらで設定した環境変数はshowボタンで後から確認できます。
また、現在のバージョンでは設定後に 再度Deployしないと環境変数が読み込まれない ようです。
Web GUI上ではRedeployを行う機能が見当たらないので、READMEを適当に変更してpushするなどして再起動する必要があります。
環境変数の使用
コード中でDeno.env.get()
を呼べば使えます。
SECRET_TOKEN=xxxxx
console.log(Deno.env.get("SECRET_TOKEN"))
問題なく.env
内のデータを使えるようになりました。めでたしめでたし。
もちろん、実際はトークンをconsole.log()
なんてやっちゃ駄目ですよ。
通常のスクリプトとの共存
これでDeno Deploy環境ではエラーが出なくなります。
ところが、同じコードをdeno run
でも動かしたい場合、コード内に環境変数を読み込む箇所がなくなるため、失敗(Deno.env.get("SECRET_TOKEN")
がundefined
)となります。
しかし、前述の通り、コード内で外部ファイルを読み込むとDeno Deploy非対応となってしまいます。
したがって、「Denoの実行コード外で環境変数を設定する」必要があります。
実行時に環境変数を渡す
deno run
実行時に環境変数を渡すことで、環境変数を使用できます。
❯ SECRET_TOKEN=xxxxx deno run --allow-env main.ts
ただし、毎回これを行うのは面倒です。入力はコピペで楽をするとしても、端末の表示に生のトークンが残ってしまうのはあまり気持ちよくありません。
Velociraptorを使って環境変数を設定する
個人的にオススメの方法はこちら。タスクランナーのVelociraptorの力を借ります。
VelociraptorのenvFile
オプションを使うと、指定したファイルから環境変数を読み込み、Deno.env
に設定してくれます [1]。
以下のvelociraptor.yml
を用意することで、vr main
でmain.ts
を、vr server
でserver.ts
を実行でき、その両方で.env
ファイルから環境変数を読み込んで使用できます。
envFile:
- .env
scripts:
main:
desc: Runs main script
cmd: main.ts
server:
desc: Starts local server
cmd: deployctl run --env=.env server.ts
Velociraptorに関しては以下の記事でも説明しています。
要検討:env.tsで一元管理する
以下は実行時に渡すか、Velociraptorを使うかしてDeno.env
に環境変数が設定されている場合の環境変数の管理方法の検討です。
上で紹介したテンプレートの記事にも載せていたのですが、これまではこういうenv.ts
で環境変数を一元管理していました。
import { config } from "https://deno.land/x/dotenv@v2.0.0/mod.ts";
const {
SECRET_TOKEN,
} = config({ safe: true });
export { SECRET_TOKEN };
使うときは以下のようにenv.ts
から定数をimport
します。
import { SECRET_TOKEN } from "./env.ts";
console.log(SECRET_TOKEN)
ただ、今後Deno Deployを使うことを考えると、この形式は採用できません。
しかし、各所でDeno.env.get()
を呼び出すのもなんかちょっとな…という気がします。環境変数を定義し忘れているときにthrow new Error()
してもらいたいというのもあるので。
ということで、env.ts
の代替案を考えてみました。
案1
const SECRET_TOKEN = Deno.env.get("SECRET_TOKEN") ?? "";
if (!SECRET_TOKEN) {
throw new Error("No token: SECRET_TOKEN");
}
export { SECRET_TOKEN };
import { SECRET_TOKEN } from "./env.ts";
console.log(SECRET_TOKEN)
- 👍 使う側のコードをこれまでと変える必要がない
- 👍 必要な環境変数を
env.ts
内で確認できる - 👎
env.ts
内に環境変数名が5回も出現する 扱う環境変数が複数になるとしんどい
案2
const env: { [key: string]: string } = {};
const envNames = [
"SECRET_TOKEN",
];
envNames.forEach((envName) => {
const envValue = Deno.env.get(envName);
if (!envValue) {
throw new Error(`No token: ${envName}`);
}
env[envName] = envValue;
});
export default env;
import env from "./env.ts";
const { SECRET_TOKEN } = env
console.log(SECRET_TOKEN)
- 👍 環境変数名を
envNames
に追加すれば読み込み可能 - 👍 必要な環境変数を
env.ts
内で確認できる - 👎
export
されている環境変数名をTSコンパイラが認識できないため、使用する際に変数env
(名前は任意)を経由する必要がある
案3
export default function (envName: string) {
const envValue = Deno.env.get(envName);
if (!envValue) {
throw new Error(`No token: ${envName}`);
}
return envValue;
}
import env from "./env.ts";
console.log(env("SECRET_TOKEN"))
- 👍 実質
Deno.env.get()
のラッパー- 存在確認と型限定が追加された感じ
- 👎 必要な環境変数を一覧できる場所がない
- 👎 使用感がけっこう変わる
ちょっと使い方が変わるものの、案3が一番良いかな…と考えています。
ボツ案
Deno DeployのWeb上ではDENO_DEPLOYMENT_ID
という環境変数が設定されるのでこれで分岐とかできないかなーとか思いましたがローカルでdeployctl
使うときには設定されないようだったので諦めました
おわりに
Deno Deployは現状beta1なのでこのあたりの仕様が変わる可能性はあります。
とはいえ、今後盛り上がっていくことは確実なので、Deno deployで使いやすい設定に寄せて行ったほうがよさそうです。
-
実際は環境変数を読み込んで
Deno.run()
のenv
オプションに渡しています:https://github.com/jurassiscripts/velociraptor/blob/main/src/run_commands.ts ↩︎
Discussion