110 lines
3.2 KiB
Markdown
110 lines
3.2 KiB
Markdown
# Frontend API client and auth integration
|
||
|
||
This page documents the frontend integration points you’ll touch when changing how the UI talks to the backend or how auth is applied.
|
||
|
||
## Related docs
|
||
|
||
- [Architecture](05-architecture.md)
|
||
- [Configuration](06-configuration.md)
|
||
- [API reference](07-api-reference.md)
|
||
|
||
## API base URL
|
||
|
||
The frontend uses `NEXT_PUBLIC_API_URL` as the single source of truth for where to send API requests.
|
||
|
||
- Code: `frontend/src/lib/api-base.ts`
|
||
- Behavior:
|
||
- reads `process.env.NEXT_PUBLIC_API_URL`
|
||
- normalizes by trimming trailing slashes
|
||
- throws early if missing/invalid
|
||
|
||
In Docker Compose, `compose.yml` sets `NEXT_PUBLIC_API_URL` both:
|
||
- as a **build arg** (for `next build`), and
|
||
- as a **runtime env var**.
|
||
|
||
## API client layout
|
||
|
||
### Generated client
|
||
|
||
- Location: `frontend/src/api/generated/*`
|
||
- Generator: **Orval**
|
||
- Config: `frontend/orval.config.ts`
|
||
- Script: `cd frontend && npm run api:gen`
|
||
- Convenience target: `make api-gen`
|
||
|
||
By default, Orval reads the backend OpenAPI schema from:
|
||
- `ORVAL_INPUT` (if set), otherwise
|
||
- `http://127.0.0.1:8000/openapi.json`
|
||
|
||
Output details (from `orval.config.ts`):
|
||
- Mode: `tags-split`
|
||
- Target index: `frontend/src/api/generated/index.ts`
|
||
- Schemas: `frontend/src/api/generated/model`
|
||
- Client: `react-query`
|
||
- All requests go through the custom mutator below.
|
||
|
||
### Custom fetch / mutator
|
||
|
||
All generated requests go through:
|
||
|
||
- Code: `frontend/src/api/mutator.ts`
|
||
- What it does:
|
||
- resolves `NEXT_PUBLIC_API_URL` and builds the full request URL
|
||
- sets `Content-Type: application/json` when there’s a body
|
||
- injects `Authorization: Bearer <token>` when a Clerk session token is available
|
||
- converts non-2xx responses into a typed `ApiError` (status + parsed response)
|
||
|
||
## Auth enablement and token injection
|
||
|
||
### Clerk enablement (publishable key gating)
|
||
|
||
Clerk is enabled in the frontend only when `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` looks valid.
|
||
|
||
- Gating helper (dependency-free): `frontend/src/auth/clerkKey.ts`
|
||
- UI-safe wrappers/hooks: `frontend/src/auth/clerk.tsx`
|
||
- provides `SignedIn`, `SignedOut`, `SignInButton`, `SignOutButton`, `useUser`, and `useAuth`
|
||
- returns safe fallbacks when Clerk is disabled (to allow secretless builds/prerender)
|
||
|
||
### Token injection
|
||
|
||
When the UI makes an API request, the mutator attempts to read a token from the Clerk session:
|
||
|
||
- Code: `frontend/src/api/mutator.ts` (`resolveClerkToken()`)
|
||
- If a token is available, the request includes:
|
||
- `Authorization: Bearer <token>`
|
||
|
||
### Route protection (middleware)
|
||
|
||
Request-time route protection is implemented via Next.js middleware:
|
||
|
||
- Code: `frontend/src/proxy.ts`
|
||
- Behavior:
|
||
- when Clerk is enabled: uses `clerkMiddleware()` to enforce auth on non-public routes
|
||
- when Clerk is disabled: passes all requests through
|
||
|
||
## Common workflows
|
||
|
||
### Update the backend API and regenerate the client
|
||
|
||
1. Run the backend so OpenAPI is available:
|
||
|
||
```bash
|
||
# from repo root
|
||
cp backend/.env.example backend/.env
|
||
make backend-migrate
|
||
cd backend && uv run uvicorn app.main:app --reload --port 8000
|
||
```
|
||
|
||
2. Regenerate the client:
|
||
|
||
```bash
|
||
# from repo root
|
||
make api-gen
|
||
|
||
# or from frontend/
|
||
ORVAL_INPUT=http://127.0.0.1:8000/openapi.json npm run api:gen
|
||
```
|
||
|
||
3. Review diffs under `frontend/src/api/generated/*`.
|
||
|