Cloudflare Workersの環境差をTypeScriptの型で守る
X-tech推進本部 黒澤はじめに
Cloudflare Workersでは、単にコードを実行するだけではなく、データベース(Workers KVやD1)やオブジェクトストレージ(R2)などのサービスをバインドして(紐づけて)、複雑な機能を実現できます。また、環境変数を参照して処理を切り替えることもできます。
一般に、バインディングや環境変数は、本番やステージングなどの環境によって異なります。存在しないバインディングや環境変数を呼び出そうとするとエラーになるため、環境の混同が起きにくいコードを記述することが重要です。
この記事では、TypeScriptの型を使って、環境を混同しにくいコードを記述する方法を紹介します。
環境ごとの設定管理
型の話に入るまえに、まずは環境ごとの設定管理方法を確認します。
Cloudflare Workersでは、wranglerコマンドの設定ファイル(wrangler.jsoncなど)にenvオブジェクトを記述することで、環境ごとの設定を管理できます(Cloudflareの開発者向けドキュメント)。
次の例ではstaging環境に対してWorkers KVがバインドされていますが、main環境にはバインドされていません。また、環境変数WORKER_ENVは環境ごとに値が異なります。
{
"$schema": "node_modules/wrangler/config-schema.json",
"account_id": "...",
"name": "main",
"main": "_worker/index.ts",
"vars": {
"WORKER_ENV": "main"
},
"env": {
"staging": {
"name": "staging",
"vars": {
"WORKER_ENV": "staging"
},
"kv_namespaces": [
{
"binding": "KV_DEBUG",
"id": "..."
}
]
}
}
}
wranglerコマンドに--envを指定することで、どの環境の設定を参照させるかを制御できます。
# main環境へのデプロイ
wrangler deploy
# staging環境へのデプロイ
wrangler deploy --env staging
このように環境ごとに使える機能が異なると、条件分岐を間違えた時に実行時エラーになります。次は、この違いをTypeScriptの型として表現し、型チェック時に検知する方法を見ていきます。
環境とTypeScriptの型
環境ごとの違いをコード側から見ていきます。次のようにKV_DEBUGを無条件に呼ぶと、main環境では実行時エラーになります。
export default {
async fetch(request, env, ctx): Promise<Response> {
await env.KV_DEBUG.put('DEBUG_KEY', 'DEBUG_VALUE');
return new Response('ok');
}
} satisfies ExportedHandler<Env>;
staging環境かどうかで分岐したいところですが、そのまえにwrangler typesで生成される型を確認します。
npx wrangler types
wranglerの設定ファイルでenvオブジェクトに環境を記述している場合、worker-configuration.d.tsには次のような型が生成されます(wrangler 4.114.0の例)。
interface __BaseEnv_Env {
KV_DEBUG?: KVNamespace;
WORKER_ENV: "staging" | "main";
}
declare namespace Cloudflare {
interface GlobalProps {
mainModule: typeof import("./_worker/index");
}
interface StagingEnv {
KV_DEBUG: KVNamespace;
WORKER_ENV: "staging";
}
interface Env extends __BaseEnv_Env {}
}
interface Env extends __BaseEnv_Env {}
Cloudflare Workersのコードで通常参照するEnv型は、__BaseEnv_Env型を継承しています。__BaseEnv_Env型は、全環境に存在するもの(WORKER_ENV)は非optionalな型として定義され、一部の環境にしか存在しないもの(KV_DEBUG)はoptionalな型として定義されます。また、環境変数WORKER_ENVは環境によって値が異なるため、ユニオン("staging" | "main")として定義されています。
一方で、envオブジェクトで定義した環境ごとに専用の型が生成されます。先ほどの例ではstaging環境を定義しましたので、Cloudflare.StagingEnvが定義されています。環境専用の型ですので、全てのプロパティは非optionalな型として定義されています。また、環境変数WORKER_ENVも純粋なリテラル("staging")として定義されています。
fetch関数に渡されるのは集約されたEnv型ですので、KV_DEBUGはoptionalです。また、環境ごとの型がユニオンになる(例:type Env = MainEnv | StagingEnv)のではなく、WORKER_ENVなどのプロパティ値がユニオンになっている(WORKER_ENV: "staging" | "main")ため、条件分岐でプロパティ値をチェックしても型は絞り込めません。
export default {
async fetch(request, env, ctx): Promise<Response> {
if (env.WORKER_ENV === 'staging') {
await env.KV_DEBUG.put('DEBUG_KEY', 'DEBUG_VALUE');
}
return new Response('ok');
}
} satisfies ExportedHandler<Env>;
上記の例ではenv.KV_DEBUGはoptionalなままです(undefinedの可能性があるため型チェックが通りません)。
そこで、環境専用の型へ絞り込む型ガードを用意します。
const isStagingEnv = (env: Env): env is Cloudflare.StagingEnv => {
return env.WORKER_ENV === 'staging';
};
上記の型ガードで条件分岐すれば、envの型はCloudflare.StagingEnvに絞り込まれ、env.KV_DEBUGは非optionalになります。
export default {
async fetch(request, env, ctx): Promise<Response> {
if (isStagingEnv(env)) {
await env.KV_DEBUG.put('DEBUG_KEY', 'DEBUG_VALUE');
}
return new Response('ok');
}
} satisfies ExportedHandler<Env>;
また、型ガードを通さずenv.KV_DEBUGを直接呼ぶコードが残っていても、optionalのままなのでTypeScriptの型チェックで弾けます。
制限事項
この記事の例では型ガードに環境変数WORKER_ENVの値を利用しています。複数の環境でWORKER_ENVが同じ値を持つと、型の絞り込み結果に意味がなくなります。型ガードで利用する環境変数は環境ごとに異なる値を設定する必要があります。
wrangler typesが環境ごとの型を生成するようになったのは2026年1月リリースの4.60.0からですので、それ以前のwranglerを利用している場合はこの方法は利用できません。wranglerの更新が必要です。
また、wrangler 4.114.0時点では、wrangler typesはトップレベル専用の型(この例ではmain)は生成しません。そのため、main環境の型に絞り込めるようにするには、自分で型を記述するか、envオブジェクトにmain環境用の設定を記述する必要があります。
例えば、トップレベルはstaging環境でもmain環境でもないdev環境とし、main環境とstaging環境をどちらもenv配下に定義することが可能です。こうすると、MainEnvとStagingEnvの両方が生成されます。なお、次の例では説明のため、先ほどの例とは逆にmain側へKVを置いています。
{
"$schema": "node_modules/wrangler/config-schema.json",
"account_id": "...",
"name": "dev",
"main": "_worker/index.ts",
"env": {
"main": {
"name": "main",
"vars": {
"WORKER_ENV": "main"
},
"kv_namespaces": [
{
"binding": "KV_DEBUG",
"id": "..."
}
]
},
"staging": {
"name": "staging",
"vars": {
"WORKER_ENV": "staging"
}
}
}
}
まとめ
本記事でご紹介したように、wrangler typesが生成する環境ごとの型と型ガードを組み合わせると、環境固有のバインディングを誤って参照しにくいコードを書けます。Cloudflare Workersを複数環境で運用する場合の参考になれば幸いです。