How to Integrate 9router API Handlers with a Frontend Application
Integrate your frontend with 9router by calling the Next.js API routes (/api/v1/...) using standard fetch requests with OpenAI-compatible JSON payloads, where the backend handles provider authentication, request translation, and streaming responses automatically.
The decolua/9router repository provides a Next.js backend that proxies requests to multiple AI providers through a unified OpenAI-compatible interface. When building a frontend application—whether React, Vue, or vanilla JavaScript—you interact with these endpoints exactly as you would with the official OpenAI API, without managing provider credentials or provider-specific SDKs on the client side.
Understanding the 9router Architecture
9router exposes HTTP endpoints under src/app/api/v1/... that act as thin wrappers around core handler functions. These handlers, located in open-sse/handlers/, normalize incoming requests, manage provider-specific authentication, and return either JSON or Server-Sent Events (SSE) streams.
API Route Structure
Each endpoint in src/app/api/v1/ corresponds to a specific AI capability. For example, src/app/api/v1/chat/completions/route.js handles chat completions by forwarding requests to handleChatCore. The routes implement comprehensive CORS handling via OPTIONS method handlers, allowing requests from any origin without additional server configuration.
Core Handler Logic
The actual business logic resides in the open-sse directory. Files like open-sse/handlers/chatCore.js manage token refresh, provider selection, and response formatting. When a frontend sends a request to /api/v1/chat/completions, the route handler invokes the core logic, which then communicates with the configured provider (OpenAI, Claude, etc.) through specialized executors in open-sse/executors/.
Sending Requests from the Frontend
Because 9router accepts standard OpenAI-compatible payloads, you can use the native fetch API without installing provider-specific SDKs.
Standard JSON Requests (Chat Completions)
For non-streaming responses, send a POST request to /api/v1/chat/completions with a JSON body containing the model and messages array:
async function callChatCompletion(messages) {
const response = await fetch('/api/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4o-mini', // Must match a provider-configured model
messages,
stream: false // Return JSON response
})
});
if (!response.ok) {
const err = await response.json();
throw new Error(`API error ${response.status}: ${err.error}`);
}
const data = await response.json();
return data; // { choices: [{ message: { content: '…' } }], … }
}
The route src/app/api/v1/chat/completions/route.js processes this request and returns a standard OpenAI-compatible completion object.
Handling Server-Sent Events (SSE) Streams
When you set stream: true, 9router returns an SSE stream instead of JSON. Consume this using the Fetch Streams API to process tokens as they arrive:
async function streamChatCompletion(messages, onDelta) {
const response = await fetch('/api/v1/chat/completions', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'gpt-4o-mini',
messages,
stream: true // Enable SSE streaming
})
});
if (!response.body) throw new Error('ReadableStream not supported');
const reader = response.body.getReader();
const decoder = new TextDecoder('utf-8');
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// SSE events are separated by double newlines
const events = buffer.split('\n\n');
buffer = events.pop(); // Keep incomplete chunk for next iteration
for (const ev of events) {
if (ev.startsWith('data:')) {
const payload = ev.slice(5).trim();
if (payload === '[DONE]') return; // Stream termination signal
const data = JSON.parse(payload);
const delta = data?.choices?.[0]?.delta?.content;
if (delta) onDelta(delta);
}
}
}
}
The core streaming logic in open-sse/handlers/chatCore.js ensures the SSE format matches OpenAI's specification exactly, allowing you to use standard parsing logic.
Image Generation Endpoints
For image generation, post to /api/v1/images/generations with parameters matching the DALL-E API structure:
async function generateImage(prompt) {
const response = await fetch('/api/v1/images/generations', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'dall-e-3',
prompt,
n: 1,
size: '1024x1024'
})
});
const data = await response.json();
// data.data[0].url contains the generated image URL
return data.data[0].url;
}
This endpoint, defined in src/app/api/v1/images/generations/route.js, forwards to handleImageGenerationCore in open-sse/handlers/imageGenerationCore.js.
Web Scraping Capabilities
9router also provides a generic fetch utility for web scraping accessible via /api/v1/web/fetch:
async function fetchWebPage(url) {
const response = await fetch('/api/v1/web/fetch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url })
});
const { content } = await response.json();
return content; // Raw HTML or extracted text depending on configuration
}
The handler in open-sse/handlers/fetch/index.js processes this request, allowing your frontend to retrieve external content without violating CORS policies.
Key Source Files and Implementation Details
Understanding these specific files helps debug integration issues and customize behavior:
src/app/api/v1/chat/completions/route.js– Next.js API route handling both JSON and SSE chat completionssrc/app/api/v1/images/generations/route.js– Image generation endpoint wrappersrc/app/api/v1/web/fetch/route.js– Web scraping proxy endpointopen-sse/handlers/chatCore.js– Core chat logic including streaming, token refresh, and provider selectionopen-sse/handlers/imageGenerationCore.js– Image generation orchestrationopen-sse/handlers/fetch/index.js– Web fetch implementation supporting multiple providersopen-sse/index.js– Main entry point exportinghandleChatCore,handleImageGenerationCore, and other top-level handlersopen-sse/config/providerModels.js– Maps provider-model combinations to target formats and stripping rules
Integration Checklist
Ensure your frontend integration follows these verified patterns:
- Base URL – All routes mount under
/api/v1/relative to your Next.js deployment - CORS Configuration – Routes automatically respond with
Access-Control-Allow-Origin: *via built-inOPTIONShandlers - Request Schema – Use OpenAI-compatible JSON structures (
model,messages,prompt,streamboolean) - Streaming Setup – Set
"stream": trueand implementReadableStreamprocessing for real-time responses - Error Handling – Expect
{ error: <message> }JSON for HTTP errors; streaming errors terminate with[DONE]signal - Security – 9router stores all provider API keys internally; frontend requests never transmit authentication secrets
Summary
- 9router exposes OpenAI-compatible endpoints under
/api/v1/that proxy to multiple AI providers through Next.js API routes. - Frontend applications send standard
fetchrequests to these routes without managing provider credentials or SDKs. - Support both JSON and SSE streaming responses by toggling the
streamparameter and using the Fetch Streams API. - Core logic in
open-sse/handlers/normalizes requests across providers, handles authentication, and manages error translation. - All endpoints include automatic CORS handling, making them safe to call from any frontend origin.
Frequently Asked Questions
What request format does 9router expect from the frontend?
9router expects OpenAI-compatible JSON payloads. For chat completions, include model, messages, and optional stream parameters. For images, provide prompt, model, n, and size. The schema matches the official OpenAI API specification exactly, allowing drop-in replacement of endpoint URLs.
How does 9router handle CORS for cross-origin requests?
Each API route in src/app/api/v1/ implements an OPTIONS method handler that returns Access-Control-Allow-Origin: * headers. This allows browser-based frontend applications hosted on different domains to call the 9router backend without triggering CORS errors or requiring additional proxy configuration.
Can I use the official OpenAI SDK with 9router endpoints?
Yes, you can configure the OpenAI JavaScript SDK to point to your 9router base URL instead of api.openai.com. Initialize the client with baseURL: 'https://your-domain.com/api/v1' and remove the default authentication header (or set it to a dummy value), as 9router manages provider credentials server-side in open-sse/config/.
Where are the provider credentials stored in a 9router integration?
Provider API keys and authentication tokens are stored server-side only in the 9router configuration files (typically in open-sse/config/ or environment variables accessed by the core handlers). The frontend never handles these secrets, making the architecture secure for public-facing applications.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →