Skip to content

feat: add blog API Node.js/Docker example#121

Merged
PAMulligan merged 1 commit into
mainfrom
19-create-example-project-blog-api-nodejsdocker
Jun 13, 2026
Merged

feat: add blog API Node.js/Docker example#121
PAMulligan merged 1 commit into
mainfrom
19-create-example-project-blog-api-nodejsdocker

Conversation

@PAMulligan

Copy link
Copy Markdown
Contributor

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 unmodified setup-project.sh blog-api-node --node run (starter layout, configs, Dockerfile, and quality gates kept as generated).

  • Resources with relations — users → posts → comments (Drizzle relations(), ON DELETE CASCADE, embedded public author objects that never leak email/password hash)
  • JWT authenticationPOST /auth/register, /auth/login, /auth/refresh + GET /auth/me; jose HS256 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)
  • Pagination — offset-based ?limit=&offset= with { data, meta: { total, limit, offset } } on every list endpoint
  • Zod validation on every body/query/param via a zValidator wrapper that emits the documented { error: { code, message, details } } envelope
  • Drizzle + PostgreSQL — committed SQL migration (0000_init.sql), drizzle-kit for dev, and a compiled programmatic migration runner for deploys
  • Docker orchestrationdocker compose up builds the image, starts PostgreSQL 17, applies migrations via a one-shot migrate service, then starts the API gated on service_completed_successfully + a /health check
  • Seed script — 2 users / 3 posts / 2 comments, runnable locally (pnpm db:seed) and in-container (docker compose run --rm migrate node dist/db/seed.js)
  • README — prerequisites, compose quick start, full endpoint table, curl examples with JSON responses, error envelope docs, and a text-based ER diagram
  • Main README Quick Start now links both examples

Design notes

  • Testable composition. The app is built by createApp(db) around a driver-agnostic Database type, so the 56 Vitest tests run the exact production code against PGlite (in-process Postgres) — pnpm test needs 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.
  • Migrations in containers. The Dockerfile's build stage ends with pnpm prune --prod, so target: build has no drizzle-kit. The compose migrate service instead runs node 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.
  • Reproducible image builds. packageManager: pnpm@10.15.1 is pinned in the example's package.json: the latest pnpm enforces a minimumReleaseAge supply-chain policy by default and rejected the committed lockfile during docker build because eslint@10.5.0 was published the same day. Pinning makes the in-image pnpm match the lockfile-creating one. (Heads-up: templates/node-server/Dockerfile uses corepack 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 test56/56 pass; pnpm test:coverage meets all 80% thresholds (91.9% stmts / 82.2% branch / 84.8% funcs / 92.6% lines)
  • Full Docker e2e on this machine: 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 -v clean.

🤖 Generated with Claude Code

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create example project: Blog API (Node.js/Docker)

1 participant