Skip to content
Learni
Voir tous les tutoriels
IA & Développement

Comment maîtriser OpenRouter pour APIs IA en 2026

Read in English

Introduction

OpenRouter révolutionne l'accès aux modèles IA en 2026 en offrant une API unifiée compatible OpenAI, routant vers +100 providers (Anthropic, OpenAI, Google, Mistral...). Contrairement aux APIs directes, OpenRouter gère automatiquement les fallbacks sur modèles défaillants, optimise les coûts via leaderboards prix/latence, et supporte le streaming server-sent events pour chats temps réel.

Pourquoi ce tuto expert ? Pour devs seniors : implémentez routing custom (ex: prioriser Claude-3.5-Sonnet si <0.5$/M tokens), beascout (test auto meilleur modèle), monitoring via headers X-Usage, et résilience avec retries exponentiels. Résultat : APIs IA 99.99% uptime, 30% économies coûts. Idéal pour SaaS, agents IA ou RAG scalables. On part d'un projet Next.js 15, vers une API chat production-ready (145 mots).

Prérequis

  • Node.js 20+ et npm/yarn/pnpm
  • Next.js 15+ avec TypeScript (npx create-next-app@latest@)
  • Compte OpenRouter : dashboard.openrouter.ai pour API key gratuite (crédits initiaux)
  • Connaissances avancées : OpenAI SDK, async/await, Zod validation, Vercel deployment
  • Outils : .env.local, curl pour tests

Initialiser le projet Next.js

terminal
npx create-next-app@latest openrouter-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
cd openrouter-app
npm install openai@latest zod@latest @types/node
npm install -D @types/cors
cp .env.example .env.local

Crée un projet Next.js 15 App Router avec TypeScript et Tailwind. Installe OpenAI SDK (compatible OpenRouter via baseURL), Zod pour validation inputs, et types Node. Copie .env pour clés sensibles. Évite piège : --app pour App Router moderne, pas Pages.

Configurer .env et client OpenRouter

lib/openrouter.ts
import OpenAI from 'openai';
import 'dotenv/config';

const openrouter = new OpenAI({
  apiKey: process.env.OPENROUTER_API_KEY ?? '',
  baseURL: 'https://openrouter.ai/api/v1',
});

export default openrouter;

export type OpenRouterMessage = OpenAI.Chat.ChatCompletionMessageParam;

Définit un client OpenAI customisé pour OpenRouter : baseURL pointe l'API proxy, apiKey via dotenv. Type alias pour messages réutilisable. Piège : Vérifiez OPENROUTER_API_KEY dans .env.local (ex: OPENROUTER_API_KEY=sk-or-...). Pas de provider par défaut pour flexibilité.

Premier appel : Chat completion basique

Testons un appel simple vers Claude-3.5-Sonnet, modèle leaderboard 2026 pour raisonnement. OpenRouter ajoute headers traçabilité (X-Provider, X-Requested-Model). Utilisez curl pour valider avant code.

Implémenter API route chat basique

src/app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server';
import openrouter from '@/lib/openrouter';
import { z } from 'zod';

const schema = z.object({
  model: z.string().default('anthropic/claude-3.5-sonnet'),
  messages: z.array(z.object({ role: z.enum(['user', 'system']), content: z.string() })),
});

export async function POST(req: NextRequest) {
  try {
    const { model, messages } = schema.parse(await req.json());
    const completion = await openrouter.chat.completions.create({
      model,
      messages,
      temperature: 0.7,
    });
    return NextResponse.json({ result: completion.choices[0].message });
  } catch (error) {
    return NextResponse.json({ error: 'Validation or API failed' }, { status: 400 });
  }
}

Route POST /api/chat valide inputs Zod, appelle chat.completions. Retourne message IA. Analogie : Zod comme garde-barrière anti-prompt injection. Piège : Sans try/catch, erreurs Zod crashent ; testez avec curl -X POST http://localhost:3000/api/chat -H 'Content-Type: application/json' -d '{"messages":[{"role":"user","content":"Hello"}] }'.

Ajouter streaming Server-Sent Events

src/app/api/chat/stream/route.ts
import { NextRequest, NextResponse } from 'next/server';
import openrouter from '@/lib/openrouter';
import { z } from 'zod';

const schema = z.object({
  model: z.string().default('anthropic/claude-3.5-sonnet'),
  messages: z.array(z.object({ role: z.enum(['user', 'system']), content: z.string() })),
});

export async function POST(req: NextRequest) {
  try {
    const { model, messages } = schema.parse(await req.json());
    const stream = await openrouter.chat.completions.create({
      model,
      messages,
      stream: true,
      temperature: 0.7,
    });
    const encoder = new TextEncoder();
    const streamResponse = new ReadableStream({
      async start(controller) {
        try {
          for await (const chunk of stream) {
            const data = chunk.choices[0]?.delta?.content || '';
            controller.enqueue(encoder.encode(`data: ${data}\n\n`));
          }
          controller.enqueue(encoder.encode('data: [DONE]\n\n'));
        } catch (err) {
          controller.error(err);
        } finally {
          controller.close();
        }
      },
    });
    return new Response(streamResponse, {
      headers: { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' },
    });
  } catch (error) {
    return NextResponse.json({ error: 'Stream failed' }, { status: 500 });
  }
}

Active stream: true pour SSE. ReadableStream parse deltas en temps réel. Headers SSE essentiels pour browsers. Piège : Oublier [DONE] bloque clients ; gère erreurs pour éviter fuites mémoire en prod.

Routing avancé et fallbacks

OpenRouter excelle en routing intelligent : spécifiez provider ou models array pour fallbacks auto (ex: si Sonnet down, fallback Gemini). Param sort: 'price' optimise coûts.

API avec fallbacks et routing custom

src/app/api/chat/fallback/route.ts
import { NextRequest, NextResponse } from 'next/server';
import openrouter from '@/lib/openrouter';
import { z } from 'zod';

const schema = z.object({
  messages: z.array(z.object({ role: z.enum(['user', 'system']), content: z.string() })),
});

export async function POST(req: NextRequest) {
  try {
    const { messages } = schema.parse(await req.json());
    const completion = await openrouter.chat.completions.create({
      model: 'anthropic/claude-3.5-sonnet',
      messages,
      provider: {  // Fallback si provider principal down
        allow_fallbacks: true,
        force: false,
        order: ['anthropic', 'openai'],
        sort: 'auto',
      },
      temperature: 0.7,
      extra_headers: {
        'X-Title': 'Mon App IA',
      },
    });
    const usage = completion.usage;
    return NextResponse.json({
      result: completion.choices[0].message,
      usage,
      headers: { 'X-Provider': completion.headers?.['x-provider'] },
    });
  } catch (error: any) {
    return NextResponse.json({ error: error.message }, { status: 500 });
  }
}

provider object active fallbacks ordonnés par sort: 'auto' (latence/prix). extra_headers pour traçabilité. Parse usage pour billing. Piège : Sans allow_fallbacks: true, pas de résilience ; loggez headers['x-provider'] pour debug.

Monitoring et beascout avancé

src/app/api/models/route.ts
import { NextResponse } from 'next/server';
import openrouter from '@/lib/openrouter';

export async function GET() {
  try {
    const models = await openrouter.models.list({
      sort: 'performance',
    });
    const filtered = models.data.filter(m => 
      m.id.includes('claude') || m.id.includes('gpt-4o')
    );
    return NextResponse.json({ models: filtered.slice(0, 10) });
  } catch (error) {
    return NextResponse.json({ error: 'Models fetch failed' }, { status: 500 });
  }
}

export async function POST() {
  // Beascout : test auto meilleur modèle
  const testPrompt = [{ role: 'user', content: 'Résume ce JSON en français' }];
  const results = await Promise.all([
    openrouter.chat.completions.create({ model: 'anthropic/claude-3.5-sonnet', messages: testPrompt }),
    openrouter.chat.completions.create({ model: 'openai/gpt-4o', messages: testPrompt }),
  ]);
  const best = results.reduce((best, res, i) => 
    (res.usage?.total_tokens ?? 0) < (best.usage?.total_tokens ?? Infinity) ? res : best
  );
  return NextResponse.json({ bestModel: best.model });
}

GET liste modèles triés performance, filtre leaders. POST beascout teste/comparaison tokens. Analogie : Leaderboard comme Top 10 pilotes F1. Piège : Rate limits (60 req/min gratuit) ; cachez résultats Redis en prod.

Bonnes pratiques

  • Toujours valider Zod : Protège contre injections et abus API.
  • Implémentez retries exponentiels avec p-retry pour 99.99% uptime.
  • Cachez réponses (Upstash Redis) pour prompts récurrents, économisez 50% coûts.
  • Monitorez headers X-Usage, X-Provider via Vercel Logs/Prometheus.
  • Sécurisez clé : Vercel env vars, pas commit .env ; rotatez keys mensuel.

Erreurs courantes à éviter

  • Oublier baseURL : Appels vont OpenAI direct, facturation double.
  • Ignorer rate limits : 20k req/jour gratuit ; implémentez queue (BullMQ).
  • Pas de stream: true parsing : Clients frontend bloqués sur deltas vides.
  • Fallbacks sans allow_fallbacks : Downtime 100% si modèle down (ex: outages Anthropic).

Pour aller plus loin

Approfondissez avec docs OpenRouter. Implémentez RAG avec Pinecone + OpenRouter. Découvrez nos formations IA avancées Learni pour agents autonomes et fine-tuning multi-modèles.