Simplify the canonical D1 Worker example - #49
Merged
Conversation
`examples/d1-worker/` and `test/miniflare/` each carried their own `.prettierrc`, `.editorconfig` and `.gitignore` — byte-identical copies, so one code style was maintained in two places and disagreed with the root in all three files. The sub-projects now inherit the repository configuration: 120 columns, spaces, double quotes. The hand-written sources in those directories sit beside checked-in plugin output, which has always been 2-space and double-quoted. Under the old split the example that demonstrates the generated code looked foreign next to it. The `.gitignore` copies were redundant — every pattern already appears in the root, which additionally ignores `.env*`. The `.editorconfig` copies also carried a latent bug: their `[*.yml]` section never matched `sqlc.yaml`, the only file it was meant to protect, so YAML inherited `indent_style = tab`. Tabs are illegal for indentation in YAML, and any editorconfig-aware editor would have written a file sqlc cannot parse.
`examples/d1-worker/` and `test/miniflare/` were the only parts of the repository on bun, for no reason the tree records. Everything else — the root package, CI, the release path — already runs on the pinned Node and npm, so the two fixtures brought a second package manager, a second lockfile format and a pinned `oven-sh/setup-bun` along for the ride. `bun.lock` becomes `package-lock.json` in both, `make test-miniflare` and `make test-example` run `npm ci` and `npm run`, and the setup action loses its `bun` input, its install step and its version echo. Nothing outside those two directories consumed bun. `examples/d1-worker/CLAUDE.md` existed solely to tell an agent to reach for bun there; npm needs no such instruction. The root `.prettierignore` already ignores `package-lock.json` at any depth, so the two `bun.lock` entries leave with nothing to replace them. `npm audit` reports seven findings that bun never surfaced — esbuild, sharp, undici and ws, all transitive under miniflare, wrangler and vitest-pool-workers. They are dev-only and predate this change; the Cloudflare versions in these lockfiles are a deliberately pinned test environment, so bumping them is a separate decision.
The example exists to show what consuming the generated D1 code looks like, and most of it was showing something else: manual `URL` parsing, a path regex, method branching by hand, and a `try`/`catch` wrapped around the whole request. Routing plumbing outnumbered the query calls it was supposed to frame. Hono replaces all of it. `app.get`/`app.patch` declare the two paths, `c.req.param` supplies the id, and `app.onError` keeps the one piece of error handling that is actually about this project: `SqlcD1Error` means a generated query failed, anything else does not. `parseId` and `isRenameBody` are gone with no validation layer put in their place, because the generated runtime already validates its own arguments. A malformed id or a missing name raises `QueryArgumentError` before D1 is called, so the test that used to assert hand-written 400s now asserts the generated argument checks firing — a demonstration of the library rather than of HTTP plumbing. The trade is that bad input answers 500 rather than 400; a real API would validate at the edge, but this one is here to show the query surface. The 405 responses went too. They needed two fallback routes registered in a specific order, and nothing about D1 is clearer for having them; an unhandled method now gets Hono's 404. Net effect on the file: 46 lines to 35, one dependency, and every remaining line is a query call, a session, or the D1 binding.
The matrix mirrors each fixture without `node_modules` and type-checks it
with the root TypeScript, which held while every hand-written fixture
source imported nothing. The Worker now imports Hono, so both cells
failed on `Cannot find module 'hono'` — a dependency the matrix has no
reason to resolve.
Excluding the hand-written files is not sufficient on its own: `wrangler
types` writes `mainModule: typeof import("./src/index")` into
worker-configuration.d.ts, and TypeScript cannot exclude a file reached
through a reference, so the entry point comes back through the type
declarations. The matrix therefore includes the generated directory
alone. D1 types already come from the root `@cloudflare/workers-types`
that this script pins into `compilerOptions.types`.
This narrows the matrix to the question it exists to answer — does the
plugin's output compile under each supported sqlc version — and it still
compiles both emitted files. The hand-written sources keep their
type-check in `make test-example` and `make test-miniflare`, which run
against real installs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The canonical example was spending most of its lines on HTTP plumbing rather than on the generated D1 code it exists to demonstrate. Three commits, each independently green, so the mechanical reformat stays out of the way of the substantive changes.
1. Unify formatting on the repository configuration
examples/d1-worker/andtest/miniflare/each carried byte-identical copies of.prettierrc,.editorconfigand.gitignore, so one code style was maintained in two places and disagreed with the root in all three. They now inherit the repository configuration — 120 columns, spaces, double quotes.The win beyond deduplication: those directories hold checked-in plugin output, which has always been 2-space and double-quoted. Under the old split, the example demonstrating the generated code looked foreign sitting next to it.
The
.editorconfigcopies also carried a latent bug. Their[*.yml]section never matchedsqlc.yaml, the only file it was meant to protect, so YAML inheritedindent_style = tab— illegal for indentation in YAML. Any editorconfig-aware editor would have written a file sqlc cannot parse.2. Run the fixture sub-projects on npm
Those two directories were the only part of the repository on bun, for no reason the tree records. Everything else already runs on the pinned Node and npm, so they brought a second package manager, a second lockfile format and a pinned
oven-sh/setup-bunalong with them.bun.lockbecomespackage-lock.json, the Makefile targets runnpm ci/npm run, and the setup action loses itsbuninput, install step and version echo.3. Rewrite the canonical Worker on Hono
Manual
URLparsing, a path regex, hand-rolled method branching and a request-widetry/catchare gone. What remains is two route declarations, the query calls, and oneonErrorthat keeps the only error handling actually about this project —SqlcD1Errormeans a generated query failed, anything else does not.No validation layer replaced
parseIdandisRenameBody, because the generated runtime already validates its own arguments. A malformed id or missing name raisesQueryArgumentErrorbefore D1 is called:GET /users/not-an-idquery_failed—QueryArgumentErrorfrom the generated codePATCH {"nickname":"…"}query_failed— samePATCHwith malformed JSONinternal_error— Hono's parse error, correctly not labelled a query failureSo the malformed-input test changed character rather than disappearing: it now asserts the generated argument checks firing, which is a demonstration of the library instead of a test of hand-written HTTP plumbing.
Trade-offs, deliberate: bad input answers 500 rather than 400 — a real API would validate at the edge, but this one exists to show the query surface. The 405 responses went too; they needed two fallback routes in a specific registration order, and nothing about D1 is clearer for having them.
src/index.tsgoes from 46 lines to 35, with one dependency added.Verification
make clean && make testfrom a fresh plugin build: 80 unit, 27 candidate, drift clean, 33 miniflare, 5 example.make fmt-checkclean repo-wide.wrangler deploy --dry-runbundles the example at 87.93 KiB with theenv.DBbinding resolved, which exercises the new dependency through wrangler's bundler rather than only vitest's. Commits 1 and 2 were each checked out into a worktree and verified on their own sogit bisectnever lands on a broken tree. The four generated fixture files are byte-identical —make fmtnever touched plugin output.One thing left open
npm auditreports seven findings that bun never surfaced — esbuild, sharp, undici and ws, all transitive under miniflare, wrangler and vitest-pool-workers. Dev-only and predating this PR. Those lockfiles are a deliberately pinned test environment, so bumping them is a separate decision rather than something to slip in here.