Tous les articles
·Ingénierie·10 min

Intégrer l'API Claude dans une app Next.js : streaming, tool use et RAG

Intégrez l'API Claude dans Next.js : streaming App Router, tool use structuré et pipeline RAG. Patterns TypeScript de production pour 2026.

L'API Claude s'intègre désormais nativement dans les workflows Next.js grâce au SDK Anthropic pour TypeScript. En 2026, trois cas d'usage dominent : le streaming de réponses directement dans l'App Router, le tool use pour appeler des fonctions typées côté serveur, et les pipelines RAG (Retrieval-Augmented Generation) pour interroger une base de connaissances propriétaire. Les patterns varient selon le cas — cet article couvre les trois avec des exemples prêts pour la production.

Ce guide suppose que vous utilisez Next.js 15+ avec l'App Router, TypeScript strict et le SDK @anthropic-ai/sdk. Les patterns s'appliquent à Claude Sonnet 5, Claude Opus 5 ou tout autre modèle Anthropic accessible via l'API.

La clé d'un projet IA robuste : bien séparer la couche transport (Route Handler, Server Action), la couche métier (construction du prompt, tools, historique) et la couche UI (rendu du stream). Ce découpage évite le couplage fort qui transforme les prototypes en dettes techniques.

Interface d'une application Next.js avec streaming de réponses IA en temps réel
Streaming Claude → Next.js App Router → React : le flux complet

Configurer le SDK Anthropic dans Next.js

Installez le SDK officiel et initialisez le client une fois au niveau module :

// lib/anthropic.ts
import Anthropic from "@anthropic-ai/sdk";

if (!process.env.ANTHROPIC_API_KEY) {
  throw new Error("ANTHROPIC_API_KEY manquante — vérifiez votre .env.local");
}

// Singleton : le SDK gère lui-même le pool de connexions HTTP
// Instancier un nouveau client à chaque requête coûte cher en latence
export const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

Ne jamais exposer cette clé côté client. Dans Next.js App Router, les Route Handlers (app/api/**) et les Server Actions s'exécutent exclusivement sur le serveur — c'est là que vivent tous les appels à l'API Claude. Une variable sans le préfixe NEXT_PUBLIC_ est invisible du bundle client : utilisez ce mécanisme comme première ligne de défense.

Le SDK accepte une configuration timeout, maxRetries et baseURL personnalisée — utile si vous passez par un proxy interne pour le rate limiting ou la facturation centralisée par équipe.

Streaming dans un Route Handler App Router

Le streaming permet d'afficher les tokens au fil de leur génération, sans attendre la réponse complète. Côté serveur, le pattern repose sur un ReadableStream passé à new Response() :

// app/api/chat/route.ts
import { anthropic } from "@/lib/anthropic";
import { NextRequest } from "next/server";

// nodejs plutôt qu'edge : le runtime edge bloque certains streams Node.js natifs
export const runtime = "nodejs";

export async function POST(req: NextRequest) {
  const { messages } = await req.json();

  const stream = anthropic.messages.stream({
    model: "claude-sonnet-5",
    max_tokens: 2048,
    messages,
  });

  const readableStream = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        if (
          chunk.type === "content_block_delta" &&
          chunk.delta.type === "text_delta"
        ) {
          controller.enqueue(new TextEncoder().encode(chunk.delta.text));
        }
      }
      controller.close();
    },
  });

  return new Response(readableStream, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

Côté React, un fetch avec lecture du ReadableStream via response.body.getReader() suffit pour afficher les tokens en temps réel. La librairie ai de Vercel abstrait ce pattern avec des hooks comme useChat si vous préférez ne pas le gérer manuellement — elle gère aussi le cursor et l'historique côté client.

Attention aux limites de durée : 30 secondes sur Vercel Hobby, jusqu'à 300 secondes sur les plans Pro. Pour les réponses très longues, découpez le problème en plusieurs appels ou renvoyez un identifiant de tâche asynchrone que le client vient poller.

Tool Use : appeler des fonctions TypeScript depuis Claude

Le tool use permet à Claude de déclencher des fonctions définies côté serveur — une API métier, une requête Prisma, une recherche Elastic — puis d'utiliser le résultat pour construire sa réponse finale. Le modèle retourne un bloc tool_use, vous exécutez la fonction, et renvoyez le résultat dans un second tour.

// lib/tools.ts
import type { Tool } from "@anthropic-ai/sdk/resources/messages";

// Définir le schéma ici garantit que Claude valide l'input avant de l'envoyer :
// si l'input ne correspond pas, le modèle reformule plutôt qu'échouer silencieusement
export const tools: Tool[] = [
  {
    name: "get_project_status",
    description: "Retourne le statut en cours d'un projet par son identifiant",
    input_schema: {
      type: "object",
      properties: {
        project_id: { type: "string", description: "UUID du projet" },
      },
      required: ["project_id"],
    },
  },
];

La boucle d'appel complète est un while sur stop_reason === "tool_use" : tant que Claude demande un outil, vous exécutez, vous renvoyez, et Claude continue. Fixez un maxIterations (5-10 selon le cas) pour éviter une boucle infinie en cas de bug de prompt.

Pour le typage TypeScript, coupler le schéma JSON du tool avec Zod garantit la cohérence bout en bout — un seul schéma source qui sert à la fois à la définition du tool Anthropic et à la validation de l'input reçu avant exécution. Voir l'article sur la validation avec Zod en Next.js pour ce pattern.

Pipeline RAG : embeddings, vector search et contexte injecté

Un pipeline RAG (Retrieval-Augmented Generation) permet à Claude de répondre à partir de documents propriétaires sans les inclure entiers dans chaque requête. Le flux en trois étapes :

Indexation : découper les documents en chunks, générer un embedding par chunk via un modèle dédié (Voyage AI, OpenAI text-embedding-3, ou Cohere), puis stocker vecteurs et contenu dans une base vectorielle. pgvector sur Supabase est le choix le plus simple dans un stack Postgres existant — pas de service supplémentaire à opérer.

Retrieval : à chaque requête, générer l'embedding de la question utilisateur, chercher les k chunks les plus proches par similarité cosinus, et filtrer par seuil de score (typiquement 0.7+) pour éviter d'injecter du contexte hors sujet.

Generation : injecter les chunks récupérés dans le prompt système de Claude sous forme de <context> balisé, avec une instruction explicite : "si la réponse n'est pas dans le contexte, dis-le clairement." Sans cette instruction, les LLMs hallucinent plutôt qu'admettre leur ignorance.

La qualité d'un RAG dépend à 80 % de la stratégie de chunking, pas du modèle de génération. Des chunks trop longs diluent le signal ; trop courts, ils perdent le contexte local. Une fenêtre de 512 tokens avec 10 % de chevauchement (overlap) est un bon point de départ pour du texte technique dense. Pour la mise en cache des embeddings — coûteux à recalculer — le pattern use cache de Next.js 15 s'applique directement. Voir l'article sur le caching Next.js App Router pour l'implémentation.

Gérer l'historique de conversation multi-tours

Pour une interface de chat multi-tours, l'historique doit survivre aux rechargements de page. Deux approches :

Stockage client : l'historique vit dans useState ou useReducer, sérialisé en sessionStorage. Adapté aux prototypes, limité à la session navigateur et invisible côté serveur — pas de SSR possible sur l'historique.

Stockage serveur : chaque message est persisté en base (Postgres via Prisma, Supabase) avec un conversation_id par session. Le Route Handler relit les N derniers messages à chaque appel. Cette approche ouvre le SSR, l'historique illimité et les analytics sur les conversations.

La limite de contexte de Claude (200K tokens sur les modèles récents) est généreuse, mais inclure 200 messages dans chaque requête gonfle inutilement la facture. Stratégie productive : ne passer que les 10-15 derniers messages complets et un résumé des messages plus anciens, généré lui-même par Claude lors d'un checkpoint automatique tous les N tours.

Prompt engineering et optimisation des coûts

Quelques règles pratiques qui impactent directement la facture et la qualité des réponses :

Le prompt caching d'Anthropic peut réduire les coûts jusqu'à 90 % sur les tokens mis en cache. Pour l'activer, marquez le system prompt avec cache_control: { type: "ephemeral" } dans le SDK. Le cache dure 5 minutes par défaut — idéal pour les system prompts statiques partagés entre requêtes d'une même session.

Préférez des instructions positives aux négatives : "réponds en JSON structuré" plutôt que "ne réponds pas en prose". Les instructions positives réduisent les malentendus sur les cas limites et accélèrent la génération sur les modèles récents.

Pour le structured output, utilisez le tool use avec un seul tool format_response plutôt que d'espérer que Claude suive un format JSON décrit en prose. Le schéma est enforced par le modèle lui-même — vous éliminez toute la logique de parsing et de fallback côté serveur.

En pratique

En production chez Kreio, les projets IA Next.js suivent une séparation stricte : le Route Handler valide les inputs avec Zod, délègue à un service chat.service.ts qui gère les tools et l'historique, et ne retourne au client que le stream ou la réponse finale. Le composant React ne sait pas si la réponse vient de Claude Sonnet ou d'un cache Redis — cette opacité facilite les migrations de modèle sans toucher au frontend.

Les Server Actions sont adaptées aux interactions ponctuelles non-streamées (classifier un texte, extraire des entités d'un formulaire soumis) ; les Route Handlers sont incontournables dès qu'on streame. Voir l'article sur les Server Actions Next.js App Router pour choisir entre les deux selon votre cas d'usage.

Pour le monitoring : enregistrez input_tokens, output_tokens, model et latency de chaque réponse Anthropic en base. En quelques semaines, vous identifierez les prompts les plus coûteux et les optimiserez via du prompt caching ou une réduction du contexte passé.

Sources