← Documents
Ops · Workers + D1 · Template

テンプレートから新しい Webアプリを作る
——Cloudflare Workers + D1、10手順

動いている Workers + D1 のリポジトリをテンプレートとして複製し、新しいアプリを作る手順です。2026年8月8日にこの手順で実際にデプロイまで通しています。
順番に依存関係があります。入れ替えると失敗するか、事故ります。

ローカルの Node:不要 場所:ブラウザ / Claude Code / 人 落とし穴:5件

0最初に読む——この手順で一番危険なこと

複製元の本番 D1 に接続された Worker が、エラーを一切出さずに公開されることがあります。

理由は3つが重なるためです。

#理由効果
1Workers Builds は接続した時点で main をビルドしてデプロイする作業ブランチに正しい値を置いていても、main が複製直後のままならその main が公開される
2D1 のバインディングは database_name ではなく database_id で解決されるdatabase_name を新しい名前に直しても、database_id が古ければ古い DB に繋がる
3その状態でエラーは出ないアプリは完全に正常動作する。書き込みも通る
つまり 「動いている」ことは、正しい DB に繋がっている証拠になりません。

手順6(接続前に main へマージ)と 手順8(バインディングの目視確認)は、どちらもこの事故を防ぐためだけにあります。飛ばさないでください。

1前提と検証済みの組み合わせ

2026年8月8日に、この組み合わせで実際にデプロイまで通しています。

項目バージョン確認したこと
Node22.nvmrc20 だとデプロイ段で落ちる(→ 落とし穴2
wrangler4.120.0deploy --dry-run が認証情報なしで完走する
@cloudflare/workers-types5.x^4 だと wrangler 4系の peer 依存と衝突して npm ci が落ちる
typescript5.xtsc --noEmit が通る
このリポジトリ自体が動いているアプリです。テンプレート専用の空リポジトリではありません。複製したあとに前のアプリの値を消す作業が要ります。それを機械化したのが bin/reset-to-template.mjs です。

各ステップの「場所」は、ブラウザでやるか、Claude Code に頼むか、人が決めるかの区別です。

2手順1–3

1. リポジトリを複製する — ブラウザ

GitHub で Use this template(テンプレート設定済みの場合)または Fork / 複製して、新しいリポジトリを作る。

テンプレートリポジトリとして使うには、複製元の Settings → General → Template repository にチェックを入れておく。以後は Use this template から履歴なしの新規リポジトリを作れる。

2. 作るものを決める — 人

Claude Code に推測させない項目です。先に決めて渡します。

項目用途制約
アプリ名wrangler.jsoncname*.workers.dev のサブドメイン小文字の英数字とハイフンのみ。大文字を含むと wrangler が弾く
何をするアプリかREADME.md / 画面の見出し / src/index.ts の書き換え方針
D1 データベース名wrangler.jsoncdatabase_name手順4で作る名前と一致させる
公開ドメインroutes。指定なしなら <アプリ名>.<サブドメイン>.workers.dev独自ドメインは Cloudflare のゾーン配下に置く必要あり
アプリの中身が決まっていないなら、雛形のまま先に進めてください。接続の問題とアプリの問題を同時に切り分けるのは大変です。動くことが分かっている雛形で経路を確定させてから中身を作るほうが早く終わります。

3. 前のアプリの値を消す — Claude Code

複製先のリポジトリで Claude Code に 「テンプレートをリセットして」 と伝える。

node bin/reset-to-template.mjs          # 何が変わるかと残存を表示するだけ
node bin/reset-to-template.mjs --write  # 実際に書き換える

機械が戻すもの

ファイル戻る値
wrangler.jsoncname / database_name / database_idchange-me
package.jsonname / description → 雛形の値
docs/SETUP.md削除(複製元の環境の記録なので)

機械が「報告するだけ」のもの

スクリプトは書き換え前の値を覚えておき、リポジトリ全体を走査して残っている箇所を file:line で報告します。設定ファイルは直せても、文章の中の値は機械に判断できないためです。残存があると終了コード 1 を返します。

実際に残るのは、たとえば次のような箇所です。すべて潰してから次に進んでください。

人が決めて Claude Code が書き換えるもの

schema.sql次の手順4で実行するので、ここで確定させておきます。

3手順4–6

4. D1 を作り、その場で schema.sql を実行する — ブラウザ

この2つは続けて行います。分けると、テーブルの無い DB にデプロイして 500 になります。
  1. ダッシュボード → ストレージとデータベース → D1 SQLite データベース → 作成
    (データ ロケーションは「位置情報」=自動配置のままでよい)
  2. 同じ画面の「コンソール」タブschema.sql の中身を貼って実行
  3. 「概要」タブのテーブル数が 1 以上になっていることを確認 ← ここが確認の目印
  4. 「概要」タブの Database ID をコピー
Worker より先に D1 を作ります。wrangler deploy は実行時に database_id を Cloudflare に問い合わせて解決するので、D1 が存在しないと最初のデプロイが成功せず、Worker 自体が作られません。

Worker は手で作るものではなく、最初のデプロイ成功時に生まれます。

5. wrangler.jsonc に実値を入れる — Claude Code

手順2と手順4で決まった値を渡す。change-me は3か所。

database_id について必ず理解しておくこと』

D1 のバインディングは database_name ではなく database_id で解決されます。database_nameダッシュボード表示用のラベルにすぎません。

したがって database_name を新しい名前に直しても、database_id が複製元のままなら複製元の DB に繋がります。しかもエラーは一切出ません。書き込み経路のあるアプリなので、気づかないまま別アプリの本番 DB にデータを書き込めてしまいます。

database_id を書き換えたことを、値を目で見て確認してください。
grep -rn 'change-me' wrangler.jsonc   # 何も出ないこと
npm install                           # package-lock.json の name を同期
npm run check                         # 通ること
wrangler.jsoncname は、手順7でダッシュボードに入力する Worker 名と一致させてください。食い違った状態でも動いてしまうことがありますが、どちらが優先されるかは検証していません(→8章)。一致していないと「どの設定でデプロイされたのか」の切り分けが極端に難しくなります。

6. main にマージする — Claude Code

接続より前にここをやります。理由は次の手順7の警告のとおりです。

作業ブランチで開発 → npm run check が通ることを確認 → main へマージ。

4手順7–10

7. リポジトリを Workers Builds に接続する — ブラウザ

⚠ 接続する前に、main が新しいアプリの値になっていることを確認する

Workers Builds は接続した時点で main をビルドしてデプロイします。

main が複製直後のままだと、複製元の database_id がそのままバインドされ、複製元の本番 D1 に接続された Worker が公開されます。エラーは出ません。画面も正常に動きます。

リセットと値の設定を作業ブランチに置いたままにせず、接続前に main へマージしておくこと(手順6)。

コンピュート → Workers & Pages からリポジトリをインポートする。

項目
Worker 名wrangler.jsoncname と同じ値にする
Build commandnpm ci
Deploy commandnpx wrangler deploy
ルート ディレクトリ/
Production branchmain
ビルド出力ディレクトリ空のまま(静的アセット用の項目。Worker アプリでは使わない)

GitHub App の認可では Only select repositories を選び、対象リポジトリだけに絞る。

8. バインディングを目視で確認する — ブラウザ【必須】

デプロイ成功だけでは、正しい DB に繋がっている証拠になりません。

Workers & Pages → 対象の Worker → 「バインディング」タブを開き、DB の値が手順4で作った新しい D1 の名前になっていることを確認する。

表示意味
新しく作った D1 の名前正しい
複製元の D1 の名前maindatabase_id が古い。直して再デプロイし、この画面が変わるまで確認する
直したのにこの表示が変わらないときは、ビルドが失敗している可能性を先に疑ってください。ビルドが失敗すると、本番は「最後に成功したデプロイ」を配信し続けます(エラー画面にはならない)。つまり古いバインディングのまま動き続けます。Cloudflare のビルドログを確認すること。

9. 読み書きを確認する — ブラウザ

https://<アプリ名>.<サブドメイン>.workers.dev を開く。

500 になる場合は、手順4の schema.sql 実行を飛ばしてテーブルが無い可能性が高い。

10. main を保護する — ブラウザ

GitHub の Settings → Branchesmain を保護し、build チェックの通過を必須にする。

5落とし穴(5件)

実際に踏んだものだけを挙げます。どれもログや画面を素直に読むと原因が分かりにくい失敗です。

落とし穴1 — 複製元の本番 D1 に接続された Worker が公開される【最も危険】

症状:なし。アプリは正常に動く。ダッシュボードのバインディング欄を見に行かない限り気づけない。

原因:main が複製直後のまま Workers Builds に接続した。接続した瞬間にその main がビルド・デプロイされ、複製元の database_id がバインドされた。

database_name を新しい名前に直してあっても防げません。バインディングは database_id で解決されるためです。

対処:手順6(接続前に main へマージ)と手順8(バインディングの目視確認)。

気づく方法はバインディングタブだけです。動作確認では絶対に検出できません。

落とし穴2 — Node のバージョン(デプロイ段だけが落ちる)

npm warn EBADENGINE package: 'wrangler@4.120.0',
npm warn EBADENGINE required: { node: '>=22.0.0' },
npm warn EBADENGINE current: { node: 'v20.20.2' }

症状:「ビルド中」まで緑で、「デプロイ中」だけが数秒で赤くなる。

原因:wrangler 4.120.0 は Node 22 以上を要求する。.nvmrc20 だと Cloudflare のビルド環境が Node 20 を入れる。インストールは EBADENGINE 警告で通ってしまうので、ビルドが成功しているように見える。

対処:.nvmrc22package.jsonengines.node>=22 に。このリポジトリでは修正済みなので、複製すれば踏みません。

教訓:npm run check が通ってもビルド環境の Node が違えば意味がない。

落とし穴3 — assets.directory エラー

[ERROR] The directory specified by the "assets.directory" field in your ...
   /opt/buildhome/repo/dist

症状:クローン・インストール・ビルドは緑なのに、デプロイだけ失敗する。

原因:assets.directory静的サイト版の wrangler.jsonc にしかない設定。main が古い構成のままの状態で接続すると、そちらを読んでしまう。

対処:main に正しい構成をマージする(手順6)。落とし穴1と同じ根っこです。

落とし穴4 — refusing to merge unrelated histories

Claude Code のクラウド実行環境で、ローカルの mainorigin/main と無関係な履歴になっていることがある。

git checkout -B main origin/main
git merge --ff-only <作業ブランチ>

落とし穴5 — @cloudflare/workers-types のメジャー

^4 を指定すると wrangler 4系の peer 依存と衝突して npm ci が落ちます。^5 のままにしてください。

6ビルドが失敗したときの読み方

ビルドログは5段階(初期化 / クローン / インストール / ビルド / デプロイ)に分かれます。どこが赤いかで原因の範囲が決まります。

赤い段階疑うもの
インストールpackage-lock.json の不整合、依存の解決、workers-types のメジャー
ビルドBuild command そのもの
デプロイwrangler.jsonc の内容、Node のバージョン、D1 の実在
ビルドが失敗すると、本番は「最後に成功したデプロイ」を配信し続けます(エラー画面にはならない)。「push したのに変わらない」「直したのにバインディングが変わらない」ときは、まず Cloudflare のビルドログを見ること。

7手元の検証で分かることの限界

npm run checktsc --noEmitwrangler deploy --dry-run です。--dry-run は認証情報なしで動く代わりに、Cloudflare に問い合わせません。

npm run check で分かる分からない
型エラーdatabase_id が実在するか
wrangler.jsonc の書式(Worker 名の規則違反など)その database_id が正しい DB のものか
main が指すファイルの実在D1 にテーブルがあるか
バインディングの「形」が解決することビルド環境の Node で動くか
右の列は本番デプロイと、ダッシュボードの目視でしか確かめられません。特に2行目は、この手順書で最も事故につながった箇所です。

8確かめていないこと

書いていないことは、確認できていないことです。

ダッシュボードのインポート時に指定した Worker 名と、wrangler.jsoncname が食い違った場合にどちらが優先されるか。食い違いが起きうることは確認しましたが、優先順位は検証していません。

一致させて、この問いに答えなくて済むようにしてください。

9最初に設計すること

雛形の src/index.ts には POST /api/items があり、これは誰でも書き込めます。経路確認の段階では問題ありませんが、URL が知られた状態で放置するとスパムを入れられます。

アプリの中身を書き換えるときに、必ず一緒に決めてください。

「とりあえず公開して後で塞ぐ」はしない、というのが CLAUDE.md の約束です。

あわせて CLAUDE.md の「セキュリティ上の約束」も読んでください。要点は3つです。

SQL に値を入れるときは必ず .bind() —— 文字列連結で SQL を組み立てない
HTML に値を差し込むときは必ず esc() —— テンプレートリテラルへの直挿しは XSS
入力は使う前に検証する —— await req.json() の戻り値は unknown として扱う

10時刻は UTC で保存される

雛形の schema.sqlcreated_at の既定値に datetime('now') を使っています。SQLite の datetime('now') は UTC を返すので、保存される時刻は UTC です。日本時間の表示と9時間ずれますが、これは仕様であってバグではありません。

保存を JST に変えるのは勧めません。保存は UTC のまま、表示側で変換するのが定石です。タイムゾーンをまたぐ比較や、あとから表示先を変えるときに破綻しません。

11ファイルの役割

ファイル役割
CLAUDE.mdClaude Code への常設指示。運用ルールとセキュリティの約束
docs/TEMPLATE.md複製して新しいアプリを作る手順(本記事の元
docs/SETUP.mdその環境の実績記録(複製時は削除される
bin/reset-to-template.mjs複製直後に固有値を change-me に戻し、残存を報告する
src/index.tsWorker の入口
schema.sqlD1 のテーブル定義。ダッシュボードのコンソールで実行する
wrangler.jsoncWorkers の設定(name / main / d1_databases / routes
.nvmrcNode のバージョン。22 以上が必要
.github/workflows/CI(型チェック+バンドル検証)

12テンプレートを更新するとき

リポジトリを改良したら、「落とし穴」に追記してください。次に複製する人が同じところで詰まらないための唯一の仕組みです。

バージョンを上げたら「この構成が検証済みなこと」の表も、実際に通してから更新すること。確認していないことは「確かめていないこと」に書き、断定しないこと。

S情報ソース

2026年8月時点。本記事は、実際に構築した際の手順書(docs/TEMPLATE.md)をそのまま記事化したものです。

  • 【一次記録・実施者による】 手順10ステップ・落とし穴5件・ビルドログの段階別の切り分け・ダッシュボードの画面名・エラーメッセージ・バージョンの組み合わせは、2026年8月8日にこの手順で実際にデプロイまで通した際の記録に基づきます。
  • 【一次確認できていない】 「D1 のバインディングは database_name ではなく database_id で解決される」という挙動を、Cloudflare の公式ドキュメントでは裏取りできていません。本記事の執筆環境から developers.cloudflare.com への接続が遮断されており、原典に当たれませんでした。実地の観測に基づく記述です。
  • 【確認していないこと】 ダッシュボードで入力する Worker 名と wrangler.jsoncname が食い違ったときの優先順位(→8章)。またダッシュボードの画面名は日本語表示での記録であり、表示言語や UI 更新で変わりえます。

H更新履歴

日付内容
2026-08-08初版公開。Workers + D1 のテンプレートから新しいアプリを作る手順書(docs/TEMPLATE.md)を記事化した。この手順で実際にデプロイまで通した後の版であり、踏んだ落とし穴5件が反映されている。

関連ドキュメント