How to Integrate a New AI Provider into NextChat: A Complete Developer Guide
Integrating a new AI provider into NextChat requires extending two enums in app/constant.ts, creating an API route handler in app/api/, and wiring the provider mapping in app/client/api.ts to route requests through the generic ClientApi architecture.
NextChat (formerly ChatGPT Next Web) is built on a clean abstraction layer that treats every LLM vendor as a pluggable service. The open-source codebase maintained by ChatGPTNextWeb uses a dual-enum pattern—ServiceProvider for UI logic and ModelProvider for API routing—to decouple the frontend from backend implementations. This architecture allows developers to add custom AI backends like Groq, Mistral, or private endpoints without modifying core chat logic.
Understanding the Provider Architecture
NextChat abstracts every LLM behind two distinct concepts defined in app/constant.ts:
| Concept | Location | Purpose |
|---|---|---|
| ServiceProvider | enum ServiceProvider |
The logical vendor name displayed in the UI (e.g., OpenAI, Google). |
| ModelProvider | enum ModelProvider |
The internal identifier that ClientApi uses to determine request shape and base URL. |
When you integrate a new provider, you bridge these two enums through four layers: constant definitions, HTTP proxy routes, client instantiation, and optional UI customization.
Step-by-Step Integration Guide
Step 1: Extend the Enums in app/constant.ts
Add your provider to both enums to register it with the system. For this example, we will add a fictional provider called FooAI.
// app/constant.ts
export enum ServiceProvider {
OpenAI = "OpenAI",
Google = "Google",
// ... existing providers
FooAI = "FooAI", // <─ Add here
}
export enum ModelProvider {
GPT = "GPT",
GeminiPro = "GeminiPro",
// ... existing providers
FooAI = "FooAI", // <─ And here
}
Reference: See the ServiceProvider definition and ModelProvider implementation in [app/constant.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/constant.ts).
Step 2: Add Connection Constants
Define the endpoint configuration object immediately after the enums. Follow the naming convention used by existing providers like OpenaiPath or Google.
// app/constant.ts
export const FooAI = {
ExampleEndpoint: "https://api.fooai.com",
ChatPath: "v1/chat/completions", // Adjust to match vendor API spec
ApiKey: process.env.FOOAI_API_KEY, // Optional: direct env reference
};
Reference: Example constant blocks for other vendors are located at lines 78-100 of [app/constant.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/constant.ts#L78-L100).
Step 3: Create the API Route Handler
Create a new file at app/api/fooai.ts (or app/api/fooai/route.ts depending on your App Router structure). This file acts as an edge proxy between the client and the third-party API.
// app/api/fooai.ts
import { NextRequest, NextResponse } from "next/server";
import { auth } from "./auth";
import { getServerSideConfig } from "@/app/config/server";
import { ModelProvider, FooAI } from "@/app/constant";
import { prettyObject } from "@/app/utils/format";
const serverConfig = getServerSideConfig();
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
console.log("[FooAI Route] params ", params);
if (req.method === "OPTIONS") {
return NextResponse.json({ body: "OK" }, { status: 200 });
}
const authResult = auth(req, ModelProvider.FooAI);
if (authResult.error) {
return NextResponse.json(authResult, { status: 401 });
}
try {
const response = await request(req);
return response;
} catch (e) {
console.error("[FooAI] ", e);
return NextResponse.json(prettyObject(e));
}
}
export const GET = handle;
export const POST = handle;
export const runtime = "edge";
async function request(req: NextRequest) {
const controller = new AbortController();
const apiKey = req.headers.get("Authorization")?.replace("Bearer ", "") ??
serverConfig.fooaiApiKey;
if (!apiKey) {
throw new Error("Missing FOOAI_API_KEY");
}
const baseUrl = FooAI.ExampleEndpoint;
const path = req.nextUrl.pathname.replaceAll("/api/fooai", "");
const fetchUrl = `${baseUrl}/${FooAI.ChatPath}${path}`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
method: req.method,
body: req.body,
redirect: "manual",
// @ts-ignore
duplex: "half",
signal: controller.signal,
};
const res = await fetch(fetchUrl, fetchOptions);
const newHeaders = new Headers(res.headers);
newHeaders.delete("www-authenticate");
newHeaders.set("X-Accel-Buffering", "no");
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers: newHeaders,
});
}
Reference: The openai.ts handler at [app/api/openai.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/api/openai.ts#L29-L73) provides the canonical implementation pattern.
Step 4: Register the Client API Mapping
Wire the ServiceProvider to the ModelProvider in the client factory function so the frontend knows which API class to instantiate.
// app/client/api.ts
export function getClientApi(provider: ServiceProvider): ClientApi {
switch (provider) {
case ServiceProvider.OpenAI:
return new ClientApi(ModelProvider.GPT);
case ServiceProvider.Google:
return new ClientApi(ModelProvider.GeminiPro);
case ServiceProvider.FooAI:
return new ClientApi(ModelProvider.FooAI); // <─ Map new provider
default:
return new ClientApi(ModelProvider.GPT);
}
}
Reference: The getClientApi switch statement is defined at lines 68-98 of [app/client/api.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/client/api.ts#L68-L98).
Step 5: Configure Environment Variables
Add the required secrets to your environment configuration. NextChat automatically ingests *_API_KEY and *_ENDPOINT patterns through getServerSideConfig.
# .env.local or .env.template
FOOAI_API_KEY=your_fooai_api_key_here
FOOAI_ENDPOINT=https://api.fooai.com
No additional code changes are required for environment variable parsing; the existing getServerSideConfig utility in app/config/server.ts handles dynamic key ingestion.
Step 6: (Optional) Customize the UI
The provider dropdown in the Model Config dialog populates automatically from the ServiceProvider enum via the groupModels helper in app/utils/model.ts. If you want to add a custom icon or provider description, modify app/components/model-config.tsx:
// app/components/model-config.tsx
{Object.keys(groupModels).map((providerName) => (
<optgroup label={providerName} key={providerName}>
{groupModels[providerName].map((v) => (
<option
value={`${v.name}@${v.provider?.providerName}`}
key={v.name}
>
{v.displayName} ({v.provider?.providerName})
</option>
))}
</optgroup>
))}
Reference: See the model configuration component at [app/components/model-config.tsx](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/components/model-config.tsx#L41-L45).
Key Files for Provider Integration
| File | Purpose | Link |
|---|---|---|
app/constant.ts |
Defines ServiceProvider, ModelProvider enums and endpoint constants |
[constant.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/constant.ts) |
app/client/api.ts |
Contains getClientApi factory that maps providers to client instances |
[api.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/client/api.ts) |
app/api/<provider>.ts |
HTTP proxy implementation; copy openai.ts as a template |
[openai.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/api/openai.ts) |
app/utils/model.ts |
Helper functions for parsing model strings like "gpt-4o@OpenAI" |
[model.ts](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/utils/model.ts) |
app/components/model-config.tsx |
UI component rendering the provider selection dropdown | [model-config.tsx](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/components/model-config.tsx) |
Summary
- Extend both enums in
app/constant.tsto register the provider name with the system architecture. - Define endpoint constants (base URL and chat path) to configure the HTTP proxy target.
- Create an API route in
app/api/that authenticates requests and proxies them to the vendor endpoint using the Edge Runtime. - Map the provider in
getClientApiwithinapp/client/api.tsto link the UI selection to the correct backend handler. - Set environment variables following the
PROVIDER_API_KEYnaming convention for automatic server-side configuration ingestion.
Frequently Asked Questions
Do I need to modify the frontend components to show the new provider in the dropdown?
No. The Model Config dialog automatically builds the provider list from the ServiceProvider enum in app/constant.ts. As long as you add your provider to that enum, it appears in the UI immediately without touching React components.
Can I support multiple models from the same new provider?
Yes. The ClientApi class accepts a ModelProvider value, and the groupModels utility in app/utils/model.ts aggregates models by their providerName property. Simply ensure your models specify the new ServiceProvider in their definition, and they will group correctly under your provider's header in the UI.
What authentication methods does the API route support?
The example above uses Bearer token authentication passed via the Authorization header, which is the standard for most OpenAI-compatible APIs. However, you can modify the request function in your API route to implement API key headers, query parameters, or custom authentication schemes required by your specific provider.
Is the Edge Runtime mandatory for the API routes?
Yes. NextChat API routes export runtime = "edge" to ensure requests execute at the edge for optimal latency. Your proxy implementation must use Web-standard Request and Response objects compatible with the Edge Runtime, avoiding Node.js-specific modules like fs or http.
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 →