cloudflare-d1 について
cloudflare-d1 は、Cloudflare Workers 開発者向けの D1 データベース利用ガイドです。データベースのバインディング、SQL クエリ、マイグレーションに加え、ローカル・リモート開発やエラー対応を扱います。
主な機能
- 01Wrangler による作成とバインド
- 02バージョン管理された SQL マイグレーション
- 03プリペアドステートメントとバッチクエリ
- 04ローカルおよびリモートでの操作
- 05エラー再試行とクエリ最適化
活用シーン
- 01Worker 用の D1 設定
- 02スキーママイグレーションの作成と適用
- 03関連データのバッチクエリ
- 04データベースエラーの調査とクエリ調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill cloudflare-d1インストール後、Claude Code で/cloudflare-d1を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.md.claude-plugin/plugin.jsonreferences/best-practices.mdquery-patterns.mdrules/cloudflare-d1.mdtemplates/d1-setup-migration.shd1-worker-queries.tsschema-example.sql詳細
Cloudflare D1 Database
ステータス: 本番環境で利用可能 ✅ 最終更新日: 2026-01-20 依存関係: cloudflare-worker-base(Worker のセットアップ用) 最新バージョン: [email protected]、@cloudflare/[email protected]
最近の更新(2025):
- 2025年11月: Jurisdiction のサポート(データローカライゼーションへの準拠)、リモートバインディングが GA([email protected]+)、リソースの自動プロビジョニング
- 2025年9月: 読み取り専用クエリの自動再試行(最大2回)、リモートバインディングがパブリックベータに
- 2025年7月: ストレージ上限を引き上げ(250GB → 1TB)、アルファ版のバックアップアクセスを削除、REST API が50~500ms高速化
- 2025年5月: HTTP API の権限に関するセキュリティ修正(書き込みには D1:Edit が必要)
- 2025年4月: 読み取りレプリケーションがパブリックベータに(リージョン間の読み取り専用レプリカ)
- 2025年2月: PRAGMA optimize のサポート、読み取り専用アクセス権限のバグを修正
- 2025年1月: 無料プランの上限適用(2月10日開始)、Worker API のクエリが40~60%高速化
クイックスタート(5分)
1. D1 データベースを作成する
# Create a new D1 database
npx wrangler d1 create my-database
# Output includes database_id - save this!
# ✅ Successfully created DB 'my-database'
#
# [[d1_databases]]
# binding = "DB"
# database_name = "my-database"
# database_id = "<UUID>"
2. バインディングを設定する
wrangler.jsonc に追加します。
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"d1_databases": [
{
"binding": "DB", // Available as env.DB in your Worker
"database_name": "my-database", // Name from wrangler d1 create
"database_id": "<UUID>", // ID from wrangler d1 create
"preview_database_id": "local-db" // For local development
}
]
}
重要:
bindingは、コード内でデータベースにアクセスする方法です(env.DB)database_idは、本番データベースの UUID ですpreview_database_idは、ローカル開発用です(任意の文字列を指定できます)- 実際の
database_idの値を公開リポジトリにコミットしないでください。環境変数またはシークレットを使用してください
3. 最初のマイグレーションを作成する
# Create migration file
npx wrangler d1 migrations create my-database create_users_table
# This creates: migrations/0001_create_users_table.sql
マイグレーションファイルを編集します。
-- migrations/0001_create_users_table.sql
DROP TABLE IF EXISTS users;
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,
updated_at INTEGER
);
-- Create index for common queries
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
-- Optimize database
PRAGMA optimize;
4. マイグレーションを適用する
# Apply locally first (for testing)
npx wrangler d1 migrations apply my-database --local
# Apply to production when ready
npx wrangler d1 migrations apply my-database --remote
5. Worker からクエリを実行する
// src/index.ts
import { Hono } from 'hono';
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/api/users/:email', async (c) => {
const email = c.req.param('email');
try {
// ALWAYS use prepared statements with bind()
const result = await c.env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.first();
if (!result) {
return c.json({ error: 'User not found' }, 404);
}
return c.json(result);
} catch (error: any) {
console.error('D1 Error:', error.message);
return c.json({ error: 'Database error' }, 500);
}
});
export default app;
D1 マイグレーションシステム
マイグレーションのワークフロー
# 1. Create migration
npx wrangler d1 migrations create <DATABASE_NAME> <MIGRATION_NAME>
# 2. List unapplied migrations
npx wrangler d1 migrations list <DATABASE_NAME> --local
npx wrangler d1 migrations list <DATABASE_NAME> --remote
# 3. Apply migrations
npx wrangler d1 migrations apply <DATABASE_NAME> --local # Test locally
npx wrangler d1 migrations apply <DATABASE_NAME> --remote # Deploy to production
マイグレーションファイルの命名
マイグレーションは自動的にバージョン管理されます。
migrations/
├── 0000_initial_schema.sql
├── 0001_add_users_table.sql
├── 0002_add_posts_table.sql
└── 0003_add_indexes.sql
ルール:
- ファイルは順番に実行されます
- 各マイグレーションは一度だけ実行されます(
d1_migrationsテーブルで追跡) - 失敗したマイグレーションはロールバックされます(トランザクション)
- 適用済みのマイグレーションは変更または削除できません
カスタムマイグレーション設定
{
"d1_databases": [
{
"binding": "DB",
"database_name": "my-database",
"database_id": "<UUID>",
"migrations_dir": "db/migrations", // Custom directory (default: migrations/)
"migrations_table": "schema_migrations" // Custom tracking table (default: d1_migrations)
}
]
}
マイグレーションのベストプラクティス
✅ 必ず行うこと:
-- Use IF NOT EXISTS to make migrations idempotent
CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
-- Run PRAGMA optimize after schema changes
PRAGMA optimize;
-- Use UPPERCASE BEGIN/END in triggers (lowercase fails remotely)
CREATE TRIGGER update_timestamp
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
UPDATE users SET updated_at = unixepoch() WHERE user_id = NEW.user_id;
END;
-- Use transactions for data migrations
BEGIN TRANSACTION;
UPDATE users SET updated_at = unixepoch() WHERE updated_at IS NULL;
COMMIT;
❌ 絶対に行わないこと:
-- DON'T include BEGIN TRANSACTION at start of migration file (D1 handles this)
BEGIN TRANSACTION; -- ❌ Remove this
-- DON'T use lowercase begin/end in triggers (works locally, FAILS remotely)
CREATE TRIGGER my_trigger
AFTER INSERT ON table
begin -- ❌ Use BEGIN (uppercase)
UPDATE ...;
end; -- ❌ Use END (uppercase)
-- DON'T use MySQL/PostgreSQL syntax
ALTER TABLE users MODIFY COLUMN email VARCHAR(255); -- ❌ Not SQLite
-- DON'T create tables without IF NOT EXISTS
CREATE TABLE users (...); -- ❌ Fails if table exists
マイグレーションでの外部キーの扱い
-- Temporarily disable foreign key checks during schema changes
PRAGMA defer_foreign_keys = true;
-- Make schema changes that would violate foreign keys
ALTER TABLE posts DROP COLUMN author_id;
ALTER TABLE posts ADD COLUMN user_id INTEGER REFERENCES users(user_id);
-- Foreign keys re-enabled automatically at end of migration
D1 Workers API
型定義:
interface Env { DB: D1Database; }
type Bindings = { DB: D1Database; };
const app = new Hono<{ Bindings: Bindings }>();
prepare() - 主なメソッド(ユーザー入力には必ず使用してください):
const user = await env.DB.prepare('SELECT * FROM users WHERE email = ?')
.bind(email).first();
理由: SQL インジェクションを防ぎ、再利用でき、パフォーマンスが向上し、型安全です
クエリ結果のメソッド:
.all()→{ results, meta }- すべての行を取得.first()→ 行オブジェクトまたは null - 最初の行を取得.first('column')→ 値 - 単一カラムの値を取得(例: COUNT).run()→{ success, meta }- INSERT/UPDATE/DELETE を実行(結果なし)
batch() - パフォーマンス上、非常に重要:
const results = await env.DB.batch([
env.DB.prepare('SELECT * FROM users WHERE user_id = ?').bind(1),
env.DB.prepare('SELECT * FROM posts WHERE user_id = ?').bind(1)
]);
- 順次実行され、ネットワークの往復は1回です
- 1つでも失敗すると、残りのステートメントは実行されません
- 一括挿入、関連データの取得に使用します
exec() - 本番環境では使用を避ける:
await env.DB.exec('SELECT * FROM users;'); // Only for migrations/maintenance
- ❌ ユーザー入力とともに使用しないでください(SQL インジェクションのリスク)
- ✅ マイグレーションファイルや単発のタスクに限り使用してください
クエリパターン
基本的な CRUD 操作
// CREATE
const { meta } = await env.DB.prepare(
'INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)'
).bind(email, username, Date.now()).run();
const newUserId = meta.last_row_id;
// READ (single)
const user = await env.DB.prepare('SELECT * FROM users WHERE user_id = ?')
.bind(userId).first();
// READ (multiple)
const { results } = await env.DB.prepare('SELECT * FROM users LIMIT ?')
.bind(10).all();
// UPDATE
const { meta } = await env.DB.prepare('UPDATE users SET username = ? WHERE user_id = ?')
.bind(newUsername, userId).run();
const rowsAffected = meta.rows_written;
// DELETE
await env.DB.prepare('DELETE FROM users WHERE user_id = ?').bind(userId).run();
// COUNT
const count = await env.DB.prepare('SELECT COUNT(*) as total FROM users').first('total');
// EXISTS check
const exists = await env.DB.prepare('SELECT 1 FROM users WHERE email = ? LIMIT 1')
.bind(email).first();
ページネーションパターン
const page = parseInt(c.req.query('page') || '1');
const limit = 20;
const offset = (page - 1) * limit;
const [countResult, usersResult] = await c.env.DB.batch([
c.env.DB.prepare('SELECT COUNT(*) as total FROM users'),
c.env.DB.prepare('SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?')
.bind(limit, offset)
]);
return c.json({
users: usersResult.results,
pagination: { page, limit, total: countResult.results[0].total }
});
バッチパターン(疑似トランザクション)
// D1 doesn't support multi-statement transactions, but batch() provides sequential execution
await env.DB.batch([
env.DB.prepare('UPDATE users SET credits = credits - ? WHERE user_id = ?').bind(amount, fromUserId),
env.DB.prepare('UPDATE users SET credits = credits + ? WHERE user_id = ?').bind(amount, toUserId),
env.DB.prepare('INSERT INTO transactions (from_user, to_user, amount) VALUES (?, ?, ?)').bind(fromUserId, toUserId, amount)
]);
// If any statement fails, batch stops (transaction-like behavior)
エラー処理
一般的なエラーの種類:
D1_ERROR- 一般的な D1 エラー(多くの場合、一時的)D1_EXEC_ERROR- SQL 構文エラーまたは制限D1_TYPE_ERROR- 型の不一致(null ではなく undefined)D1_COLUMN_NOTFOUND- カラムが存在しません
一般的なエラーと対処方法:
| エラー | 原因 | 解決策 |
|---|---|---|
| Statement too long | 1000 行以上の大規模な INSERT | batch() を使って 100~250 行ごとに分割します |
| Network connection lost | 一時的な障害または大規模なインポート | 再試行ロジック(以下を参照)を実装するか、より小さなチャンクに分割します |
| Too many requests queued | ループ内で個別のクエリを実行 | ループの代わりに batch() を使います |
| D1_TYPE_ERROR | bind で undefined を使用 | オプション値には null を使います:.bind(email, bio || null) |
| Transaction conflicts | マイグレーション内の BEGIN TRANSACTION | BEGIN/COMMIT を削除します(D1 が自動的に処理します) |
| Foreign key violations | スキーマ変更による制約の破損 | PRAGMA defer_foreign_keys = true を使います |
| D1_EXEC_ERROR: incomplete input | D1Database.exec() 内の複数行 SQL | プリペアドステートメントまたは外部の .sql ファイルを使います(Issue #9133) |
一時的なエラーは想定された動作です
重要:D1 のクエリは、「Network connection lost」、「storage operation exceeded timeout」、「isolate exceeded its memory limit」などのエラーで一時的に失敗します。Cloudflare のドキュメントでは、**「数時間ごとに数件のエラーが発生することは想定外ではありません」**と説明されており、再試行ロジックの実装を推奨しています。(D1 FAQ)
一般的な一時的エラー:
D1_ERROR: Network connection lostD1 DB storage operation exceeded timeout which caused object to be resetInternal error while starting up D1 DB storage caused object to be resetD1 DB's isolate exceeded its memory limit and was reset
再試行パターン(推奨):
async function queryWithRetry<T>(
fn: () => Promise<T>,
maxRetries = 3,
baseDelay = 100
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
const isTransient = error.message?.includes('Network connection lost') ||
error.message?.includes('exceeded timeout') ||
error.message?.includes('exceeded its memory limit');
if (!isTransient || i === maxRetries - 1) throw error;
// Exponential backoff
await new Promise(r => setTimeout(r, baseDelay * Math.pow(2, i)));
}
}
throw new Error('Max retries exceeded');
}
// Usage
const user = await queryWithRetry(() =>
env.DB.prepare('SELECT * FROM users WHERE email = ?').bind(email).first()
);
自動再試行(2025年9月):
D1 は、再試行可能なエラーが発生した場合、読み取り専用クエリ(SELECT、EXPLAIN、WITH)を最大 2 回、自動的に再試行します。応答の meta.total_attempts で再試行回数を確認できます。書き込みクエリでは、引き続きカスタムの再試行ロジックを実装してください。
パフォーマンスの最適化
インデックスのベストプラクティス:
- ✅ WHERE 句で使うカラムにインデックスを設定します:
CREATE INDEX idx_users_email ON users(email) - ✅ 外部キーにインデックスを設定します:
CREATE INDEX idx_posts_user_id ON posts(user_id) - ✅ ソートに使うカラムにインデックスを設定します:
CREATE INDEX idx_posts_created_at ON posts(created_at DESC) - ✅ 複数カラムのインデックス:
CREATE INDEX idx_posts_user_published ON posts(user_id, published) - ✅ 部分インデックス:
CREATE INDEX idx_users_active ON users(email) WHERE deleted = 0 - ✅ 次のコマンドでテストします:
EXPLAIN QUERY PLAN SELECT ...
PRAGMA optimize(2025年2月):
CREATE INDEX idx_users_email ON users(email);
PRAGMA optimize; -- Run after schema changes
クエリの最適化:
- ✅ 特定のカラムを指定します(
SELECT *は使いません) - ✅ 大きな結果セットには必ず LIMIT を指定します
- ✅ WHERE 条件にはインデックスを使います
- ❌ WHERE 句内で関数を使うのは避けます(インデックスを使用できません):
WHERE LOWER(email)→ 代わりに小文字で保存します
ローカル開発
ローカルとリモート(2025年11月 - リモートバインディングの一般提供開始):
# Local database (automatic creation)
npx wrangler d1 migrations apply my-database --local
npx wrangler d1 execute my-database --local --command "SELECT * FROM users"
# Remote database
npx wrangler d1 execute my-database --remote --command "SELECT * FROM users"
# Remote bindings ([email protected]+) - connect local Worker to deployed D1
# Add to wrangler.jsonc: { "binding": "DB", "remote": true }
リモートバインディングの接続タイムアウト
既知の問題:リモート D1 バインディング({ "remote": true })を使うと、1 時間ちょうど操作がない状態が続いた後、接続がタイムアウトします。(GitHub Issue #10801)
エラー:D1_ERROR: Failed to parse body as JSON, got: error code: 1031
回避策:
// Keep connection alive with periodic query (optional)
setInterval(async () => {
try {
await env.DB.prepare('SELECT 1').first();
} catch (e) {
console.log('Connection keepalive failed:', e);
}
}, 30 * 60 * 1000); // Every 30 minutes
または、1 時間操作がなかった後にクエリが失敗した場合は、開発サーバーを再起動してください。
複数ワーカーでの開発(サービスバインディング)
単一の wrangler dev プロセスでサービスバインディングを使用して複数の Workers を実行すると、両方のワーカーが同じ永続化パスを共有するため、補助ワーカーは D1 バインディングにアクセスできません。(GitHub Issue #11121)
解決策:--persist-to フラグを使用して、すべてのワーカーが同じ永続化ストアを参照するようにします。
# Apply worker2 migrations to worker1's persistence path
cd worker2
npx wrangler d1 migrations apply DB --local --persist-to=../worker1/.wrangler/state
# Now both workers can access D1
cd ../worker1
npx wrangler dev # Both workers share the same D1 data
ローカルデータベースの場所:
.wrangler/state/v3/d1/miniflare-D1DatabaseObject/<database_id>.sqlite
ローカルデータベースにデータを投入する:
npx wrangler d1 execute my-database --local --file=seed.sql
スケーリングと制限
データベースサイズの上限は 10 GB — シャーディングパターン
D1 ではデータベースごとに 10 GB の厳格な上限がありますが、Cloudflare は Worker ごとに最大 50,000 個のデータベースをサポートしています。10 GB を超えてスケールするには、シャーディングを使用します。(DEV.to Article)
ハッシュベースのシャーディング例(10 個のデータベースで容量 100 GB):
// Hash user ID to shard number
function getShardId(userId: string): number {
const hash = Array.from(userId).reduce((acc, char) =>
((acc << 5) - acc) + char.charCodeAt(0), 0
);
return Math.abs(hash) % 10; // 10 shards
}
// wrangler.jsonc - Define 10 database shards
{
"d1_databases": [
{ "binding": "DB_SHARD_0", "database_id": "..." },
{ "binding": "DB_SHARD_1", "database_id": "..." },
{ "binding": "DB_SHARD_2", "database_id": "..." },
// ... up to DB_SHARD_9
]
}
// Get correct shard for user
function getUserDb(env: Env, userId: string): D1Database {
const shardId = getShardId(userId);
return env[`DB_SHARD_${shardId}`];
}
// Query user's data from correct shard
const db = getUserDb(env, userId);
const user = await db.prepare('SELECT * FROM users WHERE user_id = ?')
.bind(userId).first();
代替案:テナントベースのシャーディング(顧客/テナントごとに 1 つのデータベース)
行サイズの上限は 2 MB — D1 と R2 のハイブリッドパターン
D1 では行サイズが 2 MB に制限されています。大きなコンテンツ(HTML、JSON、画像)には、ストレージに R2、メタデータに D1 を使用します。(DEV.to Article)
エラー:database row size exceeded maximum allowed size
解決策 — ハイブリッドストレージパターン:
// 1. Store large content in R2
const contentKey = `pages/${crypto.randomUUID()}.html`;
await env.R2_BUCKET.put(contentKey, largeHtmlContent);
// 2. Store metadata in D1
await env.DB.prepare(`
INSERT INTO pages (url, r2_key, size, created_at)
VALUES (?, ?, ?, ?)
`).bind(url, contentKey, largeHtmlContent.length, Date.now()).run();
// 3. Retrieve content
const page = await env.DB.prepare('SELECT * FROM pages WHERE url = ?')
.bind(url).first();
if (page) {
const content = await env.R2_BUCKET.get(page.r2_key);
const html = await content.text();
}
データベースの移植性 — PostgreSQL への移行に関する考慮事項
後で D1(SQLite)から Hyperdrive(PostgreSQL)への移行を予定している場合は、一貫して小文字の名前を使用してください。PostgreSQL ではテーブル名とカラム名の大文字と小文字が区別されますが、SQLite では区別されません。(Mats' Blog)
-- Use lowercase for portability
CREATE TABLE users (user_id INTEGER, email TEXT);
CREATE INDEX idx_users_email ON users(email);
-- NOT: CREATE TABLE Users (UserId INTEGER, Email TEXT);
FTS5 全文検索
大文字と小文字の区別:仮想テーブルを作成するときは、必ず小文字の「fts5」を使用してください。大文字で指定すると、「not authorized」エラーが発生する場合があります。(Cloudflare Community)
-- Correct
CREATE VIRTUAL TABLE search_index USING fts5(
title,
content,
tokenize = 'porter unicode61'
);
-- Query the index
SELECT * FROM search_index WHERE search_index MATCH 'query terms';
エクスポートの制限:FTS5 仮想テーブルを含むデータベースは、wrangler d1 export を使用してエクスポートできません。エクスポートの前に仮想テーブルを削除し、インポート後に再作成してください。(GitHub Issue #9519)
大規模なインポート/エクスポート処理
大規模なインポートでのネットワークタイムアウト:INSERT 文が 5000 個以上含まれるファイルでは、「Network connection lost」エラーが発生することがあります。(GitHub Issue #11958)
解決策:
- 大きなファイルを小さなチャンク(ファイルあたり 5000 文未満)に分割します
- wrangler CLI の代わりに、Worker から
batch()API を使用します - まずローカルにインポートし、その後 Time Travel を使用してリモートに復元します
- 個々の文のサイズを小さくします(INSERT あたり 100~250 行)
Windows 固有の問題:Windows 11 では、D1 からエクスポートした大きな SQL ファイルを再インポートすると、「HashIndex detected hash table inconsistency」エラーが発生することがあります。(GitHub Issue #11708)
回避策:次を実行する前に .wrangler ディレクトリを削除します。
rm -rf .wrangler
npx wrangler d1 execute db-name --file=database.sql
ベストプラクティスのまとめ
✅ 必ず行うこと:
- ユーザー入力には
.bind()を使用したプリペアドステートメントを使います - 複数のクエリには
.batch()を使用します(レイテンシを削減します) - 頻繁にクエリするカラムにインデックスを作成します
- スキーマの変更後に
PRAGMA optimizeを実行します - 冪等性を確保するため、マイグレーションでは
IF NOT EXISTSを使用します - 本番環境に適用する前にマイグレーションをローカルでテストします
- try/catch を使用してエラーを適切に処理します
- オプションの値には
undefinedではなくnullを使用します - クエリにバインドする前に入力を検証します
- UPDATE/DELETE の後に
meta.rows_writtenを確認します
❌ 絶対にしないこと:
- ユーザー入力に対して
.exec()を絶対に使用しないでください(SQL インジェクションのリスクがあります) - 公開リポジトリで
database_idを絶対にハードコードしないでください - バインドパラメーターに
undefinedを絶対に使用しないでください(D1_TYPE_ERROR の原因になります) - ループ内で個別のクエリを絶対に実行しないでください(代わりに batch を使用してください)
- 大量の結果が返される可能性がある場合、
LIMITを絶対に忘れないでください - 本番環境では
SELECT *を絶対に使用しないでください(列を指定してください) - マイグレーションファイルに
BEGIN TRANSACTIONを絶対に含めないでください - 適用済みのマイグレーションを 絶対に変更しないでください(新しいものを作成してください)
- データベース操作で エラーハンドリングを絶対に省略しないでください
- クエリが成功すると 絶対に思い込まないでください(常に結果を確認してください)
回避できる既知の問題
このスキルは、文書化されている 14 件の D1 エラーを防ぎます。
| 問題 # | エラー/問題 | 説明 | 回避方法 | 出典 |
|---|---|---|---|---|
| #1 | ステートメントが長すぎる | 大きな INSERT ステートメントが D1 の制限を超えます | batch() を使い、100~250 行のバッチに分割します | 既知 |
| #2 | トランザクションの競合 | マイグレーションファイルに BEGIN TRANSACTION があります | BEGIN/COMMIT を削除します(D1 が自動的に処理します) | 既知 |
| #3 | 外部キー制約違反 | スキーマの変更により外部キー制約に違反します | マイグレーションで PRAGMA defer_foreign_keys = true を使用します | 既知 |
| #4 | レート制限/キューの過負荷 | 個別のクエリが多すぎます | ループの代わりに batch() を使用します | 既知 |
| #5 | メモリ制限の超過 | クエリによってメモリに過剰なデータが読み込まれます | LIMIT の追加、結果のページネーション、クエリのシャーディングを行います | 既知 |
| #6 | 型の不一致エラー | bind() で null の代わりに undefined を使用しています | オプションの値には常に null を使用します | 既知 |
| #7 | トリガー内の小文字の BEGIN | begin/end が小文字のトリガーはリモートで失敗します | 大文字の BEGIN/END キーワードを使用します(Issue #10998) | TIER 1 |
| #8 | リモートバインディングのタイムアウト | 1 時間操作がないと接続がタイムアウトします | 開発サーバーを再起動するか、キープアライブパターンを実装します(Issue #10801) | TIER 1 |
| #9 | サービスバインディングからの D1 アクセス | マルチワーカー開発環境では補助ワーカーが D1 にアクセスできません | --persist-to フラグを使用して永続化パスを共有します(Issue #11121) | TIER 1 |
| #10 | 一時的なネットワークエラー | 「ネットワーク接続が失われました」というエラーがランダムに発生します | 指数バックオフによるリトライロジックを実装します(D1 FAQ) | TIER 1 |
| #11 | FTS5 によるエクスポートの失敗 | FTS5 仮想テーブルを含むデータベースはエクスポートできません | エクスポート前に仮想テーブルを削除し、インポート後に再作成します(Issue #9519) | TIER 1 |
| #12 | exec() 内の複数行 SQL | 複数行 SQL で D1Database.exec() が失敗します | プリペアドステートメントまたは外部 .sql ファイルを使用します(Issue #9133) | TIER 1 |
| #13 | データベースの 10 GB 制限 | 単一のデータベースは 10 GB に制限されています | 複数のデータベースにまたがるシャーディングを実装します(コミュニティ) | TIER 2 |
| #14 | 行サイズの 2 MB 制限 | 2 MB を超える行は失敗します | D1 と R2 を組み合わせたハイブリッドストレージパターンを使用します(コミュニティ) | TIER 2 |
Wrangler コマンドリファレンス
# Database management
wrangler d1 create <DATABASE_NAME>
wrangler d1 list
wrangler d1 delete <DATABASE_NAME>
wrangler d1 info <DATABASE_NAME>
# Migrations
wrangler d1 migrations create <DATABASE_NAME> <MIGRATION_NAME>
wrangler d1 migrations list <DATABASE_NAME> --local|--remote
wrangler d1 migrations apply <DATABASE_NAME> --local|--remote
# Execute queries
wrangler d1 execute <DATABASE_NAME> --local|--remote --command "SELECT * FROM users"
wrangler d1 execute <DATABASE_NAME> --local|--remote --file=./query.sql
# Time Travel (view historical data)
wrangler d1 time-travel info <DATABASE_NAME> --timestamp "2025-10-20"
wrangler d1 time-travel restore <DATABASE_NAME> --timestamp "2025-10-20"
公式ドキュメント
- D1 の概要: https://developers.cloudflare.com/d1/
- はじめに: https://developers.cloudflare.com/d1/get-started/
- マイグレーション: https://developers.cloudflare.com/d1/reference/migrations/
- Workers API: https://developers.cloudflare.com/d1/worker-api/
- ベストプラクティス: https://developers.cloudflare.com/d1/best-practices/
- Wrangler コマンド: https://developers.cloudflare.com/workers/wrangler/commands/#d1
D1 での開発を始めましょう! 🚀
最終確認日: 2026-01-20 | スキルバージョン: 3.0.0 | 変更点: コミュニティ調査で判明した新たな既知の問題を 8 件追加(TIER 1-2 の調査結果):トリガーの大文字・小文字の区別、リモートバインディングのタイムアウト、複数 Worker の開発パターン、一時的なエラーの処理、FTS5 の制限、シャーディングのパターン、D1 と R2 を組み合わせたストレージ、データベースの移植性に関する考慮事項。