Last updated on

envの違い


このサイトに新しい機能を追加する更新をしていた時にハマった

process.env import.meta.env locals.runtime.envの違いに

よく分かってないんだけど、Astro+Cloudflare Workers の場合は、locals.runtime.envを使うべきという事っぽい

解説間違ってるかもだけど

このサイトの構成、Astro で作成して Cloudflare Pages にデプロイ、基本は静的なページで一部動的な部分(API)がありそれは、Astro のインテグレーションで Cloudflare Workers でインテグレーションして Cloudflare Workers で API 部分を動かしてる、っていう感じになってるはずなんだよね

それで、環境変数を使いたい時って大抵 API の部分で使いたいってなると思うんだけど

export async function POST({ locals }) {
  const secret = locals.runtime.env.SECRET_KEY;
}

こういう感じにlocals.runtime.envを使わないといけない

まず、サイトは Astro からビルドされて html や js になり、それが Cloudflare に公開されて Workers で動く部分は Workers のサーバーで動く訳だ、Astro は Vite を使ってビルドするぞ

それで API の部分もビルドされるんだよな

import.meta.envは Vite が利用するオブジェクトで Astro は Vite を使ってビルドしてるのでこれがビルド時に使えるんだけど、ビルドする際にこれらの変数は展開されるんだな
 それで、展開される訳なんだけど、環境変数が漏れ出ないようにするために、Astro はクライアント側のコードではPUBLIC_*というようなPUBLIC_から始まる環境変数しか使えないようにしてるんだな、それ以外の環境変数は undefined になって展開されるんだ
 だから、環境変数にSECRET_KEY=hogeとか入れてた場合クライアント側のコードではconst secret ="hoge"ではなくconst secret = undefinedになるんだな

https://docs.astro.build/en/guides/environment-variables/#vites-built-in-support

環境変数名をPUBLIC_*というようなPUBLIC_から始まる環境変数名にすると

pages/api/test.ts

import type { APIRoute } from "astro"
export const prerender = false

export const GET: APIRoute = async ({ locals }) => {
  return Response.json({
    buildTime: import.meta.env.PUBLIC_KEY,
    runtime: locals.runtime.env.PUBLIC_KEY,
  })
}
buildTime: "hoge",
runtime: locals.runtime.env.PUBLIC_KEY,

という感じにビルドされるんだ
 デプロイ視点から見てると Cloudflare というサーバー上でコードが動いてるんだけど、その前に Astro(Vite)が一回ビルドしてるからそこで値になってるんだな~

astro buildして dist の中の_worker.js フォルダの中とかの js コード見ると分かりやすいですね

それと

locals.runtime.envは Astro の Cloudflare adapter / integration が dev 時に Workers 互換の env をエミュレートしている
ので開発時も使える

はい

以上、たぶん、うん…

適当まとめ

仕組み | いつ値が決まるか
process.env | Node 実行時
import.meta.env | ビルド時
locals.runtime.env | Workers 実行時

そもそも俺は環境変数はいつ値が決まるかという事を意識してなかったな、それが敗因だぜ