The client builds URLs using `NEXT_PUBLIC_API_URL` (normalized to remove trailing slashes).
### Generated API client (Orval + React Query)
We generate a typed client from the backend OpenAPI schema using **Orval**:
- Config: `orval.config.ts`
- Output: `src/api/generated/*`
- Script: `npm run api:gen`
By default, Orval reads:
-`ORVAL_INPUT` (if set), otherwise
-`http://127.0.0.1:8000/openapi.json`
Example:
```bash
# from frontend/
ORVAL_INPUT=http://localhost:8000/openapi.json npm run api:gen
```
### Auth header / Clerk token injection
All Orval-generated requests go through the custom mutator (`src/api/mutator.ts`).
It will:
- set `Content-Type: application/json` when there is a body and you didn’t specify a content type
- add `Authorization: Bearer <token>` automatically from local mode token or Clerk session
- parse errors into an `ApiError` with status + parsed response body
## Mobile / responsive UI validation
When changing UI intended to be mobile-ready, validate in Chrome (or similar) using the device toolbar at common widths (e.g. **320px**, **375px**, **768px**).
Quick checklist:
- No horizontal scroll
- Primary actions reachable without precision taps
- Focus rings visible when tabbing
- Modals/popovers not clipped
## Common commands
From `frontend/`:
```bash
npm run dev # start dev server
npm run build # production build
npm run start # run the built app
npm run lint # eslint
npm run test # vitest (with coverage)
npm run test:watch # watch mode
npm run api:gen # regenerate typed API client via Orval
```
## Docker
There is a `frontend/Dockerfile` used by the root `compose.yml`.
If you’re working on self-hosting, prefer running compose from the repo root so the backend/db are aligned with the documented ports/env.
- For local mode, set `NEXT_PUBLIC_AUTH_MODE=local`.
- For Clerk mode, set `NEXT_PUBLIC_AUTH_MODE=clerk` and a real Clerk publishable key.
### Dev server blocked by origin restrictions
`next.config.ts` sets `allowedDevOrigins` for dev proxy safety.
If you see repeated proxy errors (often `ECONNRESET`), make sure your dev server hostname and browser URL match (e.g. `localhost` vs `127.0.0.1`), and that your origin is included in `allowedDevOrigins`.
Notes:
- Local dev should work via `http://localhost:3000` and `http://127.0.0.1:3000`.
- LAN dev should work via the configured LAN IP (e.g. `http://192.168.1.101:3000`) **only** if you bind the dev server to all interfaces (`npm run dev:lan`).
- If you bind Next to `127.0.0.1` only, remote LAN clients won’t connect.