docs(d1): add complete guide to d1#31263
Conversation
|
This pull request requires reviews from CODEOWNERS as it changes files that match the following patterns:
|
|
CI run failed: build logs |
Review
Warnings (31)
Suggestions (2)
CommandsOnly codeowners can run commands. Post a comment with the command to trigger it.
|
There was a problem hiding this comment.
This PR needs significant revision before merge. The new page is missing required frontmatter, uses full URLs for internal links and images, relies on bare code fences instead of mandatory components, and has multiple style guide violations.
CRITICAL
- Missing frontmatter — Every docs page requires
title,description, andpcx_content_type. The file opens with an H1 (# The Complete Guide...), which must be removed; the H1 is rendered from frontmatter. - Bare code fences — Workers JS/TS examples must use
TypeScriptExample, Wrangler config must useWranglerConfig(with$todayforcompatibility_date), and package install commands must usePackageManagers. - Full URLs for internal links — Links to
developers.cloudflare.commust be root-relative (/d1/.../). Images should use~/assets/images/d1/...if they exist in the repo. - Missing component imports — All components used must be imported from
~/components.
HIGH
- Emojis used as admonitions —
💡 Tipand⚠️ Warningmarkers must be replaced with:::tip,:::caution, and:::noteadmonitions. - Contractions — "don't", "doesn't", "Can't" must be rewritten per the style guide ("do not", "does not", "Cannot").
- Headings with punctuation/questions — "Is D1 Right for You?", "I Can't Connect...", etc. must use sentence-case noun/verb phrases without trailing punctuation.
- LLM-filler phrases — Remove "seamless" from the opening blurb and "etc." from headings.
- Hardcoded compatibility date — Use
$todayinsideWranglerConfig.
MEDIUM
- Misleading SQL example — The "Bad" example on line 219 is identical to the "Good" example apart from a comment. A full table scan and an indexed lookup look the same in SQL; the example should be clarified.
- Unused variable —
offsetis declared in the chunk-deletion sample (line 315) but never used. - Limit claims — Values in the limits table (lines 449–458) should be cross-checked against the current D1 limits page for accuracy.
Out-of-scope note: Consider whether this content should be split into multiple pages (tutorial, troubleshooting, reference) or if linking to existing canonical pages more aggressively would reduce duplication.
| # The Complete Guide to Cloudflare D1 | ||
|
|
||
| > A practical, opinionated guide for developers new to Cloudflare, Workers users ready for seamless storage, and AI agents building apps for hobbyists. | ||
|
|
||
| --- |
There was a problem hiding this comment.
Add required frontmatter and remove the H1. The page title renders from frontmatter.
| # The Complete Guide to Cloudflare D1 | |
| > A practical, opinionated guide for developers new to Cloudflare, Workers users ready for seamless storage, and AI agents building apps for hobbyists. | |
| --- | |
| --- | |
| title: The complete guide to Cloudflare D1 | |
| pcx_content_type: tutorial | |
| description: A comprehensive tutorial for implementing Cloudflare D1 in production environments, covering architecture, optimization, schema migrations, and troubleshooting. | |
| products: | |
| - d1 | |
| --- | |
| import { PackageManagers, TypeScriptExample, WranglerConfig } from "~/components"; | |
| A practical, opinionated guide for developers new to Cloudflare, Workers users ready for storage, and AI agents building apps for hobbyists. |
|
|
||
| --- | ||
|
|
||
| ## Is D1 Right for You? |
There was a problem hiding this comment.
Use a sentence-case noun phrase and remove trailing punctuation.
| ## Is D1 Right for You? | |
| ## Determine if D1 is right for you |
|
|
||
| ### The Rule of Thumb | ||
|
|
||
| If you need a SQL database for a Cloudflare Worker or Pages project, and you don't need exotic PostgreSQL features like `JSONB` operators, `SERIAL` types, or stored procedures, **D1 is the path of least resistance**. |
There was a problem hiding this comment.
Rewrite the contraction.
| If you need a SQL database for a Cloudflare Worker or Pages project, and you don't need exotic PostgreSQL features like `JSONB` operators, `SERIAL` types, or stored procedures, **D1 is the path of least resistance**. | |
| If you need a SQL database for a Cloudflare Worker or Pages project, and you do not need exotic PostgreSQL features like `JSONB` operators, `SERIAL` types, or stored procedures, **D1 is the path of least resistance**. |
|
|
||
| ```bash | ||
| npm install -g wrangler | ||
| npx wrangler login |
There was a problem hiding this comment.
Use PackageManagers for install commands.
| ```bash | |
| npm install -g wrangler | |
| npx wrangler login | |
| <PackageManagers type="install" pkg="wrangler" /> | |
| ```sh | |
| npx wrangler login |
| name = "my-d1-app" | ||
| main = "src/index.js" | ||
| compatibility_date = "2024-01-01" | ||
|
|
||
| [[d1_databases]] | ||
| binding = "DB" # how you refer to it in code | ||
| database_name = "my-first-db" | ||
| database_id = "your-uuid-here" | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Use WranglerConfig and $today for the compatibility date.
| name = "my-d1-app" | |
| main = "src/index.js" | |
| compatibility_date = "2024-01-01" | |
| [[d1_databases]] | |
| binding = "DB" # how you refer to it in code | |
| database_name = "my-first-db" | |
| database_id = "your-uuid-here" | |
| ``` | |
| <WranglerConfig> | |
| ```toml | |
| name = "my-d1-app" | |
| main = "src/index.js" | |
| compatibility_date = "$today" | |
| [[d1_databases]] | |
| binding = "DB" # how you refer to it in code | |
| database_name = "my-first-db" | |
| database_id = "your-uuid-here" |
| ```javascript | ||
| export default { | ||
| async fetch(request, env) { | ||
| const url = new URL(request.url); | ||
| const slug = url.pathname.slice(1); | ||
|
|
||
| if (request.method === "POST" && slug === "create") { | ||
| const body = await request.json(); | ||
| await env.DB.prepare( | ||
| "INSERT INTO links (slug, url) VALUES (?, ?)" | ||
| ).bind(body.slug, body.url).run(); | ||
| return new Response("Created", { status: 201 }); | ||
| } | ||
|
|
||
| const result = await env.DB.prepare( | ||
| "SELECT url FROM links WHERE slug = ?" | ||
| ).bind(slug).first(); | ||
|
|
||
| if (result) { | ||
| return Response.redirect(result.url, 302); | ||
| } | ||
|
|
||
| return new Response("Not found", { status: 404 }); | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| Notice: `env.DB` is just there. No connection strings. No TLS certificates. No connection pools. |
There was a problem hiding this comment.
Use TypeScriptExample for Workers code.
| ```javascript | |
| export default { | |
| async fetch(request, env) { | |
| const url = new URL(request.url); | |
| const slug = url.pathname.slice(1); | |
| if (request.method === "POST" && slug === "create") { | |
| const body = await request.json(); | |
| await env.DB.prepare( | |
| "INSERT INTO links (slug, url) VALUES (?, ?)" | |
| ).bind(body.slug, body.url).run(); | |
| return new Response("Created", { status: 201 }); | |
| } | |
| const result = await env.DB.prepare( | |
| "SELECT url FROM links WHERE slug = ?" | |
| ).bind(slug).first(); | |
| if (result) { | |
| return Response.redirect(result.url, 302); | |
| } | |
| return new Response("Not found", { status: 404 }); | |
| } | |
| }; | |
| ``` | |
| Notice: `env.DB` is just there. No connection strings. No TLS certificates. No connection pools. | |
| Create `src/index.ts`: | |
| <TypeScriptExample> | |
| ```ts | |
| export default { | |
| async fetch(request, env): Promise<Response> { | |
| const url = new URL(request.url); | |
| const slug = url.pathname.slice(1); | |
| if (request.method === "POST" && slug === "create") { | |
| const body = await request.json(); | |
| await env.DB.prepare( | |
| "INSERT INTO links (slug, url) VALUES (?, ?)" | |
| ).bind(body.slug, body.url).run(); | |
| return new Response("Created", { status: 201 }); | |
| } | |
| const result = await env.DB.prepare( | |
| "SELECT url FROM links WHERE slug = ?" | |
| ).bind(slug).first(); | |
| if (result) { | |
| return Response.redirect(result.url, 302); | |
| } | |
| return new Response("Not found", { status: 404 }); | |
| } | |
| }; |
| } | ||
| ``` | ||
|
|
||
| See the [error list](https://developers.cloudflare.com/d1/observability/debug-d1/#error-list) for exactly which errors are retryable. |
There was a problem hiding this comment.
Use root-relative paths for internal links.
| See the [error list](https://developers.cloudflare.com/d1/observability/debug-d1/#error-list) for exactly which errors are retryable. | |
| See the [error list](/d1/observability/debug-d1/#error-list) for exactly which errors are retryable. |
|
|
||
| ### 9. Use `db.withSession()` When Read Replication Is Enabled | ||
|
|
||
| Read replicas reduce latency for read-heavy apps with global users. Even if you don't enable read replication today, design your code to use the **Sessions API** from the start. This lets you toggle replication on later without changing Worker code. |
There was a problem hiding this comment.
Rewrite the contraction.
| Read replicas reduce latency for read-heavy apps with global users. Even if you don't enable read replication today, design your code to use the **Sessions API** from the start. This lets you toggle replication on later without changing Worker code. | |
| Even if you do not enable read replication today, design your code to use the **Sessions API** from the start. This lets you toggle replication on later without changing Worker code. |
| - Do **not** use `PRAGMA foreign_keys = off`; it is silently ignored. | ||
| - Resolve all violations before the transaction commits. | ||
|
|
||
| ### I Can't Connect from My Local GUI (TablePlus, DBeaver, etc.) |
There was a problem hiding this comment.
Use a sentence-case noun phrase without punctuation or contractions.
| ### I Can't Connect from My Local GUI (TablePlus, DBeaver, etc.) | |
| ### Cannot connect from a local GUI (TablePlus, DBeaver) |
|
|
||
| ### My Read Replica Data Is Stale | ||
|
|
||
| **Symptom:** A user submits a form, then reloads the page and doesn't see their update. |
There was a problem hiding this comment.
Rewrite the contraction.
| **Symptom:** A user submits a form, then reloads the page and doesn't see their update. | |
| **Symptom:** A user submits a form, then reloads the page and does not see their update. |
|
I reviewed PR #31263 and posted a detailed review with 10 inline suggestions. Summary of findings:
I also applied the |
Summary
This documentation adds a comprehensive, end-to-end tutorial titled "D1 Complete Guide" (
d1-complete-guide.mdx) under the D1 best practices section.The guide provides developers with a structured workflow for implementing Cloudflare D1 in production environments, filling the gap between a basic quick-start tutorial and isolated optimization tips.
Key Sections Included:
wrangler.toml, and handling schema migrations securely.