name: prisma-d1 description: Prisma ORM with Cloudflare D1 — schema configuration, migration workflow, client setup, and deployment. Load when working with Prisma + D1 migrations, schema changes, or PrismaClient instantiation in Workers. metadata: author: Yuki Minakami version: "0.1.0" source: https://github.com/qtmleap/claude-plugins
Prisma ORM + Cloudflare D1
Setup, migration, and deployment guide for Prisma with Cloudflare D1.
Retrieval Source
| Source | URL | Use for |
|---|---|---|
| Prisma D1 Guide | https://www.prisma.io/docs/guides/deployment/cloudflare-d1 |
Official guide |
| Prisma Migrate Diff | https://www.prisma.io/docs/orm/reference/prisma-cli-reference#migrate-diff |
migrate diff options |
| D1 Docs | https://developers.cloudflare.com/d1 |
D1 specs and limitations |
Required Packages
bun add prisma --dev
bun add @prisma/client @prisma/adapter-d1
Schema Configuration (prisma/schema.prisma)
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
runtime = "cloudflare"
}
datasource db {
provider = "sqlite"
}
Key points:
provider = "sqlite"— D1 is SQLite-basedruntime = "cloudflare"— generates client for Cloudflare Workers runtimeoutput— must be explicitly specified
Migration Workflow
D1 does not use standard prisma migrate dev. Instead, use prisma migrate diff to generate SQL, then wrangler d1 execute to apply it.
Initial Migration
# Generate SQL (empty state -> current schema)
bunx prisma migrate diff \
--from-empty \
--to-schema prisma/schema.prisma \
--script > migrations/0001_init.sql
# Apply to local D1
bunx wrangler d1 execute <DB_NAME> --local \
--file="./migrations/0001_init.sql"
# Apply to remote D1
bunx wrangler d1 execute <DB_NAME> --remote \
--file="./migrations/0001_init.sql"
Subsequent Migrations
# Generate SQL (current local D1 state -> current schema)
bunx prisma migrate diff \
--from-local-d1 \
--to-schema prisma/schema.prisma \
--script > migrations/0002_<description>.sql
# Apply locally
bunx wrangler d1 execute <DB_NAME> --local \
--file="./migrations/0002_<description>.sql"
# Apply remotely
bunx wrangler d1 execute <DB_NAME> --remote \
--file="./migrations/0002_<description>.sql"
Migration Naming Convention
Place sequentially numbered SQL files in the migrations/ directory:
0001_init.sql0002_add_exercises.sql0003_add_index.sql
Prisma Client Generation
bunx prisma generate
Must be run after every schema change. Generates typed client at the output path.
Using PrismaClient in Workers
import { PrismaClient } from '@/generated/prisma/client'
import { PrismaD1 } from '@prisma/adapter-d1'
// Inside Workers fetch handler:
const adapter = new PrismaD1(env.DB)
const prisma = new PrismaClient({ adapter })
const users = await prisma.user.findMany()
// Resource cleanup (required)
ctx.waitUntil(prisma.$disconnect())
Important:
env.DBmust match thebindingname inwrangler.toml/wrangler.jsoncd1_databases- Always call
prisma.$disconnect()— prevents memory leaks - Use
ctx.waitUntil()to disconnect asynchronously after response is sent
wrangler.toml D1 Binding Configuration
[[d1_databases]]
binding = "DB"
database_name = "your-db-name"
database_id = "your-db-id"
prisma.config.ts (for migrations)
import 'dotenv/config'
import { defineConfig, env } from 'prisma/config'
export default defineConfig({
schema: 'prisma/schema.prisma',
migrations: {
path: 'prisma/migrations'
},
datasource: {
url: env('DATABASE_URL')
}
})
.env:
DATABASE_URL="file:./prisma/db.sqlite"
Common Pitfalls
prisma migrate devdoes not work — D1 requiresmigrate diff+wrangler d1 executeworkflow- Forgetting
prisma generate— client must be regenerated after schema changes - Binding name mismatch —
bindinginwrangler.tomlmust matchenv.DBin code - Missing
$disconnect()— causes Workers memory exhaustion - Local and remote are separate — migrations must be applied independently with
--localand--remote - SQLite limitations — D1 is SQLite-based, so some Prisma features like
@db.TextandJsontype are unavailable - No interactive transactions — D1 does not support Prisma's interactive
$transaction(async (tx) => …); it always fails at runtime. Use a batch$transaction([...])of array-form operations, or restructure the code to avoid a transaction