|
| 1 | +# sumak + Nitro 3 |
| 2 | + |
| 3 | +Standalone Nitro server — no Nuxt on top. Same runtime Nuxt uses internally, but you get to write the whole server surface directly: file-system routes, auto-imported utils, typed event handlers, and deploy presets for Node / Deno / Bun / Cloudflare Workers / AWS Lambda / Vercel. |
| 4 | + |
| 5 | +## What it shows |
| 6 | + |
| 7 | +- **`utils/db.ts` is auto-imported everywhere.** Nitro lifts every export under `utils/` into the global scope of route handlers — no `import { db } from "../../utils/db.ts"` noise. The Pool sits on `globalThis.__pgPool` so HMR doesn't duplicate it. |
| 8 | +- **`plugins/close-pool.ts` hooks Nitro's `close` event.** Every dev-server reload and every production graceful shutdown triggers `close`; we end the shared Pool there. Without this hook, HMR leaks connections on every save and Postgres eventually refuses new clients. |
| 9 | +- **File-system method dispatch.** `events.get.ts` / `events.post.ts` — the suffix drives the HTTP method, no manual `if (event.method === ...)`. Less boilerplate, same types. |
| 10 | +- **Transactional sequence counter.** The POST handler reads `MAX(seq)` and inserts the new row inside a single `db.transaction(async tx => ...)` — both statements share a connection, so two concurrent producers can't hand out the same sequence number. |
| 11 | +- **Typed query + runtime config.** `useRuntimeConfig().databaseUrl` reads from env at runtime; sumak's `tables` record drives the typed builder; schema lives in `utils/schema.ts` so both route handlers and `sumak migrate` share it. |
| 12 | + |
| 13 | +## Structure |
| 14 | + |
| 15 | +``` |
| 16 | +nitro.config.ts — compatibilityDate + runtime config |
| 17 | +utils/ |
| 18 | + schema.ts — shared table definitions (auto-imported) |
| 19 | + db.ts — Pool + sumak singleton (auto-imported) |
| 20 | +plugins/ |
| 21 | + close-pool.ts — ends the Pool on Nitro `close` (HMR + shutdown) |
| 22 | +routes/ |
| 23 | + api/ |
| 24 | + events.get.ts — list / cursor-paginate |
| 25 | + events.post.ts — ingest with monotonic seq |
| 26 | +``` |
| 27 | + |
| 28 | +## Run |
| 29 | + |
| 30 | +```bash |
| 31 | +export DATABASE_URL="postgres://postgres:pg@localhost:5432/postgres" |
| 32 | +pnpm install |
| 33 | +pnpm migrate |
| 34 | +pnpm dev |
| 35 | +``` |
| 36 | + |
| 37 | +```bash |
| 38 | +curl -X POST http://localhost:3000/api/events \ |
| 39 | + -H 'content-type: application/json' \ |
| 40 | + -d '{"source":"checkout","payload":{"orderId":42}}' |
| 41 | +curl http://localhost:3000/api/events?limit=10 |
| 42 | +``` |
| 43 | + |
| 44 | +## Deploy |
| 45 | + |
| 46 | +```bash |
| 47 | +# Node (default preset) |
| 48 | +pnpm build |
| 49 | +node .output/server/index.mjs |
| 50 | + |
| 51 | +# Cloudflare Workers |
| 52 | +NITRO_PRESET=cloudflare_module pnpm build |
| 53 | + |
| 54 | +# AWS Lambda |
| 55 | +NITRO_PRESET=aws-lambda pnpm build |
| 56 | +``` |
| 57 | + |
| 58 | +Pick the preset that matches your target. sumak's pg driver runs on any of them; on edge runtimes you may want to swap to an HTTP-based driver (e.g. Neon's serverless driver) since long-lived pg connections don't fit the Workers connection model — but that's your call, not sumak's. |
0 commit comments