Workerプロジェクト作成
D1データベースを使うAPIエンドポイントの実装に必要なファイル構成と内容を説明します。
ファイル構成(変更・追加箇所)
my-worker/
├── src/
│ ├── index.ts ← ルーティング追加
│ └── api/
│ └── items.ts ← D1を使うAPIハンドラ(新規追加)
├── wrangler.toml ← D1バインディング追加
├── package.json
├── tsconfig.json
└── worker-configuration.d.ts ← D1の型定義追加
各ファイルの実装内容
wrangler.toml — D1バインディングを追加
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[[d1_databases]]
binding = "DB" # コード内で env.DB としてアクセスする名前
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # wrangler d1 create で取得
worker-configuration.d.ts — D1の型定義を追加
interface Env {
DB: D1Database; // D1バインディングの型
}
src/index.ts — ルーティング
import { handleItemsApi } from "./api/items";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
// /api/items へのリクエストをハンドラに委譲
if (url.pathname.startsWith("/api/items")) {
return handleItemsApi(request, env);
}
return new Response("Not Found", { status: 404 });
},
};
src/api/items.ts — D1を使うAPIハンドラ(新規追加)
export async function handleItemsApi(
request: Request,
env: Env
): Promise<Response> {
const url = new URL(request.url);
const id = url.pathname.split("/").pop();
switch (request.method) {
case "GET": {
if (id && id !== "items") {
// 1件取得
const item = await env.DB.prepare(
"SELECT * FROM items WHERE id = ?"
)
.bind(id)
.first();
if (!item) return json({ error: "Not found" }, 404);
return json(item);
}
// 一覧取得
const { results } = await env.DB.prepare(
"SELECT * FROM items ORDER BY id DESC"
).all();
return json(results);
}
case "POST": {
const body = await request.json<{ name: string }>();
const result = await env.DB.prepare(
"INSERT INTO items (name) VALUES (?) RETURNING *"
)
.bind(body.name)
.first();
return json(result, 201);
}
case "PUT": {
const body = await request.json<{ name: string }>();
await env.DB.prepare("UPDATE items SET name = ? WHERE id = ?")
.bind(body.name, id)
.run();
return json({ success: true });
}
case "DELETE": {
await env.DB.prepare("DELETE FROM items WHERE id = ?")
.bind(id)
.run();
return json({ success: true });
}
default:
return json({ error: "Method Not Allowed" }, 405);
}
}
function json(data: unknown, status = 200): Response {
return new Response(JSON.stringify(data), {
status,
headers: { "Content-Type": "application/json" },
});
}
セットアップの流れ
# 1. D1データベースを作成(database_id を取得)
npx wrangler d1 create my-database
# 2. テーブルをマイグレーション(ローカル)
npx wrangler d1 execute my-database --local --command \
"CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)"
# 3. ローカル開発サーバー起動
npx wrangler dev
# 4. 本番へデプロイ
npx wrangler deploy
ポイントまとめ
| 項目 | 内容 |
|---|---|
env.DB へのアクセス |
wrangler.toml の binding = "DB" と型定義が一致している必要がある |
| SQLインジェクション対策 | .bind() でプレースホルダーを使う(文字列結合は禁止) |
| レスポンス形式 | D1Result.results が配列、.first() が1件のオブジェクト |
| ローカルテスト | --local フラグでローカルのSQLiteに対して実行される |
エラーの原因は明確です。