Server-Side Architecture
API keys for OpenAI, Anthropic, and Google must never be in browser bundles. IntentForm provides three server-side patterns depending on your stack.
Pattern 0 — Client-side only (demos only)
Section titled “Pattern 0 — Client-side only (demos only)”flowchart LR B[Browser] -->|direct API call| AI[(AI provider)] style B fill:#313244,stroke:#f38ba8,color:#cdd6f4 style AI fill:#313244,stroke:#cba6f7,color:#cdd6f4
The provider is instantiated in the browser with the API key passed directly. Only acceptable for local dev or playgrounds where the key is disposable.
// ONLY for demos — key exposed in browserconst engine = createIntentForm({ provider: openaiProvider({ apiKey: import.meta.env.VITE_OPENAI_API_KEY }), models: [...],})Pattern A — Node.js backend (@intentform/server)
Section titled “Pattern A — Node.js backend (@intentform/server)”The engine runs on your server. The browser uses @intentform/client to POST a prompt string to your endpoint. API key never leaves the server.
flowchart LR B[Browser] -->|POST /api/intent| S["@intentform/server\nNode.js handler"] S --> E["engine.parse()"] E --> AI[(AI provider)] AI --> R["IntentResolution\nJSON response"] R --> B style B fill:#313244,stroke:#89dceb,color:#cdd6f4 style S fill:#313244,stroke:#cba6f7,color:#cdd6f4 style E fill:#313244,stroke:#b4befe,color:#cdd6f4 style AI fill:#313244,stroke:#a6e3a1,color:#cdd6f4 style R fill:#313244,stroke:#fab387,color:#cdd6f4
Supported server frameworks:
| Framework | Helper | Recipe |
|---|---|---|
| Hono | createIntentFormHonoRoute(engine) | Hono recipe |
| Next.js App Router | createIntentFormNextHandler(engine) | Next.js recipe |
| TanStack Start | createIntentFormServerFn(engine) | TanStack Start recipe |
| Generic Web Fetch | createIntentFormRoute(engine) | see below |
Generic handler (Cloudflare Workers, Bun.serve, Deno.serve, etc.):
import { createIntentFormRoute } from '@intentform/server'
const handler = createIntentFormRoute(engine)
export default { fetch: handler }Browser client:
import { createClientIntentForm } from '@intentform/client'
const engine = createClientIntentForm({ endpoint: '/api/intent', models: myModels, // needed client-side for field rendering only})Pattern B — Non-Node backend (@intentform/server-http)
Section titled “Pattern B — Non-Node backend (@intentform/server-http)”If your backend is Spring Boot, .NET, Django, or any other non-Node runtime, run @intentform/server-http as a sidecar container. Your backend doesn’t need to know about AI at all.
flowchart LR B[Browser] -->|POST /api/intent| SC["@intentform/server-http\nDocker sidecar"] SC --> AI[(AI provider)] AI --> R["JSON response"] R --> B style B fill:#313244,stroke:#89dceb,color:#cdd6f4 style SC fill:#313244,stroke:#f38ba8,color:#cdd6f4 style AI fill:#313244,stroke:#a6e3a1,color:#cdd6f4 style R fill:#313244,stroke:#fab387,color:#cdd6f4
See the HTTP Sidecar recipe for Docker Compose setup, env vars, and backend integration examples.
Choosing a pattern
Section titled “Choosing a pattern”| Situation | Pattern |
|---|---|
| Local dev / playground | 0 (client-side) |
| Node.js backend (Hono, Next, TanStack Start) | A (@intentform/server) |
| Serverless / edge (Cloudflare, Vercel Edge) | A (generic Web Fetch handler) |
| Non-Node backend (Spring, .NET, Python) | B (@intentform/server-http sidecar) |
Auth and CORS
Section titled “Auth and CORS”When Pattern A or B is exposed to the browser, protect the endpoint:
- Auth: set
INTENTFORM_AUTH_TOKEN(sidecar) or validate a bearer token in your Node handler before calling the engine. - CORS: set
INTENTFORM_CORS_ORIGIN(sidecar) or configure CORS middleware in your Node framework.