How to Use Middleware with 9Router API Handlers: Implementation Guide
9Router does not expose a public middleware API for its OpenAI-compatible endpoints, so you must either patch the source code in src/proxy.js or place an external reverse-proxy in front of the application to intercept requests.
9Router is a lightweight routing layer built on Next.js API routes that aggregates AI services behind a unified interface. While it provides a streamlined request pipeline for handling chat, embeddings, and image generation via Server-Sent-Events, the architecture deliberately avoids pluggable Express-style middleware hooks. This guide explains the internal routing mechanism and provides two proven methods for adding custom middleware logic to your 9Router deployment.
How 9Router Processes HTTP Requests
According to the decolua/9router source code, the HTTP endpoint is built on Next.js API routes wired together in src/proxy.js. The request pipeline is intentionally minimal: each incoming request is parsed, routed to a specific handler (such as those under open-sse/handlers/), and the response is streamed back via Server-Sent-Events.
The central dispatcher in src/proxy.js imports built-in utilities like open-sse/utils/requestLogger.js for logging and open-sse/utils/streamHelpers.js for SSE normalization. However, these components are woven into the router at compile time and are not exposed for external extension.
Why 9Router Lacks a Public Middleware API
Because the routing logic is internal to 9Router, there is no public hook for adding arbitrary Express-style middleware (e.g., app.use(myMiddleware)). The only "middleware-like" components that exist are the built-in utilities that handle cross-cutting concerns like request logging and stream normalization. These are hardcoded into the request flow and cannot be injected dynamically.
If you need custom behavior—such as authentication, request validation, logging, or response transformations—you must implement one of the following workarounds.
Method 1: Patching the Source Code
The most direct way to use middleware with 9Router API handlers is to modify the source files directly. This approach gives you full control over the request lifecycle but requires maintaining a fork or patch set.
Injecting Logic into the Central Router
Edit src/proxy.js to insert your own function before the handler is invoked. This is the only way to inject true middleware-style logic that affects all routes.
// src/proxy.js – after the imports
import { logRequest } from '@/open-sse/utils/requestLogger';
// … inside the main request handler
export default async function handler(req, res) {
// Custom middleware – runs before any specific handler
logRequest(req); // ← built‑in logger (you can add more)
// Existing routing logic (unchanged)
const result = await routeRequest(req);
sendSseResponse(res, result);
}
Source reference: [src/proxy.js](https://github.com/decolua/9router/blob/master/src/proxy.js)
Modifying Individual Handlers
For route-specific middleware, patch the individual handler files. For example, to add API key validation to the chat endpoint:
// src/open-sse/handlers/chatCore/streamingHandler.js
export async function streamingHandler(req, res) {
// ---- Your middleware logic ----
if (!req.headers['x-api-key'] || req.headers['x-api-key'] !== process.env.MY_API_KEY) {
res.status(401).json({ error: 'Invalid API key' });
return;
}
// Continue with the original streaming logic
const stream = await createChatStream(req.body);
streamToSse(res, stream);
}
Source reference: [open-sse/handlers/chatCore/streamingHandler.js](https://github.com/decolua/9router/blob/master/open-sse/handlers/chatCore/streamingHandler.js)
Method 2: External Reverse-Proxy Layer
If you prefer not to modify the 9Router source code, run 9Router behind your own reverse-proxy (NGINX, Cloudflare Workers, etc.) and apply middleware there. The proxy will see the raw HTTP request before it reaches 9Router, allowing you to add headers, rate-limit, or modify the payload.
# nginx.conf – place in front of 9Router
server {
listen 80;
location /v1/ {
# Example middleware: add a static header
add_header X-Custom-Middleware "enabled";
# Forward to the 9Router instance
proxy_pass http://127.0.0.1:20128;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
This approach keeps the 9Router code untouched while giving you full control over HTTP processing.
Understanding Built-in Middleware-Like Components
While you cannot extend them directly, understanding the existing utilities helps when patching:
open-sse/utils/requestLogger.js: Logs every incoming request. You can replicate or extend this pattern when adding custom logging middleware.open-sse/utils/streamHelpers.js: Normalizes Server-Sent-Event streams, acting like response middleware for SSE formatting.
These files demonstrate the expected patterns for request and response manipulation within the 9Router architecture.
Summary
- 9Router does not provide a pluggable middleware API for its OpenAI-compatible endpoints; the routing pipeline is internal and minimal.
- Patch
src/proxy.jsto inject global middleware that runs before any handler executes. - Modify individual handlers (e.g.,
streamingHandler.js) for route-specific logic like authentication or validation. - Use an external reverse-proxy (NGINX, etc.) to add middleware without touching the source code.
- Built-in utilities like
requestLogger.jsshow the patterns used for cross-cutting concerns but are not externally extensible.
Frequently Asked Questions
Does 9Router support Express-style middleware?
No. 9Router is built on Next.js API routes rather than Express, and the src/proxy.js router does not expose an app.use() interface or similar hook for adding arbitrary middleware functions. The request pipeline is closed and optimized for SSE streaming.
Can I add authentication middleware to 9Router?
Yes, but you must implement it via source modification or an external proxy. To add authentication directly, patch src/proxy.js for global checks or modify specific handlers like open-sse/handlers/chatCore/streamingHandler.js to validate headers (e.g., x-api-key) before processing the request.
Where is the request routing logic located in 9Router?
The central routing logic resides in src/proxy.js, which dispatches all /v1/* requests to the appropriate handler based on the endpoint. This file is the primary integration point if you are patching the source to add middleware.
Is there a plugin system for 9Router?
No, 9Router does not implement a plugin architecture. The only extension points are the source files themselves (src/proxy.js and the handler modules under open-sse/handlers/) or the HTTP layer via a reverse-proxy.
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 →