cloudflare-d1 について
Cloudflare D1 を利用する開発者向けのスキルです。Workers から使うサーバーレス SQLite の設定、クエリ、マイグレーションに加え、バッチ処理や読み取りレプリケーションのパターンを解説します。
主な機能
- 01データベースとバインディングの設定
- 02プリペアドステートメントとバッチクエリ
- 03スキーママイグレーションのガイド
- 04読み取りレプリケーションのパターン
- 05SQLite の型とクエリの実践
活用シーン
- 01Worker 用の D1 データベース作成
- 02スキーママイグレーションの適用とテスト
- 03データベース操作のアトミックな一括実行
- 04レプリカによるグローバル読み取りの提供
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ secondsky/ claude-skills --skill cloudflare-d1インストール後、Claude Code で/cloudflare-d1を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/2025-features.mdbest-practices.mdlimits.mdmetrics-analytics.mdquery-patterns.mdread-replication.mdsetup-guide.mdtemplates/d1-setup-migration.shd1-worker-queries.tsschema-example.sql詳細
Cloudflare D1 Database
Status: 本番環境対応 ✅ | Last Verified: 2025-01-15
目次
- D1 とは?
- クイックスタート
- 重要なルール
- D1 API メソッド
- 主なユースケース 5 選
- マイグレーションのベストプラクティス
- 一般的なパターン
- SQLite の型アフィニティ
- 防止できる主なエラー 5 選
D1 とは?
Cloudflare D1 は、エッジ上で動作するサーバーレス SQLiteです。
- サーバー不要の SQL データベース
- グローバルに分散
- コールドスタートなし
- 標準的な SQLite 構文
- グローバルなパフォーマンスを実現する読み取りレプリケーション
🆕 2025 年の新機能
D1 は 2025 年を通じて大幅にアップデートされました。
パフォーマンス(2025 年 1 月)
- グローバルでレイテンシを 40~60%削減(P50 クエリ時間)
- エッジ実行向けに SQLite エンジンを最適化
- 100 MB 未満のデータベースでコールドスタートの影響を軽減
信頼性(2025 年 9 月)
- クエリの自動再試行:一時的な障害が発生した場合、読み取りクエリを最大 2 回再試行
- アプリケーションコードからは透過的に処理(
wrangler tailに記録)
スケーラビリティ(2025 年 4 月)
- 読み取りレプリケーション(パブリックベータ):読み取りレプリカをグローバルにデプロイ
- 読み取り負荷の高いワークロードで、読み取りスループットを最大 2 倍に向上
- 読み書きを分離する Sessions API
コンプライアンス(2025 年 11 月)
- データのローカライゼーション:GDPR/データ主権のため、EU/US の管轄区域を指定
--jurisdictionフラグまたは wrangler.jsonc で設定
⚠️ 破壊的変更(2025 年 2 月 10 日)
- 無料プランの上限を強制適用:データベース 10 個、各 500 MB、呼び出しごとに 50 クエリ
- 上限を超えると 429 エラーが発生(以前は警告のみ)
- 対応:
wrangler d1 listで使用状況を確認し、必要に応じてプランをアップグレードしてください
詳細:references/2025-features.md を読み込んでください。
クイックスタート(5 分)
1. データベースを作成
bunx wrangler d1 create my-database
出力された database_id を保存してください!
2. バインディングを設定
wrangler.jsonc に追加します。
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"d1_databases": [
{
"binding": "DB", // env.DB
"database_name": "my-database",
"database_id": "<UUID>",
"preview_database_id": "local-db"
}
]
}
3. マイグレーションを作成
bunx wrangler d1 migrations create my-database create_users
migrations/0001_create_users.sql を編集します。
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
username TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
PRAGMA optimize;
4. マイグレーションを適用
# Local
bunx wrangler d1 migrations apply my-database --local
# Production
bunx wrangler d1 migrations apply my-database --remote
5. Worker からクエリを実行
import { Hono } from 'hono';
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/users/:email', async (c) => {
const { results } = await c.env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(c.req.param('email'))
.all();
return c.json(results);
});
export default app;
手順の全容については、references/setup-guide.md を読み込んでください。
重要なルール
必ず行うこと ✅
.bind()を使ったプリペアドステートメントを使用する(文字列連結は絶対にしない)- WHERE/JOIN/ORDER BY の列にインデックスを作成する
- スキーマ変更にはマイグレーションを使用する(手動の SQL は絶対に使用しない)
- 複数の操作にはクエリをバッチ処理する(.batch())
- スキーマ変更後に PRAGMA optimize を実行する
- エラーを明示的に処理する(try/catch)
- タイムスタンプには INTEGER を使用する(Date.now())
- マイグレーションをデプロイする前にローカルでテストする
- グローバルな読み取りパフォーマンスには読み取りレプリカを使用する
- SQL クエリの前に入力を検証する
絶対にしてはいけないこと ❌
- ユーザー入力を SQL に連結しない
- database_id を公開リポジトリにコミットしない
- スキーマ変更でマイグレーションを省略しない
- VARCHAR を使用しない(代わりに TEXT を使用)
- フィルター対象の列にインデックスを付け忘れない
- SQLite の型アフィニティのルールを無視しない
- LIMIT を指定せずに SELECT * を使用しない
- ローカルでテストせずにマイグレーションを実行しない
- 1 行あたり 1 MB を超えない
- DATETIME を使用しない(タイムスタンプには INTEGER を使用)
D1 API メソッド
prepare() - クエリを実行
// Single result
const { results } = await env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
// First result only
const user = await env.DB.prepare(
'SELECT * FROM users WHERE user_id = ?'
)
.bind(userId)
.first();
// Raw results (faster)
const { results } = await env.DB.prepare(
'SELECT username FROM users'
)
.raw(); // Returns arrays instead of objects
batch() - 複数のクエリ
const results = await env.DB.batch([
env.DB.prepare('INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)')
.bind('[email protected]', 'user1', Date.now()),
env.DB.prepare('INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)')
.bind('[email protected]', 'user2', Date.now()),
env.DB.prepare('SELECT COUNT(*) as count FROM users')
]);
console.log('Users count:', results[2].results[0].count);
すべてのクエリは単一のトランザクションで実行されます(すべて成功するか、すべて失敗します)。
exec() - SQL 文字列を実行
// For migrations/setup only
await env.DB.exec(`
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
email TEXT NOT NULL
);
CREATE INDEX idx_email ON users(email);
`);
ユーザー入力を含むクエリには絶対に使用しないでください!
API の全リファレンスについては、references/query-patterns.md を読み込んでください。
主なユースケース 5 選
ユースケース 1:ユーザーの CRUD
// Create
app.post('/users', async (c) => {
const { email, username } = await c.req.json();
const { results } = await c.env.DB.prepare(
'INSERT INTO users (email, username, created_at) VALUES (?, ?, ?) RETURNING *'
)
.bind(email, username, Date.now())
.all();
return c.json(results[0]);
});
// Read
app.get('/users/:id', async (c) => {
const user = await c.env.DB.prepare(
'SELECT * FROM users WHERE user_id = ?'
)
.bind(c.req.param('id'))
.first();
if (!user) {
return c.json({ error: 'Not found' }, 404);
}
return c.json(user);
});
// Update
app.patch('/users/:id', async (c) => {
const { username } = await c.req.json();
await c.env.DB.prepare(
'UPDATE users SET username = ?, updated_at = ? WHERE user_id = ?'
)
.bind(username, Date.now(), c.req.param('id'))
.run();
return c.json({ success: true });
});
// Delete
app.delete('/users/:id', async (c) => {
await c.env.DB.prepare(
'DELETE FROM users WHERE user_id = ?'
)
.bind(c.req.param('id'))
.run();
return c.json({ success: true });
});
ユースケース 2:バッチ操作
app.post('/users/bulk', async (c) => {
const users = await c.req.json(); // Array of users
const statements = users.map(user =>
c.env.DB.prepare(
'INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)'
).bind(user.email, user.username, Date.now())
);
const results = await c.env.DB.batch(statements);
return c.json({ inserted: results.length });
});
ユースケース 3:読み取りレプリケーション(グローバルな読み取り)
// Configure read replica (any region)
const session = c.env.DB.withSession({
preferredRegion: 'auto' // or 'weur', 'wnam', 'enam', 'apac'
});
// Read from nearest replica
const { results } = await session.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
// Check which region served request
console.log('Served by:', results[0].served_by_region);
ガイドの全容については、references/read-replication.md を読み込んでください。
ユースケース 4:batch によるトランザクション
// Transfer credits between users (atomic)
const results = await c.env.DB.batch([
c.env.DB.prepare(
'UPDATE users SET credits = credits - ? WHERE user_id = ?'
).bind(amount, fromUserId),
c.env.DB.prepare(
'UPDATE users SET credits = credits + ? WHERE user_id = ?'
).bind(amount, toUserId),
c.env.DB.prepare(
'INSERT INTO transactions (from_user, to_user, amount, created_at) VALUES (?, ?, ?, ?)'
).bind(fromUserId, toUserId, amount, Date.now())
]);
// All succeed or all fail (transaction)
ユースケース 5:ページネーション
app.get('/users', async (c) => {
const page = parseInt(c.req.query('page') || '1');
const limit = 20;
const offset = (page - 1) * limit;
const { results } = await c.env.DB.prepare(
'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?'
)
.bind(limit, offset)
.all();
return c.json({
users: results,
page,
limit
});
});
マイグレーションのベストプラクティス
1. 必ずマイグレーションを使用する
bunx wrangler d1 migrations create my-database add_users_avatar
2. マイグレーションを冪等にする
-- ✅ GOOD: Idempotent
CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_email ON users(email);
DROP TABLE IF EXISTS old_table;
-- ❌ BAD: Fails on re-run
CREATE TABLE users (...);
CREATE INDEX idx_email ON users(email);
3. まずローカルでテストする
bunx wrangler d1 migrations apply my-database --local
bunx wrangler d1 execute my-database --local --command "SELECT * FROM users"
4. PRAGMA optimize を追加する
-- End of migration
PRAGMA optimize;
完全なスキーマテンプレートについては、templates/schema-example.sql を読み込んでください。
参照資料を読み込むタイミング
次の場合は references/setup-guide.md を読み込んでください:
- 初めて D1 をセットアップするとき
- 初めてデータベースを作成するとき
- バインディングを設定するとき
- 最初のマイグレーションを適用するとき
次の場合は references/query-patterns.md を読み込んでください:
- API リファレンス全体が必要なとき
- 複雑なクエリパターンを扱うとき
- バッチ操作を行うとき
- エラー処理を行うとき
次の場合は references/read-replication.md を読み込んでください:
- グローバル読み取りを設定するとき
- 世界各地で低レイテンシが必要なとき
- Sessions API を理解するとき
- 順次一貫性が必要なとき
次の場合は references/best-practices.md を読み込んでください:
- クエリのパフォーマンスを最適化するとき
- スキーマ設計を決定するとき
- インデックス戦略を検討するとき
- 本番環境へのデプロイチェックリストを確認するとき
次の場合は references/limits.md を読み込んでください:
- 429 エラーやクォータの警告が発生したとき
- 無料プランと有料プランの容量を計画するとき
- データベースやクエリの制限を理解するとき
- 有料プランに移行するとき
次の場合は references/metrics-analytics.md を読み込んでください:
- パフォーマンスの問題を調査するとき
- モニタリングとアラートを設定するとき
wrangler d1 insightsコマンドを使用するとき- クエリの効率を分析するとき
次の場合は references/2025-features.md を読み込んでください:
- v2.x から v3.x にアップグレードするとき
- 新機能(自動再試行、管轄区域、レプリケーション)を有効にするとき
- 破壊的変更(2025年2月10日の適用)を理解するとき
- 期限前に移行するとき
インタラクティブツール
エージェント(自律的な診断):
agents/d1-debugger.md:9段階の診断(設定、マイグレーション、クエリ、バインディング、エラー、制限、パフォーマンス、Time Travel)agents/d1-query-optimizer.md:パフォーマンス分析(低速クエリ、インデックス不足、最適化の推奨事項)
コマンド(インタラクティブウィザード):
commands/cloudflare-d1:setup.md:初回セットアップ用のインタラクティブウィザードcommands/d1-create-migration.md:検証付きのガイドに沿ったマイグレーション作成
同梱リソースの使用
参照資料(references/)
- setup-guide.md — セットアップの全手順
- query-patterns.md — 例を含む API リファレンス全体
- read-replication.md — グローバル読み取りレプリカのセットアップ
- best-practices.md — パフォーマンスと最適化
テンプレート(templates/)
- schema-example.sql — インデックスを含む完全なスキーマ
- d1-worker-queries.ts — Workers におけるすべてのクエリパターン
- cloudflare-d1:setup-migration.sh — 完全なセットアップスクリプト
よく使われるパターン
エラー処理
try {
const { results } = await env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
return c.json(results);
} catch (error) {
console.error('D1 Error:', error);
return c.json({ error: 'Database error' }, 500);
}
Raw モード(パフォーマンス)
// Returns arrays instead of objects (faster)
const { results } = await env.DB.prepare(
'SELECT user_id, email FROM users'
)
.raw();
// results = [[1, '[email protected]'], [2, '[email protected]']]
COUNT クエリ
const count = await env.DB.prepare(
'SELECT COUNT(*) as count FROM users'
)
.first('count'); // Get single column value
console.log('Total users:', count);
SQLite の型アフィニティ
D1 は SQLite の型アフィニティを使用します:
| 宣言された型 | アフィニティ |
|---|---|
| INTEGER, INT | INTEGER |
| TEXT, VARCHAR, CHAR | TEXT |
| REAL, FLOAT, DOUBLE | REAL |
| BLOB | BLOB |
| (型なし) | BLOB |
ベストプラクティス:
- 数値には
INTEGERを使用します - 文字列には
TEXTを使用します(VARCHAR ではなく) - タイムスタンプには
INTEGERを使用します(Date.now()) - バイナリデータには
BLOBを使用します
防止できる主なエラー 5 件
- SQL インジェクション:
.bind()を使用し、文字列連結は決して行いません - インデックス不足:WHERE/JOIN 列にインデックスを作成します
- マイグレーションの失敗:まずローカルでテストします
- 型の混同:タイムスタンプには INTEGER を使用します
- バッチサイズ:バッチ内のステートメント数を 500 未満に制限します
エラー防止の詳細については、references/best-practices.md を読み込んでください。
安全なインストール
D1 ドライバーパッケージをインストールするときは、サプライチェーンセキュリティのベストプラクティスに従ってください:
- インストール後スクリプトをブロックする —
npm config set ignore-scripts true(または Bun:デフォルトで無効) - クールダウン期間を設ける — 新しいパッケージバージョンがコミュニティによって検証されるまで、7 日間待ちます
- インストール前に監査する —
socket package score npm <pkg>を実行するか、socket npm install <pkg>を使用してパッケージを確認します
Socket CLI との統合、クールダウンの設定、ロックファイルの検証、CI での適用を含むセキュリティ設定全体については、dependency-upgrade スキルを読み込んでください。
公式ドキュメント
- D1 の概要: https://developers.cloudflare.com/d1/
- はじめに: https://developers.cloudflare.com/d1/get-started/
- クライアント API: https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/
- 読み取りレプリケーション: https://developers.cloudflare.com/d1/reference/read-replication/
質問や問題がありますか?
- セットアップについては
references/setup-guide.mdを確認してください - API リファレンスについては
references/query-patterns.mdを確認してください - グローバル読み取りについては
references/read-replication.mdを参照してください - 最適化については
references/best-practices.mdを読み込んでください