データベース
プロジェクトごとに専用のデータベースを持てます。naro が作成し、アプリに env.DB としてバインドし、デプロイをまたいで保持する Cloudflare D1(SQLite)です。Cloudflare のトークンを扱う必要はありません — SQL は naro を経由し、誰が読めて誰が書けるかはチームのロールが決めます。
有効にする
naro db enable は、プロジェクトにデータベースが無ければ作成します。二度実行しても安全です。データベースに書き込めるロールなら誰でも実行でき、CLI やエージェントからの最初の書き込みが自動で有効にもします。
naro db enablenaro db infoSQL を実行する
SQL は引数、--file、またはパイプで渡します。読み取りには読み取り権限、書き込む文には書き込み権限が必要です。ひとつのリクエスト内の複数の文はひとつのトランザクションとして適用されます。三つ目が失敗すると、前の二つも取り消されます。
naro db query "select name from sqlite_master"naro db query --file seed.sqlマイグレーション
マイグレーションは ./migrations にある番号付きの .sql ファイルで、wrangler と同じ d1_migrations テーブルに記録されます。そのため二つのツールが互いの作業を再適用することはありません。naro db push は未適用分を一覧し、適用前に確認します。--yes は確認を省き、--dry-run は一覧だけです。マイグレーションのファイルも、全体が適用されるか全体が取り消されるかのどちらかです。失敗したファイルは記録されず、次の push がクリーンな状態からそのファイルを再実行します。 テーブルはあるのにマイグレーションが無いデータベースなら、naro db pull から始めます。現在のスキーマを migrations/0001_remote_schema.sql に書き出し、適用済みとして記録します。 マイグレーションには、そのファイルが作るテーブルの Data API ポリシーも書けます。同じファイルに Supabase と同じように CREATE POLICY を書くと、テーブルと一緒に全体が適用されるか全体が取り消されるかのどちらかになります(構文と例はバックエンドのドキュメントの「行レベルセキュリティ」にあります)。naro db pull は、データベースにすでにあるポリシーもそのベースラインファイルの末尾に書き出します。
naro migration new create_usersnaro db pushnaro migration listnaro db pullSQL を書かずにスキーマを変える
naro db table と naro db index はフラグでテーブルを作成・変更します。table は create、add-column、rename-column、rename、drop-column、drop、index は create と drop です。SQL は naro が書きます — 名前はすべて引用符で囲み、型とデフォルト値は検査します。それをひとつのマイグレーションとして適用し、d1_migrations に記録して同じ名前(NNNN_schema_…)のファイルとして ./migrations に書き出します。そのため naro migration list、db pull、naro db reset はほかのマイグレーションと同じように扱います。--dry-run は SQL を表示するだけで何も変更しません。--no-write は適用だけしてファイルを書かないので、naro db reset はその変更を適用し直せません。台帳にまだないローカルのマイグレーションファイルがあると変更は拒否されます。そのファイルより先に適用されてしまうので、先に naro db push を実行してください。drop と drop-column は実行前に確認し(--yes で省略)、削除したテーブルは naro db restore で戻せます。同じ変更は自作のツールからは POST /api/projects/{id}/database/schema で、エージェントからは 5 つの MCP ツールで行えます。
naro db table create posts --column "id:integer:pk:autoincrement" --column "title:text:notnull" --column "created_at:datetime:notnull:default=current_timestamp" --owner user_idnaro db table add-column posts --column "published:boolean:notnull:default=false"naro db index create posts user_id,created_atnaro db table drop-column posts publishednaro db table create drafts --column "id:integer:pk" --dry-run列は name:type の後に pk、autoincrement、notnull、unique、default=<値>、references=<table>.<column>、ondelete=<動作>(cascade、set-null、set-default、restrict、no-action)をコロンでつなげたものです。型は INTEGER, REAL, TEXT, BLOB, NUMERIC, BOOLEAN, DATETIME と JSON です。JSON は TEXT の列に入れてください — JSON の列は NUMERIC 親和性なので '123' を数値に変えて保存します。デフォルト値はリテラルです — default=0、default=true、default=null、default='draft'、default=current_timestamp。式は受け付けません。単独の INTEGER 以外の主キーはすべて NOT NULL になり、外部キーは親テーブルの主キーか UNIQUE の列を指す必要があります。
--owner user_id は user_id を TEXT NOT NULL として追加し、プロジェクトのバックエンドがオンで準備できていれば、テーブルのポリシーを select・insert・update・delete すべて owner にします。ポリシーのないテーブルは Data API に対して閉じています。anon とサインイン済みのリクエストはすべて拒否され、service キーと env.DB だけが届きます — テーブルを作ったときに naro がそう伝えます。マイグレーションの CREATE POLICY か naro policy set で開けます。ポリシーはテーブル名で保存されます。テーブルを削除すると、プロジェクトにバックエンドがあれば(オンでもオフでも)そのポリシーも削除し、naro がそう伝えます。名前を変えるとポリシーは古い名前に残り、どのテーブルにも適用されなくなります。移すために何を実行すればよいかは naro が伝えます。ある名前に残ったポリシーは、naro db table でその名前のテーブルを作り直すか、その名前に変更したとき(元の名前に戻す場合も含む)に先に削除され、naro は削除したポリシーを naro policy create で戻せる CREATE POLICY 文として表示します。naro db push や naro db query で作ったテーブルも、残ったポリシーを引き継がず、そこで DROP TABLE をすると Postgres と同じようにそのテーブルのポリシーも削除されます。
SQLite にはできないことがあり、naro は試す前にそう伝えます。既存のテーブルに PRIMARY KEY や UNIQUE の列、デフォルト値のない NOT NULL の列、CURRENT_TIMESTAMP をデフォルト値にした列は追加できません — UNIQUE の列の代わりに unique インデックスを追加してください。主キー、UNIQUE の列、インデックスが使う列、テーブルの唯一の列は削除できないので、先にインデックスを削除します。ほかのテーブルの外部キーが指しているテーブルも削除できず、トリガーやビューが使うテーブル・列は削除も名前の変更もできません — naro はその SQL を読んで邪魔になっているものを名前で伝え、SQL から確実に判断できないときは拒否せず警告します。先にそのトリガーやビューをマイグレーションで削除するか作り直してください。列の型や制約を変えるには新しいテーブルが必要です。naro migration new でマイグレーションを作り、新しいテーブルを作成して INSERT … SELECT で行を移し、古いテーブルを削除して新しいテーブルの名前を変え、naro db push で適用します。
TypeScript の型
naro gen types はスキーマを読み、テーブルとビューごとの Row インターフェース、テーブルごとに Row・Insert・Update を持つ Database 型(naro-js の createClient<Database>() が受け取るもの — Backend を参照)、DB バインディングを持つ Env を出力します。NOT NULL か、テーブルの INTEGER PRIMARY KEY でない限り、そのカラムは null になり得ます。BLOB は D1 が返すとおり number[] です。
naro gen types --output src/database.types.tsバックアップ
naro db dump は Cloudflare に SQL ダンプを依頼し、--output のファイルか stdout にストリーミングで書き出します。スキーマとすべての行、マイグレーションの記録まで含まれます。--schema-only と --data-only で絞り込め、--table はカンマ区切りで複数指定できます。エクスポート中、Cloudflare はこのデータベースへの他のクエリをすべて止めるので、本番のトラフィックが待たされます — 大きなダンプは空いている時間帯に取ってください。仮想テーブル(FTS5)があると、Cloudflare は全体とスキーマのダンプを拒否します。ほかのテーブルは --table で選べばエクスポートできます。ダンプの最後にその時点のブックマークが表示されます。復元ポイントなので控えておいてください。
naro db dump --output backup.sqlnaro db dump --schema-only --output schema.sql過去に戻す
Cloudflare はすべてのデータベースの履歴を 30 日間保持しています(Time Travel)。naro db restore に --timestamp(タイムゾーン付きの RFC 3339、または Unix 秒)か --bookmark を渡すと、その時点より後の書き込みをすべて巻き戻します。行やテーブルだけでなくマイグレーションの履歴も巻き戻り、その瞬間に実行中だったクエリはキャンセルされます。実行前に確認し、--dry-run は戻り先を表示するだけです。復元すると直前のブックマークが表示され、そのブックマークに復元し直せば今の復元を取り消せます。
naro db restore --timestamp 2026-09-21T14:35:22Z --dry-runnaro db restore --timestamp 2026-09-21T14:35:22Zマイグレーションファイルの状態に戻す
naro db reset はプロジェクトが持つテーブル・ビュー・トリガーをすべて削除し、d1_migrations の記録を空にしてから、ローカルのマイグレーションファイルを最初から適用し直します。つまりデータベースはそれらのファイルが示す状態になります。データベース自体は削除しないので、アプリは同じ env.DB を読み続けます。実行前に確認し、その前に削除直前の Time Travel ブックマークを表示します。naro db restore --bookmark <その値> でデータは元に戻ります。Data API のポリシーもスキーマと一緒に空にされ、マイグレーションファイルがもう一度作ります。そのため、データベースにしかないポリシーは失われます。確認の前に、ファイルが作らないポリシーを、それを作り直す SQL(CREATE POLICY と、owner ルールならそのカラムを残す COMMENT ON POLICY)と一緒に一覧するので、残したいものは先にその SQL をマイグレーションに加えてください(--json では policiesNotInFiles[].sql)。読めないポリシーの行は SQL の代わりに -- Policy … cannot be read というコメントで表示されます。書き写しても何も残らないので、そのポリシーを削除して作り直してください。アカウント、セッション、その他のバックエンドのテーブルはそのままです。ワーカーが名前付きポリシーより古いバックエンドは、naro backend enable で更新するまで 409 BACKEND_OUTDATED で拒否されます。--dry-run は何を削除し何を適用し直すかを見せるだけで、何も変更しません。ディレクトリにマイグレーションファイルがひとつもなければ、reset は空のデータベースを残し、そのことを伝えます。
naro db reset --dry-runnaro db resetスキーマの点検
naro db lint は D1 が実際につまずくものだけを報告します。消えた親行を指している行、失敗した quick_check、主キーのないテーブル、INTEGER でないのに NULL を受け付ける主キー、どのインデックスの先頭列でもない外部キー、REST API では作り直せないトリガー、db dump に全体のダンプを拒否させる仮想テーブル。error があれば終了コード 1、warn だけなら 0 なので CI のゲートに使えます。--strict は warn も失敗にし、--json は結果をデータで返します。本番に向けて実行する前に知っておくことが 2 つあります。何も変更しないのに書き込み権限が必要です — 2 つの検査(foreign_key_check、quick_check)はデータベース全体のスキャンで、naro はそれを意図的に書き込みとして分類しています。読み取り専用のロールが全体スキャンを始められないようにするためです。そしてそのスキャンは実際の作業です。外部キーを持つ行すべてと、quick_check はファイル全体を走査するので、大きなデータベースでは読み取り行が課金され、30 秒のクエリ上限に届くことがあります。
naro db lintnaro db lint --strict --json記録がずれたとき
naro migration repair <名前> --status applied は SQL を実行せずにそのマイグレーションを適用済みとして記録し、--status reverted はその行を削除します。流れに空いた穴のための命令です — マイグレーションファイルは実行されたのに、記録の行を書く前にリクエストがタイムアウトすると、次の push が同じマイグレーションをもう一度適用しようとします。repair は d1_migrations 以外には触れず、記録がすでに指示どおりであればそう伝えて何も書きません。
naro migration repair 0001_init.sql --status appliednaro migration repair 0001_init.sql --status revertedAI エージェントから
MCP サーバーも同じ機能を公開します。enable_database、get_database、list_tables、execute_sql、apply_migration、create_table、alter_table、drop_table、create_index、drop_index、list_migrations、generate_typescript_types、export_database、restore_database。reset・lint・repair はあえてツールにしていません。スキーマをまるごと消すことや記録を書き換えることは execute_sql より危険で、どちらも人が答える確認の後ろにあるべきだからです。結果は信頼できないデータとして返されます — 行にはあなたのユーザーが書いた文章が含まれ得るので、エージェントはそれを指示として扱ってはいけません。
読み取り専用モード
サーバーを --read-only で起動すると、何かを変更するツールはすべて消えます。execute_sql は残りますが、naro は単一の読み取り文以外を拒否します。--features database はサーバーをデータベースのツールだけに絞ります。
claude mcp add naro-db -- npx -y naro-mcp --read-only --features database1 日の書き込み上限
プロジェクトのデータベースが書き込める行数には UTC の 1 日ごとの上限があります。Hobby は 100,000 行、Pro は 1,000,000 行、Enterprise は 10,000,000 行です。naro は 15 分ごとに Cloudflare の分析を読み、アプリ自身の env.DB を含むすべての書き込みを数えます。naro db info で今日の書き込み量を上限と並べて確認できます。上限に達すると、00:00 UTC まで naro を経由する書き込みは拒否されます。書き込む文を含む naro db query と naro db push は 429 DATABASE_WRITE_QUOTA_EXCEEDED を、バックエンドがあれば Data API の insert・update・delete とサインアップは 429 WRITE_QUOTA_EXCEEDED を返します。読み取りは naro db lint を含めて引き続き使え、restore・dump・pull も使えます。アプリが env.DB で直接書き込む分は数えますが止めません — naro がそれを止めるには読み取りまで止めるしかないからです。集計は数分遅れて届くため、暴走したループは上限を超えてから最大 20 分ほど書き込み続けた後に止まります。
上限
| 上限 | 値 |
|---|---|
| データベースのサイズ | 10 GB で、Cloudflare は引き上げません。naro db info は 8 GB から警告し、10 GB に達するとデータを削除するまですべての書き込みが 507 DATABASE_FULL で失敗します。 |
| UTC の 1 日に書き込める行 | Hobby は 100,000、Pro は 1,000,000、Enterprise は 10,000,000。超えると naro を経由する書き込みは 00:00 UTC まで止まります — 上の「1 日の書き込み上限」を参照。 |
| 文ひとつ | 100 KB、バインドパラメータは 100 個まで |
| リクエストひとつ | 50 文。マイグレーションファイルは 500 文、1 MB まで |
| クエリひとつ | 30 秒 |
| 結果ひとつ | 10,000 行 — 残りは切られます |
10 GB に達する前に、不要なデータを削除するか、大きなバイナリをストレージ(R2)に移すか、アプリ側でデータを複数のデータベースに分けてください。D1 はクエリを一度にひとつずつ実行するので、遅いクエリがあると本番のトラフィックが待たされます。本番とプレビューは同じデータベースを共有します。ブランチはありません。