API Integration
This document explains how the Rhesis frontend integrates with the Rhesis backend API.
Architecture: BFF Proxy
The browser never holds a backend access token. Client components call a same-origin
/api/backend/* proxy, which injects Authorization server-side from the httpOnly session
cookie. Server-side code (Server Components, Route Handlers) calls the backend directly with
BACKEND_URL. See apps/frontend/AGENTS.md’s “BFF Auth Pattern” section for the full contract.
API Client Implementation
The client is implemented in src/utils/api-client/ as flat per-resource files (projects-client.ts, tests-client.ts, tasks-client.ts, …), not an endpoints/ subfolder:
Base Client
Always construct new ApiClientFactory() with no arguments in client components/hooks/utils.
Only server-side code (via createServerApiFactory()) passes an explicit token/projectId.
Resource Clients
Consumed through a factory rather than imported directly:
Proxy Routes
Two Next.js Route Handlers proxy to the backend:
src/app/api/backend/[...path]/route.ts— the BFF route hit byBaseApiClienton the client. Reads the access token viagetFreshAccessToken()from the session cookie and injectsAuthorization; returns 401 if there’s no valid session.src/app/api/[...path]/route.ts— a catch-all proxy (proxyToBackend()) for routes without a dedicated handler. Forwards an allow-list of headers (authorization,content-type,accept,x-project-id, …), applies per-path timeout budgets (longer for LLM/import/Garak calls), and follows same-origin backend redirects.
Avoid next.config.mjs rewrites for backend proxying — rewrite destinations are baked into the build at compile time, which doesn’t work for a runtime-configurable BACKEND_URL.
Type Definitions
API types live in src/utils/api-client/interfaces/ (singular resource files, e.g. project.ts) and mirror the backend’s snake_case fields rather than being camelCased:
WebSocket API for Playground Chat
In addition to REST clients, the frontend Playground uses WebSocket events for interactive endpoint chat. The shared message types live in:
apps/frontend/src/utils/websocket/types.tsapps/backend/src/rhesis/backend/app/schemas/websocket.py
Core chat event types:
chat.messagechat.responsechat.error
chat.message payload (frontend → backend)
| Field | Required | Description |
|---|---|---|
endpoint_id | Yes | UUID of the endpoint to invoke |
message | Yes | User message text |
conversation_id | No | Conversation continuity identifier |
files | No | Attachments with filename, content_type, data |
chat.response payload (backend → frontend)
| Field | Required | Description |
|---|---|---|
output | Yes | Endpoint response text |
endpoint_id | Yes | Invoked endpoint ID |
trace_id | No | Trace identifier for opening the trace drawer |
conversation_id | No | Canonical conversation ID returned by backend |
output_files | No | Files returned by the endpoint |
All WebSocket traffic on the /ws endpoint (not just Playground chat) is capped at 10 MB per message.
Runtime Configuration
The backend URL is not baked into the client bundle at build time. app/layout.tsx injects it into
every server-rendered page as window.__ENV__:
getClientApiBaseUrl() (utils/url-resolver.ts) reads window.__ENV__.apiBaseUrl — this is used by pages that call the backend directly and unauthenticated from the browser (login, magic-link, forgot-password, provider discovery), not by BaseApiClient, which always uses the same-origin /api/backend proxy regardless of environment.
There is no NEXT_PUBLIC_API_BASE_URL env var and no build-time placeholder-replacement script — the same built image works across environments because the backend URL is read from process.env at request time, not baked in.
Error Handling
BaseApiClient.fetch() centralizes error handling: structured error parsing, retry with backoff on transient failures, and automatic session clearing on a 401 from the backend. Components can still add their own error.tsx boundary for UI-level fallbacks (see Routing).