Rock8Cloud
Blueprints

Next.js (API + Swagger) — Blueprint

REST API backend with Next.js 15, Node.js, and PostgreSQL

REST API backend built with Next.js 15, Node.js, and PostgreSQL. Serves a JSON API and renders a Swagger UI for interactive docs. Intended as a starting point for Kubernetes-deployed services: one table, one API endpoint, full Docker support out of the box.

Tech stack:

LayerTechnology
RuntimeNode.js 20
FrameworkNext.js 15
LanguageTypeScript 5
Database drivernode-postgres (pg)
MigrationsCustom runner (plain SQL)
API docsswagger-ui-react + hand-authored OpenAPI spec

The application runs on port 3000. Opening / redirects to the Swagger UI at /swagger.

View on GitHub →

Project Structure

app/
├── page.tsx                         # Redirects / → /swagger
├── swagger/
│   └── page.tsx                     # Swagger UI (client component, loads /api/openapi.json)
└── api/
    ├── targets/route.ts             # GET /api/targets
    ├── info/route.ts                # GET /api/info
    └── openapi.json/route.ts        # GET /api/openapi.json — serves the OpenAPI spec

lib/
├── db.ts                            # pg Pool (DATABASE_URL or DB_*)
└── migrate.ts                       # Reads and runs SQL files from db/migrations/

db/migrations/
└── V1__init.sql                     # Creates space_target table + seed data

instrumentation.ts                   # Next.js hook: runs migrations on server startup
next.config.ts                       # output: 'standalone' for Docker

Key design decisions:

  • lib/db.ts creates a single pg.Pool that is reused across requests. It reads DATABASE_URL first and falls back to individual DB_* variables. SSL is enabled when sslmode=require appears in the URL or when DB_SSL_MODE=require is set.
  • Migrations run via Next.js instrumentation.ts, which is called once when the Node.js server starts. This keeps migration logic out of request handlers.
  • The OpenAPI spec in app/api/openapi.json/route.ts is hand-authored. The Swagger UI page loads it dynamically to avoid SSR issues with swagger-ui-react.
  • next.config.ts sets output: 'standalone' so the Dockerfile can copy only the minimal runtime output (/app/.next/standalone). The db/ directory is copied separately because instrumentation.ts needs the migration files at runtime after build.
  • To add a new endpoint: create a route.ts under app/api/, query via the pool from lib/db.ts, and update the OpenAPI spec in app/api/openapi.json/route.ts.

Environment Variables

The application runs on port 3000 and requires a PostgreSQL database.

Database connection is configured via one of two approaches:

VariableRequiredExample
DATABASE_URLyespostgres://user:pass@host:5432/dbname?sslmode=require

Option B — Individual variables

VariableRequiredDefaultDescription
DB_HOSTnolocalhostDatabase host
DB_PORTno5432Database port
DB_NAMEnospacedbDatabase name
DB_USERnopostgresDatabase user
DB_PASSWORDnopostgresDatabase password
DB_SSL_MODEno(none)Set to require to enable SSL

DATABASE_URL takes precedence. If it is set, the individual DB_* variables are ignored.


Running Locally

With Docker Compose

docker compose up

Starts the application together with a PostgreSQL instance and sets DATABASE_URL automatically. No additional configuration needed.

Standalone

# install dependencies
npm install

# run in development mode (DATABASE_URL or DB_* must be set)
DATABASE_URL=postgres://postgres:postgres@localhost:5432/spacedb npm run dev

# build and run in production mode
npm run build
DATABASE_URL=postgres://postgres:postgres@localhost:5432/spacedb npm start

On this page