# Next.js + React - Blueprint (/docs/blueprints/nextjs-react)



Fullstack application built with **Next.js 15**, **React 19**, and **PostgreSQL**. The backend exposes a JSON API. The frontend is a React client component that fetches and displays the data. A deploy-ready starting point: one table, one API endpoint, one page, full Docker support out of the box.

**Tech stack:**

| Layer           | Technology                  |
| --------------- | --------------------------- |
| Runtime         | Node.js 20                  |
| Framework       | Next.js 15                  |
| Frontend        | React 19 (client component) |
| Language        | TypeScript 5                |
| Database driver | node-postgres (pg)          |
| Migrations      | Custom runner (plain SQL)   |

The application runs on port **3000**. Opening `/` shows the React frontend with a live connection indicator and a table of space targets.

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

***

## Project Structure [#project-structure]

```
app/
├── page.tsx                         # React client component: fetches API, renders table
├── layout.tsx                       # Root layout
└── api/
    ├── targets/route.ts             # GET /api/targets
    └── info/route.ts                # GET /api/info

lib/
├── db.ts                            # pg Pool (DATABASE_URL or DB_*), sslmode handling
└── 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:**

* `app/page.tsx` is a `'use client'` component: it runs in the browser, calls `/api/info` and `/api/targets` via `fetch`, and renders the table once both requests succeed. The connection indicator dot reflects the fetch status.
* `lib/db.ts` strips `sslmode` from the connection string before passing it to `pg` because pg v8+ treats `sslmode=require` as `verify-full` (rejecting self-signed certs). SSL is instead controlled via the `ssl` object, with `rejectUnauthorized: false` for cloud-managed databases.
* Migrations run via Next.js `instrumentation.ts`, called once when the Node.js server starts.
* `next.config.ts` sets `output: 'standalone'` for minimal Docker images. The `db/` directory is copied separately in the Dockerfile because `instrumentation.ts` needs the migration files at runtime.
* To add a new endpoint: create a `route.ts` under `app/api/` and query via the pool from `lib/db.ts`. Update `app/page.tsx` to display the new data.

***

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