Intermediate
⏱ 35 min read
© Gate of AI 2026-06-02
Architect a multi-provider LLM gateway in Next.js using App Router Route Handlers to dynamically toggle production requests between OpenAI and Anthropic architectures while maintaining state context.
Prerequisites
- Node.js 18.x or higher
- Next.js 14.x or 15.x (App Router structure)
- Valid API keys configured in the OpenAI and Anthropic developer consoles
- Familiarity with polymorphic API payloads and React state composition
What We’re Building
Production AI engineering often requires model redundancy or specialized routing—routing creative tasks to Claude and strict structural updates to GPT. In this guide, we are building a unified API abstraction layer that sanitizes, maps, and executes multi-turn conversations across both ecosystems cleanly.
Setup and Installation
Bootstrap a clean Next.js application layer and pull down both official vendor SDK packages:
npx create-next-app@latest multi-provider-chat --ts --no-tailwind --app --src-dir=false
cd multi-provider-chat
npm install openai @anthropic-ai/sdk
Populate your local environment matrix inside .env.local in your root directory:
OPENAI_API_KEY=your_openai_project_secret_key
ANTHROPIC_API_KEY=your_anthropic_live_secret_key
Step 1: Engineering the Unified Model Gateway
Create your server route handler at app/api/chat/route.js. We explicitly instantiate both clients and create an abstraction parser to handle the structural layout differences between OpenAI’s choices array and Anthropic’s content blocks.
import { NextResponse } from 'next/server';
import { OpenAI } from 'openai';
import { Anthropic } from '@anthropic-ai/sdk';const openai = new OpenAI();
const anthropic = new Anthropic();export async function POST(req) {
try {
const { provider, messages } = await req.json();if (!messages || !Array.isArray(messages)) {
return NextResponse.json({ error: 'Malformed message history payload' }, { status: 400 });
}let replyText = '';if (provider === 'openai') {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: messages.map(msg => ({
role: msg.role,
content: msg.content
}))
});
replyText = response.choices[0].message.content;} else if (provider === 'anthropic') {
// Anthropic separates the 'system' message from the historical array
const systemMessage = messages.find(m => m.role === 'system')?.content || 'You are a precise assistant.';
const conversationHistory = messages.filter(m => m.role !== 'system');const response = await anthropic.messages.create({
model: 'claude-3-5-haiku-20241022',
max_tokens: 1024,
system: systemMessage,
messages: conversationHistory.map(msg => ({
role: msg.role === 'assistant' ? 'assistant' : 'user', // Safe role mapping
content: msg.content
}))
});
replyText = response.content[0].text;
} else {
return NextResponse.json({ error: 'Unsupported provider routing request' }, { status:...Continue Reading
Log in for free to read the rest of this article and access exclusive AI tools.
Log in / Register