HOSTING FOR AI-BUILT APPS
Deploy a Next.js blog with PostgreSQL on Yeeted
A Yeeted sample: Next.js App Router on managed postgres. What it demonstrates:
- The
next.jsbuild branch: apackage-lock.jsonpicksnpm ci, the synthesized image runsnext build, andnext startserves on$PORT. - Managed postgres with self-migration:
DATABASE_URLis injected by the platform; the app appliesmigrations/*.sqlin order on first use, tracked inschema_migrations(tracked across sequential restarts). - Graceful degradation: no
DATABASE_URLruns an in-memory store, which is what the unit tests use and whatnext buildprerenders against.
The two things that catch people
next build executes your server components. It prerenders every route it
can, at build time, on a machine with no database. A page that queries postgres
without opting out is either a build failure or, worse, a static file baked
from an empty store and served forever, so the app deploys clean and shows
nothing. app/page.js and the API route set export const dynamic =
"force-dynamic"; /stats deliberately does not, and revalidates instead.
pg must stay out of the bundle. serverExternalPackages: ["pg"] in
next.config.mjs. Without it next build tries to resolve pg's optional
native bindings at compile time and fails.
Routes
| Route | What it is |
|---|---|
/ |
server-rendered per request, lists posts from postgres |
/stats |
statically generated, revalidate = 10 |
/api/posts |
GET lists, POST creates ({"title","body"}) |
/healthz |
ok |
/stats is the one route that makes next start write to .next/cache at
runtime, on a root filesystem the platform mounts read-only except for /tmp.
It is here so that the case is exercised rather than avoided.
Tests: npm test (unit, zero services) · ./smoke.sh https://... (smoke).
Run locally
Use a Node.js version supported by the pinned Next.js dependency (Node 20.9+
for Next.js 16). From node/blog-next inside the examples tree:
npm ci
npm test
npm run dev
Open http://localhost:3000. Without DATABASE_URL, posts live in memory and
are lost on restart. For a database-backed local run, provide a PostgreSQL
connection string through your environment before starting the server. The
unit tests exercise the memory store, not PostgreSQL or rendering.
Deploy and verify
Follow the deployment walkthrough, using this
folder as the source root. Include the lockfile, next.config.mjs, migrations,
and yeeted.yaml. The manifest requests PostgreSQL. The store opens lazily,
applies its migration on first use, and exposes its kind in API responses.
sh smoke.sh "$PREVIEW_URL"
Expect health and page checks to pass, a new post to return 201, and
GET /api/posts to contain the new post and "store":"postgres". The
script also fetches /stats and rejects a blank title with 400. Merely
rendering /stats does not verify that its cached count eventually changes;
check again after its revalidation interval when testing that behavior.
For a manual write:
curl -i "$PREVIEW_URL/api/posts" -H 'Content-Type: application/json' -d '{"title":"My first Yeeted post","body":"Deployed from my coding agent."}'
curl -fsS "$PREVIEW_URL/api/posts"
What to change before real use
There is no author authentication. Migrations are recorded, but the sample migration runner does not serialize concurrent instances. Use a coordinated migration step before running multiple replicas. A rollback of application code does not undo a schema migration. Verify production rendering and PostgreSQL behavior separately from the in-memory unit tests.