Cloudflare D1 App¶
Status: Authored
Cloudflare Workers or Pages Functions using D1 with versioned SQL migrations and environment-specific bindings.
Full rubric¶
Reference implementation¶
| Score evidence | Value |
|---|---|
| Repository | vibecodeqa/ref-cloudflare-d1-app (published) |
| VCQA report | 92/100 |
| Verification | self-reported |
| Assessed commit | 167e6e4 |
| Assessed on | 2026-07-25 |
| CI run | success (2026-07-25) |
| Independent assessment | None published yet. The score above is the reference repo's own claim, not third-party proof. |
| Catalog entry last verified | 2026-08-08 |
Reference template map¶
| Evidence | Where to look | What it proves |
|---|---|---|
| Per-environment bindings | wrangler.toml |
Local, preview, and production D1 databases are declared separately and selected on purpose. |
| Versioned migrations | migrations/ |
Schema history is ordered SQL files, not ad hoc console statements. |
| Drift guard | migrations/manifest.json, scripts/check-migration-manifest.mjs |
An applied migration cannot be edited without the checksum check failing. |
| Clean local apply gate | scripts/check-migrations.mjs |
Every migration is proved to apply to an empty database before remote promotion. |
| Query safety | src/db.ts, scripts/check-query-safety.mjs |
Request values reach SQL through prepared-statement binding, and the rule is enforced by a script rather than by review habit. |
| HTTP boundary tests | src/http.test.ts, src/db.test.ts |
Query helpers and the request boundary are covered, not just the happy path. |
| Deploy ordering | .github/workflows/deploy.yml, scripts/check-deploy-shape.mjs |
CI gates run, then production migrations apply, then code deploys - in that order. |
| Operations | docs/runbook.md |
Migration failure and rollback have a written procedure. |
| Score evidence | docs/vcqa-report.md |
The template carries a tracked VCQA score and visible gaps. |
What this teaches¶
Choose this stack when a Cloudflare runtime owns a SQL database and the schema has to change over time without losing data. The interesting part is not writing D1 queries - Cloudflare documents that - it is that a database is the one part of a deploy you cannot roll back by redeploying the previous artifact. Code is replaceable; applied schema is not.
Everything this standard judges follows from that asymmetry. Migrations are append-only once a shared environment has applied them, because editing an applied file makes source history and database history disagree with no way to detect it later - hence a checksum manifest or equivalent drift guard. Migrations must be proved to apply to a clean database in CI, because the only environment where an untested migration is cheap is a disposable one. Environments must be distinct databases, because a single shared database turns a preview experiment into a production incident. Tenant isolation must be a declared model rather than an assumption, because "we always pass the tenant id" is not a control. And deploy ordering must be explicit, because a release whose code expects a column that has not landed fails in the least recoverable place.
Do not choose this stack for generic SQL style guidance, for non-Cloudflare databases, or for a repo that merely reads a D1 binding it does not own the schema of.
Decision matrix¶
| Need | Better fit |
|---|---|
| A Cloudflare runtime that owns D1 schema and migrations | Cloudflare D1 App. |
| A Pages app with an API but no database of its own | Cloudflare Pages Fullstack alone. |
| A Pages app with an API and D1 | Both standards; this one owns schema, migrations, and query safety. |
| Key-value or object storage only (KV, R2) | Not this standard; binding scope is judged by the hosting stack standard. |
| A database outside Cloudflare (Postgres, Turso, PlanetScale) | Not this standard. The migration doctrine transfers; the rules and detection signals do not. |
| Per-tenant databases, provisioning, and promotion gates | Tenant-Deployed Cloudflare SaaS plus this standard. |
| Reading a D1 binding whose schema another repo owns | The consuming stack's standard; schema rules belong to the owner. |
Upstream references¶
Cloudflare owns D1 and Wrangler doctrine; VCQA owns the migration, drift, isolation, and deploy-ordering glue that no single upstream page covers.
- Cloudflare D1 documentation
- D1 migrations
- D1 prepared statement methods
- D1 environments
- D1 local development
- D1 Time Travel and backups
- D1 import and export data
- Wrangler D1 commands
- Pages Functions bindings
- GitHub Actions secure use
- OWASP Top 10
Scope¶
Cloudflare Workers or Pages Functions using D1 with versioned SQL migrations and environment-specific bindings.
Not in scope¶
- generic SQL style
- non-Cloudflare database deployments
- frontend state management
Composes¶
VCQA-owned rule surface¶
- append-only/versioned migrations.
- migration checksum or drift guard.
- local apply test.
- staging/prod/tenant database isolation.
- parameterized query enforcement.
Detection signals¶
[[d1_databases]]in Wrangler configmigrations/*.sql- D1 binding usage in app code
Combination-born guidelines¶
- Migrations are append-only after deployment; editing applied migrations is a defect.
- CI applies migrations to a clean local database before production deployment.
- Tenant, staging, preview, and production databases need distinct binding boundaries.
Rule highlights¶
- R-SHAPE-1: The runtime boundary is Cloudflare-owned. D1 access belongs in Workers, Pages Functions, or server code compiled to those runtimes, never in browser code.
- R-BIND-1: Binding names are stable and typed at the boundary. D1 bindings should be
represented in generated Cloudflare types or explicit
Env/Bindingsinterfaces. - R-MIG-1: Migrations are append-only after shared apply. A migration that may have reached preview, staging, production, or a tenant database is changed only by a later migration.
- R-MIG-3: Clean local migration apply is a required gate. CI applies all migrations to a clean local D1 database before remote promotion.
- R-DRIFT-1: Applied migration identity is drift-checked. A checksum manifest, protected migration history, or equivalent guard proves source history still matches applied database history.
- R-ENV-1: Local, preview/staging, and production use distinct databases. Wrangler and Pages bindings must make the target database explicit.
- R-TENANT-1: Tenant isolation model is declared and enforced. Multi-tenant apps choose per-tenant database, per-tenant binding/service boundary, or shared database with mandatory row/table scoping.
- R-SQL-1: Untrusted values use prepared statement binding. Request data, auth claims,
route params, and tenant IDs enter SQL through
.bind(...), not interpolation. - R-CI-2: Production migrations run before production code deploy. Deploy ordering must match the schema expectations of the release, with documented exceptions for staged rollouts.
- R-DEPLOY-1: Preview deploys do not run production migrations. Pull request and preview workflows use preview/staging databases only.
Limitations¶
- Append-only is judged from repository history. VCQA can see that a migration file changed after it was added; it cannot see which databases had already applied it. A repo that rewrites history, or squashes, can hide a real violation.
- Drift is checked against a manifest, not against the live database. The guard proves source history is internally consistent. Only a query against the deployed database proves the database agrees, and a repository scan does not connect to one.
- Environment isolation is judged from declared config. Databases created and bound through the Cloudflare dashboard rather than in Wrangler config cannot be seen, so the check falls back to evidence-only.
- Tenant isolation is judged as a declared model plus its enforcement points. A shared database with row scoping can satisfy the rule and still leak if one query forgets the predicate; the rubric asks for enforcement you can point at, not a proof of correctness.
- Query safety detection is pattern-based. Prepared-statement binding is detectable; SQL assembled through several helpers before reaching D1 may need human review.
- Backup and restore posture is out of scope for v1. Time Travel and export are cited as upstream references, not judged as rules.
Anti-patterns¶
- Editing a migration after it has been applied to staging or production.
- Reusing one D1 database for preview, staging, and production.
- Building SQL strings with request data.
- Running production migrations from a local laptop with no CI gate.
- Leaving tenant isolation as an implementation assumption instead of a documented model.
Benefits¶
- Cloudflare SaaS example D1 usage.
Maintenance¶
| Maintenance | Value |
|---|---|
| Latest edition | Cloudflare D1 App v1 |
| Pin reports and scans to | /standards/cloudflare-d1-app/v1/ |
| Last reviewed | 2026-07 |
| Next review due | 2027-07 |
| Edition targets | cloudflareD1 latest, wrangler 4, typescript 6 |
| Lifecycle | active |
| Errata | none |
| Composes | cloudflare-d1, cloudflare-pages-functions, cloudflare-workers, typescript, web-security, github-actions |
Editions are cut on material change, not on a calendar. A review that finds the edition
still correct moves Last reviewed forward without a new edition. Metadata lives in
standards/registry.json.