# How to Integrate 9router API Handlers with a Frontend Application

> Easily integrate your frontend with 9router API handlers. Utilize standard fetch requests to OpenAI-compatible endpoints for automatic backend authentication, translation, and streaming responses.

- Repository: [decolua/9router](https://github.com/decolua/9router)
- Tags: how-to-guide
- Published: 2026-05-08

---

**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`](https://github.com/decolua/9router/blob/main/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`](https://github.com/decolua/9router/blob/main/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:

```javascript
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`](https://github.com/decolua/9router/blob/main/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:

```javascript
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`](https://github.com/decolua/9router/blob/main/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:

```javascript
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`](https://github.com/decolua/9router/blob/main/src/app/api/v1/images/generations/route.js), forwards to `handleImageGenerationCore` in [`open-sse/handlers/imageGenerationCore.js`](https://github.com/decolua/9router/blob/main/open-sse/handlers/imageGenerationCore.js).

### Web Scraping Capabilities

9router also provides a generic fetch utility for web scraping accessible via `/api/v1/web/fetch`:

```javascript
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`](https://github.com/decolua/9router/blob/main/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`](https://github.com/decolua/9router/blob/main/src/app/api/v1/chat/completions/route.js)** – Next.js API route handling both JSON and SSE chat completions
- **[`src/app/api/v1/images/generations/route.js`](https://github.com/decolua/9router/blob/main/src/app/api/v1/images/generations/route.js)** – Image generation endpoint wrapper
- **[`src/app/api/v1/web/fetch/route.js`](https://github.com/decolua/9router/blob/main/src/app/api/v1/web/fetch/route.js)** – Web scraping proxy endpoint
- **[`open-sse/handlers/chatCore.js`](https://github.com/decolua/9router/blob/main/open-sse/handlers/chatCore.js)** – Core chat logic including streaming, token refresh, and provider selection
- **[`open-sse/handlers/imageGenerationCore.js`](https://github.com/decolua/9router/blob/main/open-sse/handlers/imageGenerationCore.js)** – Image generation orchestration
- **[`open-sse/handlers/fetch/index.js`](https://github.com/decolua/9router/blob/main/open-sse/handlers/fetch/index.js)** – Web fetch implementation supporting multiple providers
- **[`open-sse/index.js`](https://github.com/decolua/9router/blob/main/open-sse/index.js)** – Main entry point exporting `handleChatCore`, `handleImageGenerationCore`, and other top-level handlers
- **[`open-sse/config/providerModels.js`](https://github.com/decolua/9router/blob/main/open-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-in `OPTIONS` handlers
- **Request Schema** – Use OpenAI-compatible JSON structures (`model`, `messages`, `prompt`, `stream` boolean)
- **Streaming Setup** – Set `"stream": true` and implement `ReadableStream` processing 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 `fetch` requests to these routes without managing provider credentials or SDKs.
- Support both JSON and SSE streaming responses by toggling the `stream` parameter 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.