mcpField
C
cloudflare-d1

cloudflare-d1

github.com/secondskyデータ・データベース240406 回インストール5 週間前に更新
リポジトリ

cloudflare-d1 について

Cloudflare D1 を利用する開発者向けのスキルです。Workers から使うサーバーレス SQLite の設定、クエリ、マイグレーションに加え、バッチ処理や読み取りレプリケーションのパターンを解説します。

主な機能

  1. 01データベースとバインディングの設定
  2. 02プリペアドステートメントとバッチクエリ
  3. 03スキーママイグレーションのガイド
  4. 04読み取りレプリケーションのパターン
  5. 05SQLite の型とクエリの実践

活用シーン

  1. 01Worker 用の D1 データベース作成
  2. 02スキーママイグレーションの適用とテスト
  3. 03データベース操作のアトミックな一括実行
  4. 04レプリカによるグローバル読み取りの提供

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/secondsky/claude-skills --skill cloudflare-d1

インストール後、Claude Code で/cloudflare-d1を入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
2025-features.md
best-practices.md
limits.md
metrics-analytics.md
query-patterns.md
read-replication.md
setup-guide.md
templates/
d1-setup-migration.sh
d1-worker-queries.ts
schema-example.sql

詳細

Cloudflare D1 Database

Status: 本番環境対応 ✅ | Last Verified: 2025-01-15

目次

  1. D1 とは?
  2. クイックスタート
  3. 重要なルール
  4. D1 API メソッド
  5. 主なユースケース 5 選
  6. マイグレーションのベストプラクティス
  7. 一般的なパターン
  8. SQLite の型アフィニティ
  9. 防止できる主なエラー 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 を読み込んでください。


重要なルール

必ず行うこと ✅
  1. .bind() を使ったプリペアドステートメントを使用する(文字列連結は絶対にしない)
  2. WHERE/JOIN/ORDER BY の列にインデックスを作成する
  3. スキーマ変更にはマイグレーションを使用する(手動の SQL は絶対に使用しない)
  4. 複数の操作にはクエリをバッチ処理する(.batch())
  5. スキーマ変更後に PRAGMA optimize を実行する
  6. エラーを明示的に処理する(try/catch)
  7. タイムスタンプには INTEGER を使用する(Date.now())
  8. マイグレーションをデプロイする前にローカルでテストする
  9. グローバルな読み取りパフォーマンスには読み取りレプリカを使用する
  10. SQL クエリの前に入力を検証する
絶対にしてはいけないこと ❌
  1. ユーザー入力を SQL に連結しない
  2. database_id を公開リポジトリにコミットしない
  3. スキーマ変更でマイグレーションを省略しない
  4. VARCHAR を使用しない(代わりに TEXT を使用)
  5. フィルター対象の列にインデックスを付け忘れない
  6. SQLite の型アフィニティのルールを無視しない
  7. LIMIT を指定せずに SELECT * を使用しない
  8. ローカルでテストせずにマイグレーションを実行しない
  9. 1 行あたり 1 MB を超えない
  10. 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, INTINTEGER
TEXT, VARCHAR, CHARTEXT
REAL, FLOAT, DOUBLEREAL
BLOBBLOB
(型なし)BLOB

ベストプラクティス:

  • 数値には INTEGER を使用します
  • 文字列には TEXT を使用します(VARCHAR ではなく)
  • タイムスタンプには INTEGER を使用します(Date.now())
  • バイナリデータには BLOB を使用します

防止できる主なエラー 5 件

  1. SQL インジェクション:.bind() を使用し、文字列連結は決して行いません
  2. インデックス不足:WHERE/JOIN 列にインデックスを作成します
  3. マイグレーションの失敗:まずローカルでテストします
  4. 型の混同:タイムスタンプには INTEGER を使用します
  5. バッチサイズ:バッチ内のステートメント数を 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 スキルを読み込んでください。

公式ドキュメント


質問や問題がありますか?

  1. セットアップについては references/setup-guide.md を確認してください
  2. API リファレンスについては references/query-patterns.md を確認してください
  3. グローバル読み取りについては references/read-replication.md を参照してください
  4. 最適化については references/best-practices.md を読み込んでください

コメント