+ 作るログイン
1 / 14
Cloudflare のすべて ― 1機能ずつ、仕組みと存在理由から / 全35本

Cloudflare 23:D1 ― Workers のそばに置く SQL データベース

発行
アニメモ公式
版
第2版
改訂
コマ
全14

Cloudflare の SQL データベース D1 を、なぜ必要かから説明します。中身が SQLite であること、バインディングでの呼び出し、本体とレプリカ、Sessions API のしおり、wrangler によるマイグレーション、料金と大きさの上限、よくある落とし穴、Postgres + Hyperdrive との選び方まで。

Workers から、表でデータを持ちたい

Workers(18号)でアプリを作り始めると、すぐに「データをどこに置くか」で手が止まります。ログインした利用者の投稿を新しい順に20件出す、タグで絞り込む、今月の売上を合計する。こうした処理は、行と列の表に入れて SQL で問い合わせるのがいちばん素直です。

ところが、Workers のそばにある置き場のうち、KV(20号)は「鍵を渡すと値が返る」だけの仕組みです。絞り込みや並べ替え、集計は苦手で、自分で索引を作り込むことになります。

では、よそのクラウドにある普通のデータベースにつなげばよいかというと、ここにも壁があります。

困りごと中身
遠いWorker は利用者の近くで動くが、データベースは1か所にある
接続が重いデータベースへの接続は、最初の握手に何往復もかかる
準備が多いサーバーを立て、パスワードを管理し、バックアップを設定する

「SQL が使えて、準備が要らず、Workers からすぐ呼べる置き場」。この穴を埋めるのが D1 です。

※この号の内容は2026年9月時点の情報です。料金・上限・提供状況は変わることがあるので、使う前に必ず公式のドキュメントで確かめてください。

  • Cloudflare のすべて 23 ― D1
  • 1 / 14
  • Workers から、表でデータを持ちたい
  • KV:鍵と値だけ
  • ?
  • 利用者
  • Worker
  • 外部の DB:遠い・接続が重い
  • 世界中の拠点で動く
  • 「ユーザーごとの投稿を新しい順に20件」を KV でやるのは大変
  • SQL が使える置き場を、Workers のすぐそばに置きたい
  • 一覧・絞り込み・集計をしたい。でも、表を置く場所が近くに無い。

D1 は、Cloudflare が運用する SQLite

D1 は、Cloudflare が運用するサーバーレスの SQL データベースです。中身は、世界でいちばん広く使われているデータベースエンジンの SQLite です。スマートフォンのアプリやブラウザの中でも動いている、小さくて頑丈なデータベースです。

「サーバーレス」とは、データベースのサーバーを自分で用意しなくてよい、という意味です。台数を決めることも、OS を更新することも、接続数を気にすることもありません。コマンド1つでデータベースができ、使った分だけ数えられます。

SQLite なので、ふつうの SQL がそのまま使えます。

できること例
表を作るCREATE TABLE posts (...)
索引を張るCREATE INDEX ... で検索を速くする
結合・集計JOIN、GROUP BY、COUNT
まとめて実行複数の文を1回でまとめて成功・失敗させる(batch)

Worker から D1 を呼ぶときは、接続文字列もパスワードも使いません。次のページで見る「バインディング」でつなぎます。

  • Cloudflare のすべて 23 ― D1
  • 2 / 14
  • D1 は、Cloudflare が運用する SQLite
  • env.DB
  • 利用者
  • Worker
  • D1(本体)
  • 世界中の拠点で動く
  • SQLite のデータベース
  • Worker からは「バインディング」で直接呼ぶ。接続文字列もパスワードも無い
  • 中身は SQLite。表・索引・JOIN・トランザクション(batch)が使える
  • サーバーの用意も接続の管理も要らない。SQL がそのまま使える。

バインディング:コードからは env.DB で呼ぶ

Workers では、Cloudflare の他の機能を使うときにバインディングという仕組みを使います。設定ファイル(wrangler.jsonc または wrangler.toml)に「この名前で、このデータベースを使う」と書いておくと、Worker が動くときに、その名前のオブジェクトが env に入って渡されます。

図の右の設定では、binding に DB と書いています。すると、コードの中では env.DB として D1 を呼べます。名前は自由に付けられるので、データベースを2つ使うなら USERS_DB と LOGS_DB のように分けます。

この方式の良いところは、秘密の情報がコードにも設定にも出てこないことです。database_id はデータベースを指す番号にすぎず、それだけでは外から読み書きできません。「どの Worker がどのデータベースを使ってよいか」は、Cloudflare の側がアカウントの中で結び付けています。

TypeScript を使う場合は、wrangler types を実行すると、設定ファイルから env の型が作られます。env.DB を打ったときに、使えるメソッドが補完されるようになります。

  • Cloudflare のすべて 23 ― D1
  • 3 / 14
  • バインディング:コードからは env.DB で呼ぶ
  • // wrangler.jsonc
  • "d1_databases": [{
  • "binding": "DB",
  • env.DB
  • "database_name": "app",
  • 利用者
  • Worker
  • D1(本体)
  • "database_id": "…"
  • }]
  • 世界中の拠点で動く
  • SQLite のデータベース
  • binding に付けた名前が、そのまま env の名前になる(env.DB)
  • 鍵はコードに書かない。つなぐ先は Cloudflare 側が知っている
  • 設定ファイルに1行足すと、コードの env に DB が現れる。

本体は、1か所に住んでいる

D1 のデータベースは、作ったときに決まる1か所の拠点に本体が置かれます。場所は、ふつうは作った人の近く(または指定した地域)が選ばれます。

ここで大事なのは、Worker とデータベースの距離です。Worker は利用者のいちばん近くの拠点で動きます。東京の利用者なら東京で、ロンドンの利用者ならロンドンで動きます。ところが、データベースの本体は1か所なので、ロンドンの Worker から東京の D1 までは遠いのです。1回の問い合わせごとに、地球を半周して戻ってくることになります。

もう1つの性質は、1つのデータベースは、問い合わせを1つずつ順番に処理することです。SQLite の性質を受け継いでいます。1回の問い合わせが短ければ、1秒にたくさんさばけます。でも、索引の無い重い問い合わせが1つ混ざると、その間ほかの問い合わせは待たされます。

この2つから、D1 の設計の基本が見えてきます。

性質設計の工夫
本体が1か所読み取りの多いアプリは、写し(レプリカ)を使う
1つずつ処理問い合わせを短く保つ。索引を張る
1つずつ処理利用者や会社ごとにデータベースを分けて、処理を分散させる
  • Cloudflare のすべて 23 ― D1
  • 4 / 14
  • 本体は、1か所に住んでいる
  • 作った場所の近くに1つ
  • env.DB
  • 利用者
  • Worker
  • D1(本体)
  • 世界中の拠点で動く
  • SQLite のデータベース
  • 遠い国の利用者:Worker は近いが、D1 までは遠い
  • 1つのデータベースは1つの SQLite ファイル。本体は1か所で、同時に1つずつ処理する
  • 読み取りが多いなら、写し(レプリカ)を近くに置けば速くなる → 次のページ
  • 書き込みは必ず本体へ行く。遠い利用者ほど往復が長くなる。

読み取りレプリカを、世界に置く

多くの Web アプリは、書き込みより読み取りのほうがずっと多いものです。記事は1回書かれて、何千回も読まれます。そこで D1 には、読み取りレプリカ(読み取り専用の写し)を世界の各地に置く機能があります。

仕組みはこうです。書き込み(INSERT・UPDATE・DELETE)は、必ず本体に届きます。本体は、変更の内容を各地のレプリカに順に送ります。読み取り(SELECT)は、Worker のいちばん近くにあるレプリカが答えます。ロンドンの利用者の読み取りは、ロンドンに近いレプリカで完結するので、速くなります。

ただし、変更がレプリカに届くまでにはわずかな遅れがあります。この送り方を「非同期」と呼びます。このため、何も工夫しないと、「書いた直後に読んだら、まだ古い内容が返ってきた」ということが起こりえます。

この問題を解くのが、次のページの Sessions API です。レプリカを使うときは、コードを Sessions API で書くことになっています。

読み取りレプリカは、データベースごとに管理画面や API で有効にします。2025年に公開ベータとして登場した機能なので、今の提供状況と追加料金の有無は公式で確かめてください。

  • Cloudflare のすべて 23 ― D1
  • 5 / 14
  • 読み取りレプリカを、世界に置く
  • レプリカ(欧州)
  • env.DB
  • 利用者
  • Worker
  • D1(本体)
  • レプリカ(北米)
  • 世界中の拠点で動く
  • SQLite のデータベース
  • レプリカ(アジア)
  • 本体の変更は、少し遅れて各地のレプリカに届く(非同期)
  • データベースごとに有効にする。コードは Sessions API を使って書く
  • 読み取りは近くの写しへ。書き込みだけが本体へ行く。

Sessions API:自分が書いた内容を、確実に読む

Sessions API は、「一連の問い合わせ」をひとまとめ(セッション)にして、その中では時間が巻き戻らないことを保証する仕組みです。

鍵になるのがしおり(bookmark)です。しおりは「データベースのこの版まで見た」という印です。セッションの中で書き込むと、しおりが新しい版に進みます。次の読み取りは、しおりの版以降を持っているレプリカ(または本体)だけが答えます。だから、自分が書いた内容を読み落としません。

ts
// 前の応答で渡したしおりを受け取る(無ければ最初はどこでもよい)
const bookmark = request.headers.get("x-d1-bookmark") ?? "first-unconstrained";
const session = env.DB.withSession(bookmark);

const post = await session
  .prepare("SELECT * FROM posts WHERE id = ?")
  .bind(id)
  .first();

const res = Response.json(post);
// 次のリクエストでも続きから読めるように、しおりを返す
res.headers.set("x-d1-bookmark", session.getBookmark() ?? "");
return res;

withSession には、しおりのほかに "first-primary"(最初は必ず本体に聞く)も渡せます。確実に最新を読みたい処理はこちらを使います。しおりを応答で返し、次のリクエストで受け取ると、ページをまたいでも巻き戻りません。

  • Cloudflare のすべて 23 ― D1
  • 6 / 14
  • Sessions API:自分が書いた内容を、確実に読む
  • しおり
  • レプリカ(欧州)
  • env.DB
  • 利用者
  • Worker
  • D1(本体)
  • レプリカ(北米)
  • 世界中の拠点で動く
  • SQLite のデータベース
  • しおり=「この時点より新しい版を読んで」
  • レプリカ(アジア)
  • 書いた直後に読むと、まだ届いていない写しに当たることがある
  • withSession(しおり) で始めると、しおり以降の版を持つ所だけが答える
  • しおり(bookmark)を持ち回れば、古い写しを読まずに済む。

マイグレーション:表の形を、ファイルで管理する

アプリを育てていくと、表に列を足したり、索引を張ったりと、表の形(スキーマ)を変えることになります。これを本番のデータベースに手で打ち込むと、「手元と本番で形が違う」「誰がいつ変えたか分からない」という事故が起きます。

そこで D1 では、表の形の変更をマイグレーションという SQL ファイルに書いて残します。

npx wrangler d1 migrations create app add_tags
# migrations/0003_add_tags.sql ができるので、中に SQL を書く

npx wrangler d1 migrations apply app --local    # 手元で試す
npx wrangler d1 migrations apply app --remote   # 本番に当てる

ファイル名の先頭の番号の順に当てられます。どのファイルまで当てたかは、D1 の中の専用の表に記録されるので、同じファイルが2回当たることはありません。apply を実行すると、まだ当てていない分だけが走ります。

--local は、wrangler dev が手元で使う SQLite に当てます。本番の D1 に当てるには --remote を付けます。マイグレーションのファイルは Git に入れて、コードと一緒にレビューするのがおすすめです。

  • Cloudflare のすべて 23 ― D1
  • 7 / 14
  • マイグレーション:表の形を、ファイルで管理する
  • migrations/
  • 手元(--local)
  • 0001_create_posts.sql
  • wrangler dev の中の SQLite
  • 0002_add_index.sql
  • 0003_add_tags.sql
  • 本番(--remote)
  • Cloudflare 上の D1
  • 当てたファイルは D1 の中の表に記録され、2回は当たらない。未適用の分だけが走る
  • 手元で試してから本番へ。手で ALTER を打たない
  • SQL をファイルに残し、手元と本番に同じ順で当てる。

導入:作る → バインドする → 表を作る

D1 を使い始める手順は、次のとおりです。管理画面の「Storage & databases」から作ることもできますが、ここでは wrangler(34号)で進めます。

順番やることコマンドや場所
1データベースを作るnpx wrangler d1 create app
2表示された ID を設定に書くwrangler.jsonc の d1_databases
3表を作る SQL をマイグレーションに書くwrangler d1 migrations create
4手元で当てて試すmigrations apply app --local と wrangler dev
5本番に当てて公開するmigrations apply app --remote と wrangler deploy

設定ファイルは、たとえば次のようになります。

toml
# wrangler.toml の場合
[[d1_databases]]
binding = "DB"
database_name = "app"
database_id = "(d1 create で表示された ID)"

前提として、Workers のプロジェクトが1つ動いていることが必要です。D1 は Workers の無料プランでも使えるので、まずは無料で試せます。中身をのぞきたいときは、npx wrangler d1 execute app --remote --command "SELECT count(*) FROM posts" のように、コマンドから SQL を実行できます。

  • Cloudflare のすべて 23 ― D1
  • 8 / 14
  • 導入:作る → バインドする → 表を作る
  • D1 を作る
  • 設定に書く
  • 表を作る
  • 動かす
  • wrangler d1 create
  • ID が表示される
  • wrangler.jsonc
  • d1_databases にバインディングを足す
  • migrations apply
  • --local で試し、--remote で本番へ
  • Workers の無料プランでも使える。先に Workers(18号)を1つ動かしておくとよい
  • コマンド3つと設定1か所。管理画面からでも作れる。

使い方:prepare して bind し、all で受け取る

D1 の基本の呼び方は、「SQL を用意(prepare)して、値を差し込み(bind)、実行する」の3段です。

ts
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const author = new URL(request.url).searchParams.get("author") ?? "";

    const { results } = await env.DB
      .prepare("SELECT id, title FROM posts WHERE author = ? ORDER BY id DESC LIMIT 20")
      .bind(author)
      .all();

    return Response.json(results);
  },
};

? の場所に、bind で値が入ります。文字列をつなげて SQL を組み立てないでください。利用者の入力に SQL を混ぜ込まれる SQL インジェクションの原因になります。bind を使えば、値はあくまで値として扱われます。

実行のしかたは、欲しい結果に合わせて選びます。

メソッド返るもの向いている場面
all()全行の配列一覧
first()最初の1行(無ければ null)1件の詳細
run()変更した行数などINSERT・UPDATE・DELETE
batch([...])各文の結果の配列複数の文をまとめて

batch に並べた文は、全部成功するか、全部取り消されるかのどちらかです。「注文を入れて、在庫を減らす」のように、片方だけ成功しては困る処理に使います。

  • Cloudflare のすべて 23 ― D1
  • 9 / 14
  • 使い方:prepare して bind し、all で受け取る
  • const { results } = await env.DB
  • all()
  • 全行を配列で
  • .prepare("SELECT id, title FROM posts
  • WHERE author = ? LIMIT 20")
  • first()
  • 最初の1行だけ
  • .bind(author)
  • run()
  • INSERT・UPDATE
  • .all();
  • return Response.json(results);
  • batch()
  • まとめて1回で
  • ? に bind で値を入れると、SQL インジェクションを防げる
  • batch() に並べた文は、まとめて成功するか、まとめて失敗する(トランザクション)
  • 値は必ず bind で渡す。文字列を連結して SQL を作らない。

料金と制限:読んだ行・書いた行・保存量で数える

D1 の料金は、読んだ行の数、書いた行の数、保存しているデータの量で数えます。データを Worker に返す通信量そのものには課金されません。

項目Workers 無料プランWorkers 有料プラン
読んだ行1日 500万行まで月の込みの量が大きく、超えた分は従量
書いた行1日 10万行まで同上
保存量合計 5GB まで込みの量を超えた分は従量
1つのデータベースの大きさ500MB まで10GB まで

無料プランの1日の枠は、世界標準時(UTC)の0時、つまり日本時間の朝9時にリセットされます。枠を使い切ると、リセットまで問い合わせが失敗します。

注意したいのは、「読んだ行」は返した行の数ではなく、データベースが調べた行の数だという点です。10万行の表に索引が無いまま WHERE で1件を探すと、1件しか返さなくても10万行を読んだと数えられます。索引は、速さのためだけでなく、料金のためにも張ります。

1つのデータベースの大きさには上限があり、それ以上は大きくできません。大きくなりそうなら、利用者ごと・会社ごとにデータベースを分ける設計にします。

※2026年9月時点の情報です。料金・上限は変わるので、公式の Pricing と Limits のページで確かめてください。

  • Cloudflare のすべて 23 ― D1
  • 10 / 14
  • 料金と制限:読んだ行・書いた行・保存量で数える
  • 数え方
  • 読んだ行数・書いた行数・保存量
  • 無料(1日)
  • 読み 500万行・書き 10万行
  • 1つの大きさ
  • 無料 500MB / 有料 10GB まで
  • 有料プラン
  • 月の込みの量が大きく、超えた分は従量
  • 転送量
  • データの出入りそのものには課金しない
  • 「読んだ行」は返した行ではなく、走査した行。索引が無いと全件を読んで数える
  • 2026年9月時点。料金・上限は変わるので公式の Pricing / Limits で確かめる
  • 無料枠あり。1つのデータベースの大きさには上限がある。

よくある落とし穴

D1 でつまずきやすい点を並べます。どれも、手元の小さなデータでは気づかず、本番で初めて表に出ます。

落とし穴何が起きるかどうするか
索引が無い全件を走査して遅くなり、読んだ行も膨らむWHERE・ORDER BY に使う列に索引を張る。EXPLAIN QUERY PLAN で確かめる
1つに全部入れる大きさの上限に届き、処理も1か所に集中する利用者や会社ごとにデータベースを分ける
BEGIN TRANSACTION を書くD1 ではコードからの明示的なトランザクションは使えないbatch() にまとめる
--remote の付け忘れ手元にだけ表ができ、本番で「表が無い」マイグレーションは手元→本番の順で両方に当てる
書いた直後に読むレプリカを使うと古い内容が返ることがあるSessions API のしおりを使う
日時の扱いSQLite には専用の日時型が無いISO 形式の文字列か、数値(UNIX 時刻)に統一する

重い集計(ランキング・月次の合計など)を、リクエストのたびに計算するのも避けます。Cron Triggers(25号)で定期的に計算して別の表に書いておき、画面ではそれを読むだけにすると、速く、安くなります。

  • Cloudflare のすべて 23 ― D1
  • 11 / 14
  • よくある落とし穴
  • 索引を張らずに WHERE / ORDER BY → 全件走査で遅く、読み取り行数も膨らむ
  • 1つのデータベースに全部入れる → 上限に届く。利用者や会社ごとに分ける
  • コードで BEGIN TRANSACTION を書く → 使えない。batch() を使う
  • --remote を付け忘れ、手元にだけ表を作って本番で「表が無い」
  • 重い集計は、先に計算した結果を別の表や KV(20号)に置いておく
  • 索引・大きさ・BEGIN・--remote。どれも本番で初めて気づく。

D1 か、Postgres + Hyperdrive か

Cloudflare から SQL のデータベースを使う道は、大きく2つあります。D1 と、よそにある Postgres や MySQL に Hyperdrive(26号)でつなぐ道です。

比べる点D1Postgres + Hyperdrive
準備コマンド1つデータベースを別に用意する
データの大きさ1つあたり 10GB まで使う事業者の上限まで
機能SQLite の範囲Postgres の拡張機能や細かい権限も使える
分け方小さな DB をたくさん作るのが得意1つの大きな DB が得意
運用Cloudflare に任せる自分か、DB の事業者が担う

選び方の目安は、「データがもうどこかにあるか」です。すでに会社の Postgres があり、ほかのシステムもそれを使っているなら、データを動かさずに Hyperdrive でつなぐほうが自然です。これから新しく作る小さなアプリや、利用者ごとに独立したデータを持つアプリなら、D1 がいちばん手軽です。

途中で乗り換えることもできますが、SQL の方言(SQLite と Postgres の違い)の書き直しが要ります。最初に、データがどこまで大きくなりそうかを見積もっておきましょう。

  • Cloudflare のすべて 23 ― D1
  • 12 / 14
  • D1 か、Postgres + Hyperdrive か
  • D1
  • Postgres + Hyperdrive
  • Cloudflare の中で完結する SQLite
  • 今ある DB に、速くつなぐ(26号)
  • 準備ゼロ。無料で始められる
  • 既存のデータと道具をそのまま使える
  • 利用者ごとに DB を分ける設計に強い
  • 大きな DB・拡張機能・細かい権限
  • 1つあたり 10GB まで
  • DB の運用は自分(または別の事業者)
  • 迷ったら:データが既にどこかにあるなら Hyperdrive、無いなら D1 から
  • 新しく小さく作るなら D1。既存の DB や大きな DB は Hyperdrive。

他の号とのつながり

D1 は「行と列の表」の置き場です。Cloudflare にはほかにも置き場があり、得意なものが違います。組み合わせて使うのが普通です。

号機能D1 との分担
20KV設定値や、表から作った結果を、世界中で速く読むために置く
21Durable Objectsチャットやゲームの部屋のように、1つずつ順に処理したい状態
22R2画像・動画・PDF などの大きなファイル。D1 には置き場所のキーだけを入れる
24Queues重い書き込みを後回しにして、リクエストを速く返す
25Workflows と Cron定期的な集計や、何段もある処理
26Hyperdriveすでにある Postgres / MySQL を使うとき
34Wranglerd1 create・d1 migrations・d1 execute などのコマンド

特に R2 との組み合わせは定番です。利用者がアップロードした画像そのものは R2 に置き、D1 には「誰の、どの画像が、どのキーで R2 にあるか」という表を持ちます。大きなファイルをデータベースに入れないことで、D1 の大きさの上限にも届きにくくなります。

  • Cloudflare のすべて 23 ― D1
  • 13 / 14
  • 他の号とのつながり
  • 20 KV
  • 21 Durable Objects
  • 22 R2
  • 設定・キャッシュ
  • 1つずつ順に処理
  • 画像・大きなファイル
  • 24 Queues
  • 26 Hyperdrive
  • 34 Wrangler
  • 重い書き込みを後で
  • 既存の Postgres
  • d1 コマンド
  • D1 の中の表:投稿・ユーザー・注文のような「行と列」のデータ
  • ファイル本体は R2 に置き、D1 にはその置き場所(キー)だけを入れるのが定番
  • D1 は「表」の置き場。ほかの置き場と役割を分けて使う。

まとめ:Workers のそばに置く、SQL の表

この号で見たことをまとめます。

項目内容
なぜ要るのかWorkers から、準備なしで SQL の表を使いたい
正体Cloudflare が運用する SQLite
つなぎ方バインディング。コードからは env.DB
本体と写し本体は1か所。読み取りはレプリカで近くに
読み違えないSessions API のしおり(bookmark)
表の形マイグレーションのファイルで管理。--local → --remote
書き方prepare → bind → all / first / run、まとめるなら batch
料金読んだ行・書いた行・保存量。無料枠あり
上限1つあたりの大きさに上限。分けて使う
選び方既存の DB があるなら Hyperdrive(26号)

D1 は、「小さく始めて、たくさん作る」ことに向いたデータベースです。次の24号では、重い処理を後回しにする Queues を見ます。

  • Cloudflare のすべて 23 ― D1
  • 14 / 14
  • まとめ:Workers のそばに置く、SQL の表
  • レプリカ(欧州)
  • env.DB
  • 利用者
  • Worker
  • D1(本体)
  • レプリカ(北米)
  • 世界中の拠点で動く
  • SQLite のデータベース
  • レプリカ(アジア)
  • 無料で始められる SQLite。1つあたりの大きさに上限があるので、分けて使う
  • 既存の Postgres / MySQL があるなら、Hyperdrive(26号)を選ぶ
  • binding でつなぎ、migrations で育て、replicas で近づける。

0 件のコメント

読み込み中…

権利侵害・不適切な内容の連絡: [email protected]