# Next.js (API + Swagger) - Blueprint (/docs/blueprints/nextjs)



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

**Tech stack:**

| Layer           | Technology                                    |
| --------------- | --------------------------------------------- |
| Runtime         | Node.js 20                                    |
| Framework       | Next.js 15                                    |
| Language        | TypeScript 5                                  |
| Database driver | node-postgres (pg)                            |
| Migrations      | Custom runner (plain SQL)                     |
| API docs        | swagger-ui-react + hand-authored OpenAPI spec |

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

<a href="https://github.com/rock8-cloud/template-nextjs" target="_blank" rel="noopener noreferrer">
  View on GitHub →
</a>

***

## Project Structure [#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 [#environment-variables]

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

Database connection is configured via **one of two approaches**:

### Option A - Single connection URL (recommended for managed services) [#option-a---single-connection-url-recommended-for-managed-services]

| Variable       | Required | Example                                                 |
| -------------- | -------- | ------------------------------------------------------- |
| `DATABASE_URL` | yes      | `postgres://user:pass@host:5432/dbname?sslmode=require` |

### Option B - Individual variables [#option-b---individual-variables]

| Variable      | Required | Default     | Description                    |
| ------------- | -------- | ----------- | ------------------------------ |
| `DB_HOST`     | no       | `localhost` | Database host                  |
| `DB_PORT`     | no       | `5432`      | Database port                  |
| `DB_NAME`     | no       | `spacedb`   | Database name                  |
| `DB_USER`     | no       | `postgres`  | Database user                  |
| `DB_PASSWORD` | no       | `postgres`  | Database password              |
| `DB_SSL_MODE` | no       | *(none)*    | Set to `require` to enable SSL |

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

***

## Running Locally [#running-locally]

### With Docker Compose [#with-docker-compose]

```bash
docker compose up
```

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

### Standalone [#standalone]

```bash
# 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
```
