Cómo Crear un Chat con IA en Streaming Usando Next.js 16 y Claude API: Guía Paso a Paso
# Cómo Crear un Chat con IA en Streaming Usando Next.js 16 y Claude API: Guía Paso a Paso
¿Alguna vez te has preguntado cómo funcionan realmente esos chats con inteligencia artificial que ves en todas partes? No me refiero a los que usan un SDK mágico que hace todo por ti, sino a los que realmente entiendes cómo trabajan por debajo.
Hoy te voy a enseñar a construir tu propia aplicación de chat con IA en streaming. Y lo haremos desde cero, usando Next.js 16 y la API de Claude de Anthropic. Sin atajos, sin misterios.
¿Por Qué Construir un Chat Desde Cero?
Seguro que has oído hablar del Vercel AI SDK. Es el estándar de facto para añadir IA a aplicaciones Next.js. Y sí, funciona genial. Pero tiene un problema: te abstrae tanto que no entiendes qué está pasando realmente.
Cuando usas un SDK, el streaming, los manejadores de ruta (Route Handlers) y la comunicación con el servidor quedan ocultos. Es como conducir un coche automático: llegas a tu destino, pero no sabes cómo funciona el motor.
Construir desde cero te da superpoderes. Entiendes cómo viajan los datos, cómo se manejan los eventos en tiempo real y, lo más importante, cómo mantener segura tu clave de API. Y todo esto con solo unas 50 líneas de código.
La Estructura de Nuestra Aplicación
Vamos a usar la última versión de Next.js (16.2.6) con el App Router. Esto es importante porque la mayoría de los tutoriales que encuentras por ahí siguen usando Next.js 14 o 15. Las cosas han cambiado, y para mejor.
Nuestra app tendrá tres componentes principales:
- **Un Route Handler** en `/api/chat` que se comunica con Claude en el servidor - **SSE (Server-Sent Events)** para enviar respuestas en streaming al cliente - **Un componente React 19** con `"use client"` que renderiza el stream en tiempo real
La clave aquí es la separación entre código de servidor y cliente. La clave de API solo se lee en el servidor, nunca llega al navegador. Esto es posible gracias a cómo Next.js App Router separa ambos entornos.
Manos a la Obra: Configuración del Proyecto
Primero, necesitas tener Node.js 18 o superior. También necesitarás una clave de API de Anthropic, que puedes obtener en [console.anthropic.com](https://console.anthropic.com).
Abre tu terminal y ejecuta:
```bash npx create-next-app@latest nextjs-claude-chat --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" ```
Esto crea un proyecto con Next.js 16.2.6 y React 19.2.4. Luego instala el SDK de Anthropic:
```bash npm install @anthropic-ai/sdk ```
Ahora, crea un archivo `.env.local` en la raíz del proyecto y añade tu clave de API:
``` ANTHROPIC_API_KEY=sk-ant-tu-clave-real-aqui ```
Importante: **nunca uses el prefijo `NEXT_PUBLIC_`** para claves de API. Eso las expondría en el bundle del navegador, y cualquiera podría verlas con las herramientas de desarrollador.
El Corazón del Servidor: Route Handler
Vamos a crear el archivo `src/app/api/chat/route.ts`. Este es el núcleo de nuestra aplicación. Aquí es donde ocurre la magia del streaming.
```typescript import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, });
export async function POST(req: Request) { const { messages } = await req.json();
const stream = await client.messages.stream({ model: "claude-opus-4-7", max_tokens: 1024, messages, });
const encoder = new TextEncoder(); const readable = new ReadableStream({ async start(controller) { for await (const chunk of stream) { if ( chunk.type === "content_block_delta" && chunk.delta.type === "text_delta" ) { controller.enqueue( encoder.encode( `data: ${JSON.stringify({ text: chunk.delta.text })}
` ) ); } } controller.enqueue(encoder.encode("data: [DONE]
")); controller.close(); }, });
return new Response(readable, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }, }); } ```
¿Ves qué sencillo? El método `client.messages.stream()` devuelve un `AsyncIterableStream`. Con un bucle `for await...of` vamos recibiendo fragmentos (chunks) uno a uno y los enviamos al cliente. Cuando el stream termina, enviamos una señal `[DONE]` y cerramos el controlador.
El filtro `content_block_delta` es importante: el protocolo de streaming de Anthropic emite varios tipos de eventos. Solo los eventos `text_delta` dentro de `content_block_delta` contienen texto real. Los demás son metadatos.
La Interface de Usuario: Componente Cliente
Ahora creamos `src/app/page.tsx`. Este es el componente que ve el usuario, con el estado de streaming gestionado manualmente.
```typescript "use client";
import { useState, useRef, useEffect } from "react";
type Message = { role: "user" | "assistant"; content: string; };
export default function ChatPage() {
const [messages, setMessages] = useState
useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]);
const sendMessage = async () => { if (!input.trim() || isLoading) return;
const userMessage: Message = { role: "user", content: input }; const updatedMessages = [...messages, userMessage]; setMessages(updatedMessages); setInput(""); setIsLoading(true);
const assistantMessage: Message = { role: "assistant", content: "" }; setMessages([...updatedMessages, assistantMessage]);
const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: updatedMessages }), });
if (!res.body) return;
const reader = res.body.getReader(); const decoder = new TextDecoder();
while (true) { const { done, value } = await reader.read(); if (done) break;
const chunk = decoder.decode(value); const lines = chunk.split(" ");
for (const line of lines) { if (line.startsWith("data: ") && line !== "data: [DONE]") { const data = JSON.parse(line.slice(6)); setMessages((prev) => { const last = prev[prev.length - 1]; return [ ...prev.slice(0, -1), { ...last, content: last.content + data.text }, ]; }); } } }
setIsLoading(false); };
return (
{msg.content}Claude Chat
Dos detalles importantes aquí. Primero, la comprobación `line !== "data: [DONE]"` evita que intentemos parsear `[DONE]` como JSON, lo que lanzaría un error. Segundo, usamos `setMessages((prev) => ...)` en lugar de acceder directamente al estado. Esto es crucial dentro de un bucle asíncrono, donde el estado podría haber cambiado entre iteraciones.
Lo Que No Debes Olvidar para Producción
Sé sincero: no lleves esto a producción tal cual. Te faltan tres cosas importantes:
1. **Manejo de errores**: Si la API de Claude falla (límite de tasa, error de red, clave inválida), el stream se corta y el usuario no ve nada. Necesitas bloques `try/catch` y eventos SSE de error.
2. **Límite de conversación**: El historial completo de mensajes se envía a la API en cada petición. Las conversaciones largas excederán la ventana de contexto o dispararán los costes. En producción, necesitas recortar a los últimos N mensajes o gestionar el conteo de tokens.
3. **Gestión de peticiones concurrentes**: Los mensajes rápidos o múltiples pestañas causan colisiones en el streaming. Falta lógica de `AbortController` para cancelar peticiones anteriores.
Cuando despliegues en Vercel, recuerda: - Añadir `ANTHROPIC_API_KEY` en Project Settings → Environment Variables - Forzar el runtime Node.js con `export const runtime = 'nodejs'` - Configurar el tiempo máximo de ejecución si superas los 10 segundos del plan Hobby
Conclusión: El Poder de Entender lo que Construyes
Construir un chat con IA desde cero te da algo que ningún SDK puede ofrecerte: comprensión profunda. Sabes cómo funciona el streaming, cómo se separa el código de servidor y cliente, y cómo mantener seguras tus claves de API.
¿Merece la pena hacerlo así siempre? No. Para prototipos rápidos, el Vercel AI SDK es tu mejor amigo. Pero para proyectos donde necesitas control total, personalización y rendimiento, entender estos fundamentos marca la diferencia.
Mi recomendación: construye esto una vez desde cero. Luego, usa el SDK. Sabrás exactamente qué está haciendo por ti.
Y si quieres llevar tu aplicación al siguiente nivel, considera añadir capacidades de herramientas (Tool Use) para que Claude pueda ejecutar funciones, o implementar caché de prompts para reducir costes hasta un 90 %.
¿Necesitas Ayuda con tu Proyecto de IA?
Si estás desarrollando una aplicación de inteligencia artificial o necesitas automatizar procesos en tu negocio, podemos ayudarte. En [Guillermo Mateo](https://guillermomateo.es) ofrecemos soluciones de automatización con IA y desarrollo de aplicaciones web personalizadas.
También te recomiendo echar un vistazo a la documentación oficial de [Anthropic](https://docs.anthropic.com) para profundizar en las capacidades de Claude API.
¿Tienes un proyecto en mente? No dudes en contactarnos. Estaremos encantados de ayudarte a construir algo increíble.