Skip to content
LazyFrog

Next.js with PostgreSQL

Deploy a Next.js app with its own PostgreSQL.

Every LazyFrog project runs your Next.js app as a real Node server beside its own PostgreSQL, one database for the preview and one for the live site. Your AI builds or imports the app, checks it in a real browser and publishes when you say so.

A real example: a book club app

We gave Codex one message on 13 November 2026. It took 29 steps on LazyFrog in about 4 minutes, and nothing failed. The book club is made up.

You

Use LazyFrog to build a small Next.js app for my book club, Page Turners: a page listing the books we've read and the next one, a form to suggest a book (title, author, who suggested it), and an admin page where I can delete suggestions. Keep the books and suggestions in the project's PostgreSQL database with a few sample rows. Call the project page-turners-check. Use sensible defaults, don't ask me questions, and show me the preview when it works. Don't publish.
  1. 1.Read LazyFrog's guide on planning, the database and forms, and made a project from the Next.js template.
  2. 2.Saved a short plan (the pages, the two tables, what counts as done) and, as asked, went ahead on sensible defaults.
  3. 3.Wrote a migration for the books and suggestions tables, applied it to the preview database and added sample books.
  4. 4.Protected /admin with a password page in the project's routing, so deleting needs the owner's password.
  5. 5.Filled in and sent the suggestion form in a real browser on a phone-sized screen, then deleted the suggestion from the admin page, and checked the database each time.
  6. 6.Took screenshots at phone, tablet and desktop widths, ran the design check, fixed what it found and tried the form again.
  7. 7.Committed the work. Nothing was published.

The files that make it run

These are the project's own files, as Codex left them. A new project from the Next.js template starts with the first two.

services.json: what runs. The preview runs dev with hot reload; publishing runs build, then start.

services.json
{
  "services": {
    "web": {
      "type": "web",
      "dev": "exec ./node_modules/.bin/next dev --hostname 0.0.0.0 --port $PORT",
      "build": "pnpm build",
      "start": "exec ./node_modules/.bin/next start --hostname 0.0.0.0 --port $PORT",
      "port": 3000,
      "healthcheck_path": "/api/health"
    }
  }
}

lib/db.ts: one connection pool, from the DATABASE_URL LazyFrog sets in each environment.

lib/db.ts
import pg from "pg";

// One pool per server process. DATABASE_URL is set by the platform in every environment.
const globalForDb = globalThis as unknown as { db?: pg.Pool };

export const db: pg.Pool =
  globalForDb.db ?? new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 5 });

if (process.env.NODE_ENV !== "production") globalForDb.db = db;

migrations/: the schema, as numbered SQL files. Applied to the preview, and to the live database on every publish after a rehearsal on a copy of it.

migrations/0002_book_club.sql
-- migrations/0002_book_club.sql
CREATE TABLE books (
  id serial PRIMARY KEY,
  title text NOT NULL,
  author text NOT NULL,
  status text NOT NULL CHECK (status IN ('read', 'next')),
  note text NOT NULL DEFAULT ''
);
CREATE UNIQUE INDEX one_next_book ON books (status) WHERE status = 'next';
CREATE TABLE suggestions (
  id serial PRIMARY KEY,
  title text NOT NULL CHECK (length(title) BETWEEN 1 AND 200),
  author text NOT NULL CHECK (length(author) BETWEEN 1 AND 200),
  suggested_by text NOT NULL CHECK (length(suggested_by) BETWEEN 1 AND 100),
  created_at timestamptz NOT NULL DEFAULT now()
);

Queries take their values as parameters:

app/suggest/submit.ts
// app/suggest/submit.ts (a server action), shortened
await db.query(
  "INSERT INTO suggestions (title, author, suggested_by) VALUES ($1, $2, $3)",
  [title, author, suggestedBy],
);

Your own app

  1. 1.Connect LazyFrog to Claude, ChatGPT, Codex or Claude Code.
  2. 2.Ask for a new app, or to import yours: “import https://github.com/you/your-app into LazyFrog”. Code in a folder on your machine is pushed with git (how that works).
  3. 3.Check the preview. Your AI tries the pages in a real browser and reads the logs; you get the private address.
  4. 4.Say “publish it”. A test publish builds the app and rehearses the migrations on a copy of the live database first. A live site starts with Hobby at $9 a month.

Before it goes live, go through the security checklist. The book club app's own code scanned clean.

Questions

How do I deploy a Next.js app with PostgreSQL?
Connect LazyFrog to your AI (Claude, ChatGPT, Codex or Claude Code) and ask it to build or import the app. Every project has its own PostgreSQL and sets DATABASE_URL for the app; the preview runs next dev and the live site runs next build and next start.
Where does DATABASE_URL come from?
LazyFrog sets it in every environment: the preview's points at the preview database and the live site's at the live one. They're separate, and preview data is never copied to the live site.
How do schema changes reach the live database?
As numbered SQL files in migrations/. They're applied to the preview when your AI runs them or you push, and to the live database when you publish, after a rehearsal on a copy of it. A framework's own migration tool works too, as the migrate command in services.json.
Do I need Vercel, Docker or a separate database provider?
No. The app, its database and its address are in one project. A Dockerfile and compose.yml come with it so the same code runs anywhere else, with a PostgreSQL of its own.
Which Next.js features work?
The app runs as a Node server with next start, so server components, server actions, route handlers and middleware run as they do on your machine. Background work goes in a worker or a cron service beside the site.
Is the book club example real?
Yes. Codex built it on LazyFrog on 13 November 2026 from the one message on this page, on a Free account, and left it as a preview.