feat: add blog API Node.js/Docker example#121
Merged
Conversation
Complete working example for the Node.js/Docker deployment path, complementing the Cloudflare Workers todo example with the features a realistic API needs beyond basic CRUD: - users -> posts -> comments with Drizzle relations, cascade deletes, and embedded public author objects - JWT auth with jose (HS256, issuer/audience validation, 15m access + 7d refresh rotation); passwords hashed with Node's built-in scrypt - offset pagination with a meta envelope on every list endpoint - Zod validation on all bodies, queries, and params with structured VALIDATION_ERROR details - 56 Vitest tests; integration tests run the real app against PGlite (in-process Postgres) and apply the committed migrations, so pnpm test needs no Docker or external database - docker compose up builds the image, starts PostgreSQL 17, applies migrations via a one-shot job running the compiled migrator inside the production image, and health-checks the API - packageManager pinned so the in-image pnpm matches the committed lockfile (pnpm@latest's minimumReleaseAge policy rejects same-day dependency releases at image build time) Generated with scripts/setup-project.sh blog-api-node --node, then extended; the starter layout, configs, Dockerfile, and quality gates are kept as generated. Fixes #19 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
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.
Fixes #19
What this adds
examples/blog-api-node/— a complete, working blog API for the Node.js/Docker deployment path, complementing the Cloudflare Workers todo example. It demonstrates the features a realistic API needs beyond basic CRUD, built on top of an unmodifiedsetup-project.sh blog-api-node --noderun (starter layout, configs, Dockerfile, and quality gates kept as generated).relations(),ON DELETE CASCADE, embedded public author objects that never leak email/password hash)POST /auth/register,/auth/login,/auth/refresh+GET /auth/me;joseHS256 with issuer/audience validation, 15m access + 7d refresh rotation, type claims so tokens can't cross roles; passwords hashed with Node's built-in scrypt (no native deps in the Alpine image)?limit=&offset=with{ data, meta: { total, limit, offset } }on every list endpointzValidatorwrapper that emits the documented{ error: { code, message, details } }envelope0000_init.sql), drizzle-kit for dev, and a compiled programmatic migration runner for deploysdocker compose upbuilds the image, starts PostgreSQL 17, applies migrations via a one-shotmigrateservice, then starts the API gated onservice_completed_successfully+ a/healthcheckpnpm db:seed) and in-container (docker compose run --rm migrate node dist/db/seed.js)Design notes
createApp(db)around a driver-agnosticDatabasetype, so the 56 Vitest tests run the exact production code against PGlite (in-process Postgres) —pnpm testneeds no Docker and no database, mirroring how the todo example uses Miniflare for D1. The test harness applies the committed migrations, so the migration SQL is exercised on every test run.pnpm prune --prod, sotarget: buildhas no drizzle-kit. The composemigrateservice instead runsnode dist/db/migrate.js(drizzle-orm's migrator is a runtime dependency) inside the same production image as the API — one image, no dev tooling in containers.packageManager: pnpm@10.15.1is pinned in the example's package.json: the latest pnpm enforces aminimumReleaseAgesupply-chain policy by default and rejected the committed lockfile duringdocker buildbecauseeslint@10.5.0was published the same day. Pinning makes the in-image pnpm match the lockfile-creating one. (Heads-up:templates/node-server/Dockerfileusescorepack prepare pnpm@latest, so freshly generated projects can hit the same build failure within 24h of any dependency release — possible follow-up issue.)Verification
pnpm typecheck✓,pnpm lint✓ (0 errors),pnpm exec prettier --check✓pnpm test— 56/56 pass;pnpm test:coveragemeets all 80% thresholds (91.9% stmts / 82.2% branch / 84.8% funcs / 92.6% lines)docker compose up -d --build --wait→ migrate exited 0, API healthy; seeded in-container; then 11 live checks against the running stack all passed (health, seeded login, register, refresh round-trip, 401 guard, post creation, pagination meta + public author, comments, 403 ownership, NOT_FOUND envelope);docker compose down -vclean.🤖 Generated with Claude Code