DeepSeek Vision and Grok Imagine models are now live on CometAPI →
guide/CometAPI Research

So entwickeln Sie eine Multi-Modell-KI-Chat-App mit Next.js und CometAPI

Erstelle eine Streaming-fähige Next.js-KI-Chat-App, die über einen einzigen CometAPI-Schlüssel und einen Backend-Endpunkt zwischen den Modellen GPT, Claude und Gemini umschaltet.

CometAPI
AnnaForschungsteam für KI-Modelle und API
Aktualisiert Aug 25, 2026 13 Min. Lesezeit
So entwickeln Sie eine Multi-Modell-KI-Chat-App mit Next.js und CometAPI
Dieses Muster verwenden

Den ersten API-Aufruf ausführen.

from openai import OpenAI

client = OpenAI(
    api_key="YOUR_COMETAPI_KEY",
    base_url="https://api.cometapi.com/v1",
)

response = client.chat.completions.create(
    model="gpt-5-mini",
    messages=[{"role": "user", "content": "Build this workflow."}],
)

print(response.choices[0].message.content)

Für einen Next.js-Chatbot, der mehrere Modellanbieter benötigt, ist ein Backend am nützlichsten, das die Authentifizierung zentralisiert und der Anwendung erlaubt, pro Anfrage ein Modell zu wählen. CometAPI bietet dieses Muster: Der Server behält einen API-Schlüssel und die OpenAI-kompatible Basis-URL https://api.cometapi.com/v1, während das model-Feld der Anfrage ein verfügbares GPT-, Claude-, Gemini- oder anderes Chatmodell auswählt.

Dieses Tutorial baut ein lauffähiges App-Router-Projekt mit einer serverseitigen Modell-Allowlist, einem Streaming-Route-Handler, einer Modellwahl im Browser, geschützten Umgebungsvariablen, einer vollständigen Chatoberfläche und Hinweisen zur Bereitstellung. Der CometAPI-Schlüssel gelangt niemals in den Browser.

Was ist eine Multi-Model-AI-Chat-App?

Eine Multi-Model-AI-Chat-Architektur setzt ein vom Backend der Anwendung kontrolliertes System zwischen die Benutzeroberfläche und mehrere Modellanbieter. In diesem Projekt sendet der Browser die Unterhaltung an /api/chat, während der Server die CometAPI-Zugangsdaten privat hält, die angeforderte Modell-ID validiert und die Anfrage über eine kompatible API weiterleitet.

Wie funktioniert das Modellwechseln in Next.js?

Das Wechseln von Modellen ist eine Routing-Entscheidung. Der Browser sendet eine genehmigte Modell-ID mit der Unterhaltung an /api/chat. Der Route-Handler fügt die CometAPI-Zugangsdaten hinzu, ruft POST /v1/chat/completions auf und streamt die Antwort des ausgewählten Modells zurück in den Browser.

Der gemeinsame Endpunkt macht nicht alle Modelle identisch. Ausgabestil, Tool-Verhalten, unterstützte Parameter, Kontextgrenzen und Preise können sich unterscheiden. Halten Sie Modell-IDs in einer serverseitigen Allowlist und testen Sie jede Route mit denselben Anwendungsprompts, bevor Sie sie für Nutzer verfügbar machen.

Was Sie bauen werden

Die fertige Anwendung hat ein Next.js-Backend und drei auswählbare Chat-Routen:

Model IDBeispielrolleCometAPI-Preis
gemini-3.7-flashKostenbewusster, hochvolumiger Chat$0.60 Eingabe / $3.00 Ausgabe
claude-opus-5Fokussiertes Premium-Reasoning$4 Eingabe / $20 Ausgabe
gpt-5.6gpt-5.6 ist die generische CometAPI-Route für GPT-5.6 und ist derzeit der Sol-Stufe zugeordnet.$3.2 Eingabe / $16 Ausgabe im Kurzkontext-Tier

Hinweis zur Preisgestaltung: Die unten angegebenen Preise sind in USD pro eine Million Tokens, wurden am 21. August 2026 geprüft und können sich ändern. Überprüfen Sie die aktuellen Preise auf den Modellseiten, bevor Sie sie in der Produktion verwenden. Die Tabelle ist ein Routing-Beispiel, keine Qualitätsrangliste. GPT-5.6 verwendet oberhalb von 272.000 Tokens eine höhere Preisstufe. Siehe die verlinkten Modellseiten für die hier verwendeten datierten Preise: Gemini 3.7 Flash, Claude Opus 5 und GPT-5.6.

CometAPI bepreist Modelle transparent gemäß der offiziellen API jedes Anbieters. Modelle mit einheitlicher offizieller Preisgestaltung — OpenAI, Claude, Gemini und ähnliche — werden pro Token zu einem 0,8:1-Verhältnis zum offiziellen Preis abgerechnet, einem Rabatt von 20%. Modelle ohne offizielle APIs (MidJourney, Kling, Luma) werden pro Aufruf zu den von CometAPI festgelegten Sätzen abgerechnet, ebenfalls um 20% rabattiert. Siehe den CometAPI Pricing Guide für die Aufschlagsformel, die einzelnen Modellpreise und Abrechnungseinheiten.

Die drei Routen decken unterschiedliche Trade-offs ab. Gemini 3.7 Flash ist Googles effizienter agentischer Leistungsträger mit multimodaler Eingabe und einem Kontextfenster von 1.048.576 Tokens für hochvolumigen Chat, Coding und Wissens-Workflows. Claude Opus 5 ist das Frontier-Reasoning-Modell von Anthropic, stark in mehrschrittiger Analyse, Code und sorgfältigem Longform-Schreiben zu einem Premiumpreis. GPT-5.6 ist OpenAIs Allzweckmodell, das Reasoning, Tool-Nutzung und Entwurf über ein großes Kontextfenster für den alltäglichen Produktionsverkehr ausbalanciert.

Bevor Sie beginnen

Sie benötigen Node.js, npm, ein CometAPI-Konto und einen serverseitigen API-Schlüssel aus dem CometAPI Quick Start. Die drei exakten Modell-IDs oben waren am 21. August 2026 im Live-Katalog verfügbar, nicht als „coming soon“ gekennzeichnet und stellten POST /v1/chat/completions bereit.

Das Tutorial verwendet diese gemeinsamen Einstellungen:

Schritt 1: Erstellen Sie die Next.js-App

npx create-next-app@latest multi-model-chat --ts --app --eslintcd multi-model-chatnpm run dev

Für diese Version ist kein SDK eines Modellanbieters erforderlich. Der Server verwendet die eingebaute fetch-API und leitet den Upstream-Server-Sent-Events-Stream weiter.

Schritt 2: Behalten Sie den CometAPI-Schlüssel auf dem Server

Erstellen Sie .env.local im Projektstamm:

COMETAPI_API_KEY=replace_with_your_cometapi_keyCOMETAPI_BASE_URL=https://api.cometapi.com/v1

Prefixen Sie den Schlüssel nicht mit NEXT_PUBLIC_. Next.js exponiert nur Variablen mit diesem Präfix in Browser-Bundles; der API-Schlüssel gehört in den serverseitigen Route-Handler.

Schritt 3: Definieren Sie die Modellrichtlinie

Legen Sie die erlaubten IDs im Backend fest, nicht nur im Dropdown. Ein Benutzer kann Browser-Steuerungen umgehen und Ihre Route direkt aufrufen, daher muss der Server unbekannte Modellwerte zurückweisen.

const ALLOWED_MODELS = [  "gemini-3.7-flash",  "claude-opus-5",  "gpt-5.6",] as const;​type ModelId = (typeof ALLOWED_MODELS)[number];

Der Client verwendet dieselben drei IDs für seinen Selektor, während der Server die maßgebliche Quelle bleibt.

Schritt 4: Streamen Sie CometAPI über einen Route-Handler

Erstellen Sie app/api/chat/route.ts. Die Route validiert eine Chat-Payload nur mit Text, ruft CometAPI mit stream: true auf und gibt den Upstream-Event-Stream zurück, ohne die Zugangsdaten offenzulegen.

export const runtime = "nodejs";export const dynamic = "force-dynamic";​const ALLOWED_MODELS = [  "gemini-3.7-flash",  "claude-opus-5",  "gpt-5.6",] as const;​type ModelId = (typeof ALLOWED_MODELS)[number];type ChatMessage = {  role: "system" | "user" | "assistant";  content: string;};​function isModelId(value: unknown): value is ModelId {  return (    typeof value === "string" &&    (ALLOWED_MODELS as readonly string[]).includes(value)  );}​function isChatMessage(value: unknown): value is ChatMessage {  if (typeof value !== "object" || value === null) return false;​  const message = value as Record<string, unknown>;  return (    ["system", "user", "assistant"].includes(String(message.role)) &&    typeof message.content === "string" &&    message.content.length > 0 &&    message.content.length <= 20_000  );}​export async function POST(request: Request) {  const apiKey = process.env.COMETAPI_API_KEY;  const baseUrl = (    process.env.COMETAPI_BASE_URL || "https://api.cometapi.com/v1"  ).replace(/\/$/, "");​  if (!apiKey) {    return Response.json(      { error: "COMETAPI_API_KEY is not configured." },      { status: 500 },    );  }​  let payload: { model?: unknown; messages?: unknown };​  try {    payload = await request.json();  } catch {    return Response.json({ error: "Invalid JSON body." }, { status: 400 });  }​  if (!isModelId(payload.model)) {    return Response.json({ error: "Unsupported model ID." }, { status: 400 });  }​  if (    !Array.isArray(payload.messages) ||    payload.messages.length === 0 ||    payload.messages.length > 50 ||    !payload.messages.every(isChatMessage)  ) {    return Response.json(      { error: "messages must contain 1 to 50 valid text messages." },      { status: 400 },    );  }​  const upstream = await fetch(`${baseUrl}/chat/completions`, {    method: "POST",    headers: {      Authorization: `Bearer ${apiKey}`,      "Content-Type": "application/json",    },    body: JSON.stringify({      model: payload.model,      messages: payload.messages,      stream: true,    }),    cache: "no-store",    signal: request.signal,  });​  if (!upstream.ok) {    const requestId = upstream.headers.get("x-request-id");​    console.error("CometAPI request failed", {      status: upstream.status,      requestId,    });​    return Response.json(      {        error: "The selected model request failed.",        status: upstream.status,        requestId,      },      { status: upstream.status },    );  }​  if (!upstream.body) {    return Response.json(      { error: "The model returned no response body." },      { status: 502 },    );  }​  return new Response(upstream.body, {    status: 200,    headers: {      "Content-Type": "text/event-stream; charset=utf-8",      "Cache-Control": "no-cache, no-transform",    },  });}

Der Handler gibt das Trennsignal des Browsers nach Upstream weiter, sodass das Schließen der Anfrage unnötige Generierung stoppen kann. Er gibt außerdem eine bereinigte Fehlermeldung an den Client zurück und behält diagnostische Details in den Server-Logs.

Schritt 5: Modellwahl und Stream-Parsing hinzufügen

Erstellen Sie eine Client-Komponente in app/page.tsx. Sie sendet das ausgewählte Modell zusammen mit der Unterhaltung, parst jedes data:-Event und hängt den inkrementellen delta.content-Text an die letzte Assistant-Nachricht an.

Der Browser ruft CometAPI niemals direkt auf. Sein einziges Ziel ist Ihre eigene /api/chat-Route, die den Schlüssel privat hält und die Allowlist durchsetzt.

Kernprojekt-Code

Der oben gezeigte Route-Handler ist die vollständige Datei app/api/chat/route.ts. Fügen Sie die folgende Seite, das Layout und das Stylesheet hinzu, um das ausführbare Projekt abzuschließen.

app/page.tsx

"use client";​import { FormEvent, useState } from "react";​const MODEL_OPTIONS = [  { id: "gemini-3.7-flash", label: "Gemini 3.7 Flash" },  { id: "claude-opus-5", label: "Claude Opus 5" },  { id: "gpt-5.6", label: "GPT-5.6" },] as const;​type Role = "user" | "assistant";type Message = { role: Role; content: string };​function textFromSseLine(line: string): string {  const trimmed = line.trim();  if (!trimmed.startsWith("data:")) return "";​  const data = trimmed.slice(5).trim();  if (!data || data === "[DONE]") return "";​  try {    const event = JSON.parse(data);    return event.choices?.[0]?.delta?.content ?? "";  } catch {    return "";  }}​export default function Home() {  const [model, setModel] = useState("gemini-3.7-flash");  const [messages, setMessages] = useState<Message[]>([]);  const [input, setInput] = useState("");  const [loading, setLoading] = useState(false);  const [error, setError] = useState("");​  function appendAssistantText(text: string) {    if (!text) return;​    setMessages((current) => {      const next = [...current];      const lastIndex = next.length - 1;​      if (lastIndex >= 0 && next[lastIndex].role === "assistant") {        next[lastIndex] = {          ...next[lastIndex],          content: next[lastIndex].content + text,        };      }​      return next;    });  }​  async function sendMessage(event: FormEvent<HTMLFormElement>) {    event.preventDefault();​    const content = input.trim();    if (!content || loading) return;​    const outgoing: Message[] = [...messages, { role: "user", content }];    setMessages([...outgoing, { role: "assistant", content: "" }]);    setInput("");    setError("");    setLoading(true);​    try {      const response = await fetch("/api/chat", {        method: "POST",        headers: { "Content-Type": "application/json" },        body: JSON.stringify({ model, messages: outgoing }),      });​      if (!response.ok) {        const body = await response.json().catch(() => ({}));        throw new Error(body.error || `Request failed with ${response.status}`);      }​      if (!response.body) throw new Error("Streaming is not available.");​      const reader = response.body.getReader();      const decoder = new TextDecoder();      let buffer = "";​      while (true) {        const { value, done } = await reader.read();        buffer += decoder.decode(value, { stream: !done });​        const lines = buffer.split("\n");        buffer = lines.pop() ?? "";​        for (const line of lines) {          appendAssistantText(textFromSseLine(line));        }​        if (done) {          appendAssistantText(textFromSseLine(buffer));          break;        }      }    } catch (requestError) {      setError(        requestError instanceof Error ? requestError.message : "Request failed.",      );    } finally {      setLoading(false);    }  }​  return (    <main className="shell">      <section className="chat">        <header>          <p className="eyebrow">Next.js + CometAPI</p>          <h1>Multi-model chat</h1>          <label>            Model            <select              value={model}              onChange={(event) => setModel(event.target.value)}              disabled={loading}            >              {MODEL_OPTIONS.map((option) => (                <option key={option.id} value={option.id}>                  {option.label}                </option>              ))}            </select>          </label>        </header>​        <div className="messages" aria-live="polite">          {messages.length === 0 ? (            <p className="empty">Choose a model and send a message.</p>          ) : (            messages.map((message, index) => (              <article className={message.role} key={`${message.role}-${index}`}>                <b>{message.role === "user" ? "You" : "Assistant"}</b>                <p>{message.content || "…"}</p>              </article>            ))          )}        </div>​        <form onSubmit={sendMessage}>          <textarea            value={input}            onChange={(event) => setInput(event.target.value)}            placeholder="Ask something…"            rows={3}            maxLength={20_000}          />          <button disabled={loading || !input.trim()} type="submit">            {loading ? "Streaming…" : "Send"}          </button>        </form>​        {error ? <p className="error">{error}</p> : null}      </section>    </main>  );}

app/layout.tsx

import type { Metadata } from "next";import "./globals.css";​export const metadata: Metadata = {  title: "Multi-Model Chat",  description: "A streaming Next.js chat app powered by CometAPI.",};​export default function RootLayout({  children,}: Readonly<{ children: React.ReactNode }>) {  return (    <html lang="en">      <body>{children}</body>    </html>  );}

app/globals.css

:root {  color-scheme: dark;  font-family: Arial, sans-serif;  background: #07111f;  color: #eef4ff;}* { box-sizing: border-box; }body { margin: 0; }button, select, textarea { font: inherit; }​/* Layout shell and chat card */.shell { min-height: 100vh; display: grid; place-items: center; padding: 32px 16px; }.chat { width: min(820px, 100%); background: #0d1b2e; border: 1px solid #223957; border-radius: 20px; padding: 24px; }​/* Message list and bubbles */.messages { min-height: 360px; display: grid; align-content: start; gap: 12px; margin: 24px 0; }.messages article { max-width: 85%; padding: 12px 14px; border-radius: 14px; white-space: pre-wrap; }.user { justify-self: end; background: #164f8f; }.assistant { justify-self: start; background: #182a42; }​/* Form controls, buttons, and error states follow the same dark theme. */

Schritt 6: App ausführen und bereitstellen

npm run dev

Öffnen Sie http://localhost:3000, wählen Sie ein Modell und senden Sie eine Nachricht. Für eine produktive Node.js-Bereitstellung fügen Sie COMETAPI_API_KEY und COMETAPI_BASE_URL zu den serverseitigen Umgebungsvariablen des Hosts hinzu und führen dann aus:

npm run buildnpm run start

Verwenden Sie eine Hosting-Umgebung, die Streaming-Antworten unterstützt. Ein statischer Export kann den /api/chat-Route-Handler nicht ausführen.

Streaming-Route testen

Bei laufendem Entwicklungsserver rufen Sie Ihr Backend direkt auf:

curl -N http://localhost:3000/api/chat \  -H "Content-Type: application/json" \  -d '{    "model": "gemini-3.7-flash",    "messages": [      {"role": "user", "content": "Explain model routing in two sentences."}    ]  }'

Eine erfolgreiche Anfrage gibt Server-Sent Events zurück. Die genauen IDs und Texte variieren, aber der Stream folgt diesem Muster:

data: {"choices":[{"delta":{"content":"Model"}}]}​data: {"choices":[{"delta":{"content":" routing"}}]}​data: [DONE]

Der Browser-Parser liest jedes SSE-Event, extrahiert choices[0].delta.content und hängt den gestreamten Text beim Eintreffen als Chunks an die Assistant-Nachricht an.

Häufige Integrationsfehler

SymptomUrsacheLösung
401-AuthentifizierungsfehlerFehlender oder ungültiger serverseitiger SchlüsselCOMETAPI_API_KEY setzen; nicht mit NEXT_PUBLIC_ exponieren.
404 oder falsche RouteDie Basis-URL fehlt /v1https://api.cometapi.com/v1 verwenden.
400 nicht unterstütztes ModellDie ID ist nicht in der Backend-AllowlistExakte, live verfügbare Text-Modell-ID verwenden und beide Selektoren aktualisieren.
Die Antwort erscheint auf einmalHost oder Proxy puffert den Event-StreamAntworttransformation deaktivieren und eine Streaming-fähige Node.js-Bereitstellung nutzen.

Chat-Backend für die Produktion vorbereiten

  • Authentifizieren Sie Ihre eigenen Nutzer. Exponieren Sie keine öffentliche Route, die Credits für anonymen Traffic verbraucht.
  • Rate-Limits pro Nutzer und IP. Begrenzen Sie gleichzeitige Streams, Anfragen pro Minute, Nachrichtenanzahl und Nachrichtenlänge.
  • Halten Sie die Modell-Allowlist serverseitig. Der Browser-Selektor ist eine Bequemlichkeit, keine Sicherheitsgrenze.
  • Validieren Sie den Live-Katalog während der Bereitstellung. GET https://api.cometapi.com/api/models abfragen und den Release fehlschlagen lassen, wenn eine konfigurierte ID „upcoming“, nicht verfügbar ist oder den Chat-Completions-Endpunkt nicht bietet.
  • Kosten pro Route verfolgen. Ausgewähltes Modell, Request-ID, Latenz, Token-Nutzung und Benutzer-ID loggen. Schlüsselkontingente oder Ausgabenkappen im CometAPI-Dashboard setzen, wo angemessen.
  • Disconnects und Timeouts handhaben. request.signal beibehalten, einen Anwendungstimeout setzen und die Arbeit stoppen, wenn der Client die Verbindung trennt.
  • Konfigurationsfehler nicht mit Fallback verbergen. 400- und 401-Antworten sichtbar machen. Ein anderes Modell nur für eine begrenzte Menge wiederholbarer Fehler verwenden und nur, wenn das Anfrage-Schema kompatibel ist; siehe den CometAPI Model Fallback Guide für eine zweistufige Fallback-Kette (CometAPI primär → CometAPI-Fallback-Modell → offizieller Anbieter).
  • Logs redigieren. API-Schlüssel, vollständige Prompts und sensible Modellausgaben aus produktiven Fehler-Logs fernhalten.

Ein Next.js-Backend, mehrere Modelloptionen

Der Modellwechsel gehört in Ihre Serverrichtlinie, nicht in separate Anbieter-Accounts. Ein Next.js-Route-Handler kann einen CometAPI-Schlüssel privat halten, pro Anfrage eine genehmigte Modell-ID akzeptieren und das ausgewählte Modell über einen OpenAI-kompatiblen Endpunkt streamen. Das Frontend bleibt einfach, während das Backend die Kontrolle über Zugriff, Validierung, Beobachtbarkeit und Kosten behält. Mit demselben Konto und Schlüssel erreichen Sie auch die nativen Bild- und Video-APIs von CometAPI — etwa Flux für Bildgenerierung und Kling für Videogenerierung — sodass dieses Chat-Backend ohne zweite Integration auf multimodale Workflows erweitert werden kann.

Verwenden Sie das öffentliche Modellverzeichnis von CometAPI für die Modellentdeckung und GET https://api.cometapi.com/api/models für automatisierte Routing-Validierung.

Häufig gestellte Fragen

Kann ich mehrere AI-Modelle in einer Next.js-App verwenden?

Ja. Behalten Sie eine serverseitige Route und übergeben Sie mit jeder Anfrage eine Modell-ID aus der Allowlist. Die Browser-UI kann Modelloptionen anbieten, während der Server kontrolliert, welche IDs akzeptiert werden.

Wie wechsle ich zwischen GPT, Claude und Gemini?

Senden Sie die ausgewählte Modell-ID im Anfrage-Body. Der Server validiert sie gegen eine Allowlist und leitet dieselbe Chat-Payload über den OpenAI-kompatiblen Endpunkt an das gewählte Modell weiter.

Wo sollte ich meinen CometAPI-API-Schlüssel speichern?

Speichern Sie ihn in einer serverseitigen Umgebungsvariablen wie COMETAPI_API_KEY. Exponieren Sie ihn niemals über eine NEXT_PUBLIC_-Variable oder Client-seitigen Code.

Macht eine OpenAI-kompatible API alle Modelle austauschbar?

Nein. Die Anfragenform ist portabel, aber Modelle unterscheiden sich in unterstützten Parametern, Kontextgrenzen, Tool-Verhalten, Ausgabestil, Latenz und Preis. Testen Sie jedes Modell in der Allowlist mit Ihren Produktionsprompts.

Können Next.js-Route-Handler AI-Antworten streamen?

Ja. Ein Route-Handler kann den Upstream-Server-Sent-Events-Stream mit einem text/event-stream-Content-Type zurückgeben, vorausgesetzt, die Bereitstellungsplattform und jeder vorgeschaltete Proxy puffern die Antwort nicht.

Kann ich diese App als statische Next.js-Site bereitstellen?

Nein. Ein statischer Export kann den serverseitigen /api/chat-Route-Handler nicht ausführen oder den API-Schlüssel schützen. Verwenden Sie eine Node.js- oder andere kompatible Server-Runtime, die Streaming-Antworten unterstützt.

Weiterlernen

Diesen Artikel mit der nächsten Entscheidung verknüpfen.

Alle Themen anzeigen
Veröffentlicht am Aug 25, 2026
Zuletzt aktualisiert Aug 25, 2026
0 Aufrufe
Auf Klarheit, Quellenangabe und aktuelle API-Terminologie geprüft.

Bereit, die KI-Entwicklungskosten um 20 % zu senken?

In wenigen Minuten kostenlos starten. Inklusive kostenlosem Testguthaben. Keine Kreditkarte erforderlich.

Mehr lesen