はじめに
Web サイトでは、ダークテーマや優先言語などの表示設定を記憶できます。こうした設定はアクセスのたびに読み取られる一方、変更される頻度は低いため、Workers KV の利用例として適しています。機能フラグとして単語を 1 つ保存する代わりに、名前付きフィールドを 1 つの値にまとめたテキストである JSON を保存します。Worker はそのテキストを、利用可能な設定に戻します。
この実験では、Alice と Bob は実在のユーザーではなく、架空のアカウントラベルです。2 つのアカウントに異なる設定を割り当て、エントリがない場合や破損している場合には、適切なデフォルト値を返すようにします。また、値と一緒に保存する短い説明である メタデータ を付加し、設定のリビジョンを識別します。リビジョン番号は、どのデータが読み取られたかを説明するためのものであり、すべての場所で常に最新値がすぐに表示されることを保証するものではありません。
最初に「Create a Feature Flag Store」を完了してください。この実験は、/home/labex/project/account-preferences にある新しい VM で開始します。Node.js 22.22.0 と、プロジェクトローカルの Wrangler 4.131.1 はすでにインストールされています。学習用アカウントに新しい Worker と namespace を作成し、前の実験と同じ account-read、Worker-write、KV-write 権限を使用します。公開デモで表示されるのは合成された表示設定だけであり、URL のアカウントラベルは認証ではありません。この小さな演習に有料プランへのアップグレードやドメインの購入は必要ありません。VM を離れる前に、リソースのクリーンアップを完了してください。
Preference namespace を接続する
このステップでは、サンプルアカウント設定用の独立した namespace を接続します。namespace はこのサービスの値をまとめる単位です。PREFERENCES binding により、Worker のコードから namespace にアクセスするための固定名を使えるようになります。この新しい VM では、前の実験の namespace や認証情報ではなく、アカウントに関する知識だけを再利用します。
用意されたプロジェクトに移動します。
cd /home/labex/project/account-preferences
一意な名前を一度だけ生成します。openssl rand -hex 6 はランダムなサフィックスを出力し、$(...) はその値を名前に挿入します。シェル変数に保存した名前は、このターミナルで続けて実行するコマンドから利用できます。
WORKER_NAME="labex-prefs-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
この VM を認証します。アカウント ID の読み取りに加えて、Workers Scripts Write ではデプロイと削除が許可され、Workers KV Write ではこの実験の namespace とキーを管理できます。
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
表示されたデバイス用リンクをブラウザーで開き、現在のコードを入力します。要求された権限と学習用アカウントを確認して、Wrangler を認証してください。同意ページにバックグラウンドアクセスが表示される場合もあります。ターミナルに戻り、ログインが完了するまで待ちます。
Developer Platform を展開し、Workers Scripts Write と Workers KV Storage Write を確認します。これらは「Create a Feature Flag Store」で導入したものと同じリソース管理権限です。
npx wrangler whoami --json
loggedIn: true と、学習用アカウントの name を確認します。アカウントが 1 つだけ表示される場合も同様です。そのアカウントの id をコピーします。以下の設定に保存する際は、コマンドを実行する前に YOUR_ACCOUNT_ID を置き換えてください。ここで使う cat のヒアドキュメントは、2 つの JSON 行の間にあるすべての内容をファイルに書き込みます。> はファイルを置き換えます。区切り文字が引用符で囲まれていないため、シェルは $WORKER_NAME を展開します。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true
}
JSON
そのアカウントに namespace を作成します。namespace のタイトルには Worker の一意な名前を含めるため、後で対応関係を確認できます。--update-config=false を指定すると、設定ファイルは自動変更されず、binding の編集内容を自分で確認できます。
npx wrangler kv namespace create "$WORKER_NAME-preferences" --update-config=false
出力には新しい namespace ID が含まれます。ID をコピーし、次の完全な設定内にある YOUR_ACCOUNT_ID と YOUR_NAMESPACE_ID を置き換えます。PREFERENCES はコードで使う binding 名であり、ID は実際の Cloudflare リソースを識別します。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true,
"kv_namespaces": [
{ "binding": "PREFERENCES", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
この実験の namespace タイトルを見つけ、ID が設定ファイルの ID と一致することを確認します。他の namespace が表示されても、そのままにしてください。この設定により、後続のコマンドで使用するアカウントとリソースが決まります。binding は namespace への参照であり、データのコピーではありません。
JSON 値とリビジョンメタデータを保存する
このステップでは、通常の設定と、現実的なデータミスを 2 種類含む小さなデータセットを用意します。JSON では、フィールド名と文字列に二重引用符を使用します。コマンド引数を囲む単一引用符により、シェルが JSON 内の二重引用符を解釈しないようにします。
ローカルエントリを書き込みます。Alice はダークモードと英語を、Bob はライトモードとフランス語を使用します。--metadata は、キーに別の JSON オブジェクトを付加します。ここでの revision 番号は保存したバージョンを示すラベルであり、セキュリティ上の判断や自動更新カウンターではありません。
npx wrangler kv key put account:alice '{"theme":"dark","language":"en"}' --binding PREFERENCES --local --metadata '{"revision":7}'
npx wrangler kv key put account:bob '{"theme":"light","language":"fr"}' --binding PREFERENCES --local --metadata '{"revision":8}'
npx wrangler kv key put account:broken 'not-json' --binding PREFERENCES --local
npx wrangler kv key put account:invalid '{"theme":"neon","language":"en"}' --binding PREFERENCES --local
account:broken には、JSON として解析できないテキストが含まれています。account:invalid は有効な JSON ですが、アプリケーションがサポートしていないテーマを指定しています。この 2 つのケースを用意することで、テキストの構造を読み取る 解析 と、フィールドがアプリケーションにとって妥当か確認する 検証 を区別できます。Charlie のエントリは作成しないでください。欠落キーの処理をテストするために使用します。
npx wrangler kv key list --binding PREFERENCES --local
4 つのキー名が表示されることを確認します。Alice と Bob には、それぞれ 7 と 8 のリビジョンメタデータが設定されています。他の 2 つのエントリにはメタデータがありません。一覧には名前とメタデータが表示されますが、すべての値が表示されるわけではありません。
npx wrangler kv key get account:alice --binding PREFERENCES --local --text
{"theme":"dark","language":"en"} が表示されることを確認します。このコマンドは値だけを読み取るため、リビジョンはこの JSON テキストには含まれません。
次に、同じ 4 つの合成フィクスチャを、この実験のクラウド namespace に書き込みます。以下のリモートコマンドは明示的に別の操作です。ローカルへの書き込みが Cloudflare へのアップロードになることはありません。
npx wrangler kv key put account:alice '{"theme":"dark","language":"en"}' --binding PREFERENCES --remote --metadata '{"revision":7}'
npx wrangler kv key put account:bob '{"theme":"light","language":"fr"}' --binding PREFERENCES --remote --metadata '{"revision":8}'
npx wrangler kv key put account:broken 'not-json' --binding PREFERENCES --remote
npx wrangler kv key put account:invalid '{"theme":"neon","language":"en"}' --binding PREFERENCES --remote
npx wrangler kv key list --binding PREFERENCES --remote
同じ 4 つのキー名と、それぞれのリビジョンメタデータを確認します。これらは破棄してよいデモ用レコードです。関係のない namespace は変更しないでください。
安全なデフォルト値で設定を読み取る
このステップでは、値とメタデータを一緒に取得するハンドラーを作成します。getWithMetadata() は、value フィールドと metadata フィールドを持つオブジェクトを返します。キーが存在しない場合、値は null になります。値が存在していても、メタデータが null になる場合があります。
このハンドラーを書き込みます。引用符で囲んだ JS ヒアドキュメントにより、コードがそのまま保持されます。このルートは短い小文字のアカウントラベルを受け取り、account:alice のような個別のキーを作成します。以前のリクエストのアカウントをグローバル変数に保存することはありません。
cat > src/index.js <<'JS'
function fallback(account, source) {
return Response.json({
account, theme: "light", language: "en", source, revision: null
});
}
export default {
async fetch(request, env) {
const match = new URL(request.url).pathname.match(/^\/preferences\/([a-z]{1,20})$/);
if (!match) return new Response("Not found", { status: 404 });
const account = match[1];
let entry;
try {
entry = await env.PREFERENCES.getWithMetadata(`account:${account}`, "text");
} catch {
return Response.json({ error: "Preferences temporarily unavailable" }, { status: 503 });
}
if (entry.value === null) return fallback(account, "missing");
let preferences;
try {
preferences = JSON.parse(entry.value);
} catch {
return fallback(account, "invalid");
}
if (!preferences || typeof preferences !== "object" || Array.isArray(preferences) ||
!["light", "dark"].includes(preferences.theme) ||
!["en", "fr"].includes(preferences.language)) {
return fallback(account, "invalid");
}
const revision = Number.isInteger(entry.metadata?.revision) && entry.metadata.revision > 0
? entry.metadata.revision : null;
return Response.json({
account, theme: preferences.theme, language: preferences.language,
source: "stored", revision
});
}
};
JS
最初の try/catch は、KV の読み取りが利用できない場合に HTTP 503 を返します。503 はサービスが一時的に利用できないことを示します。アカウントが存在しないかのようには扱いません。"text" として読み取った後、別の try/catch で解析することで、ストレージ障害と破損した JSON を区別できます。"json" オプションで読み取ると自動的に解析できますが、このレッスンでは 2 つの操作を分け、各エラー処理を確認できるようにしています。
設定がない場合も無効な場合も、ライトモードと英語にフォールバックします。source フィールドは、フォールバックを使用した理由を示します。有効な値の場合、レスポンスにはサポート対象のテーマと言語のフィールドだけが使用されます。entry.metadata?.revision により、メタデータがない場合も安全に処理できます。正の整数であるリビジョンだけを表示し、それ以外は null にします。これらのデフォルト値は、任意の表示設定を利用可能に保つためのものであり、認証や権限の代わりにはなりません。
ローカル Worker を起動し、プロセス ID を保存して、準備完了メッセージを待ちます。
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
バックグラウンドプロセスにすることでターミナルを自由に使えます。local.log には出力が保存されます。起動が完了していない場合は、ログコマンドをもう一度実行してください。それぞれのケースをリクエストします。
curl -i http://127.0.0.1:8080/preferences/alice
curl -i http://127.0.0.1:8080/preferences/bob
curl -i http://127.0.0.1:8080/preferences/charlie
curl -i http://127.0.0.1:8080/preferences/broken
curl -i http://127.0.0.1:8080/preferences/invalid
5 つすべてで、JSON を含む HTTP 200 が返るはずです。次の表で違いを確認してください。
| アカウント | テーマ | 言語 | ソース | リビジョン |
|---|---|---|---|---|
| alice | dark | en | stored | 7 |
| bob | light | fr | stored | 8 |
| charlie | light | en | missing | null |
| broken | light | en | invalid | null |
| invalid | light | en | invalid | null |
たとえば、Alice の本文は {"account":"alice","theme":"dark","language":"en","source":"stored","revision":7} です。Bob の後に Alice をもう一度リクエストし、設定が引き続き Alice のものであることを確認します。クリーンアップが完了するまで、ローカルサーバーは起動したままにしてください。
デプロイした設定サービスを確認する
このステップでは、クラウド namespace に対して同じケースを実行します。この独立したクラウド確認では、選択したアカウント、デプロイ済みの namespace binding、保存済みレコード、実際の HTTP レスポンスを検証します。
npx wrangler deploy
出力で生成された Worker 名と PREFERENCES binding を確認します。デプロイされた公開アドレスをコピーし、以下の変数に設定します。例のアドレスは置き換えてください。
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/preferences/alice"
curl -i "$WORKER_URL/preferences/bob"
curl -i "$WORKER_URL/preferences/charlie"
curl -i "$WORKER_URL/preferences/broken"
curl -i "$WORKER_URL/preferences/invalid"
5 つすべてのレスポンスをローカルの表と比較します。Alice と Bob はそれぞれの設定とリビジョンメタデータを保持し、Charlie と 2 つの破損レコードは説明したデフォルト値を使用する必要があります。書き込んだばかりのエントリがまだ表示されない場合は、KV の伝播を待ってから再試行してください。初回デプロイ後は、公開ホスト名が利用可能になるまで時間がかかる場合もあります。接続エラーをフォールバックレスポンスとして扱わないでください。
Dashboard で学習用アカウントを選択し、Storage & databases → Workers KV を開いて、この実験の labex-prefs-...-preferences namespace を見つけます。KV Pairs を選び、4 つのレコードを確認して、account:alice の横の View をクリックし、JSON 値をターミナル出力と比較します。この画面にはキーと値が表示されます。リビジョンのメタデータは、先ほどの Wrangler のキー一覧と API レスポンスで確認してください。一意な namespace 名と ID は例とは異なります。

公開エンドポイントは、合成された表示設定のデモにすぎません。実際の非公開設定サービスでは、どのアカウントキーにアクセスできるかを決める前に、リクエスト元を識別します。
破棄可能なクラウドリソースを削除する
このステップでは、Wrangler が認証された状態で 2 つのリソースを削除します。namespace は Worker より長く残る可能性があるため、アプリケーションだけを削除してもデータはクリーンアップされません。
このターミナルで起動したローカル開発プロセスを停止します。
kill "$DEV_PID"
削除する前に、保存されているリソース参照を確認します。
cat wrangler.jsonc
labex-prefs-... Worker 名と PREFERENCES namespace ID を確認します。この設定で選択されている Worker を削除します。
npx wrangler delete
確認を求められた場合は、表示された名前がこの実験のものと一致することを確認し、y で確定します。次に、PREFERENCES が参照している namespace だけを削除します。
npx wrangler kv namespace delete --binding PREFERENCES
確認プロンプトが表示されたら、受け入れる前に namespace を確認します。独立した確認処理で、削除対象だったリソースが存在しないことを特定できるよう、wrangler.jsonc はそのまま残してください。
npx wrangler kv namespace list
この実験の namespace が表示されず、関係のない namespace は残っていることを確認します。Dashboard の一覧を更新し、この実験の Worker と namespace が消えていることも確認します。リクエストの失敗やログインの期限切れだけでは、削除されたことを証明できません。ログアウトする前に、このステップの確認を実行して、認証済みのインベントリを調べられるようにしてください。
VM の認証を終了する
このステップでは、クリーンアップの確認に合格した後で Wrangler を切断します。ログアウトすると、この VM に保存された Wrangler の認証が終了しますが、クラウドリソースが削除されたり、通常の Dashboard のブラウザーセッションからログアウトしたりすることはありません。
npx wrangler logout
npx wrangler whoami --json
構造化された結果で "loggedIn": false と報告されることを確認します。この未認証のコマンドは、ここでは想定どおり、ゼロ以外の終了ステータスで終了する場合があります。接続エラーだけが表示されて認証状態が明示されない場合は、接続できる状態で再試行してください。
残っているローカルファイルとローカル KV の状態は、この破棄可能な VM に属するものです。すでに削除したクラウドリソースとは別のものです。これで実験を終了できます。
まとめ
Workers KV に構造化された設定とリビジョンメタデータを保存し、Worker の binding を通じて読み取りました。Alice と Bob の設定を分離し、欠落した値、形式が正しくない値、サポートされていない値には、理由を示したデフォルト値を返すようにしました。また、ストレージ障害とレコードの欠落を区別し、どちらも同じレスポンスで隠さないようにしました。
ローカルとクラウドのレスポンスを比較した後、破棄可能な Worker と namespace を削除し、ログアウトしました。次は、一時的な通知にアプリケーション上の期限と KV の有効期限を設定します。



