LangChain con Next.js
Next.js es el framework ideal para aplicaciones LangChain. El App Router ofrece capacidades del lado del servidor para ejecutar LangChain de forma segura, mientras que React Server Components y las rutas API habilitan patrones de streaming potentes.
🚀 Beneficios de Next.js + LangChain
- Server Components: Ejecuta LangChain en el servidor de forma segura
- Route Handlers: Crea endpoints API con streaming
- Edge Runtime: Despliega en edge para baja latencia
- Streaming integrado: Soporte nativo para respuestas en streaming
Configuración del proyecto
Crea un proyecto Next.js App Router e instala LangChain junto con el Vercel AI SDK.
# Create Next.js app
npx create-next-app@latest my-ai-app --typescript --tailwind --app
cd my-ai-app
# Install LangChain and AI SDK
npm install langchain @langchain/openai @langchain/core
npm install ai # Vercel AI SDK
Variables de entorno
Guarda las claves del proveedor en .env.local para que solo estén disponibles en el servidor.
# .env.local
OPENAI_API_KEY=sk-your-openai-key
# Optional: for other providers
ANTHROPIC_API_KEY=sk-ant-your-key
Ruta API básica
Un Route Handler simple que acepta un mensaje, llama al modelo y retorna JSON.
// app/api/chat/route.ts
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
import { NextResponse } from "next/server";
const model = new ChatOpenAI({
modelName: "gpt-4",
temperature: 0.7,
});
export async function POST(req: Request) {
try {
const { message } = await req.json();
const response = await model.invoke([
new HumanMessage(message),
]);
return NextResponse.json({
content: response.content,
});
} catch (error) {
return NextResponse.json(
{ error: "Failed to generate response" },
{ status: 500 }
);
}
}
Ruta API con streaming
Habilita streaming de tokens en tiempo real para mejor UX:
Transmite chunks del modelo y envíalos como respuesta legible al cliente.
// app/api/chat/stream/route.ts
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage, SystemMessage } from "@langchain/core/messages";
export const runtime = 'edge'; // Optional: use edge runtime
export async function POST(req: Request) {
const { messages } = await req.json();
const model = new ChatOpenAI({
modelName: "gpt-4",
streaming: true,
});
// Convert to LangChain messages
const langchainMessages = messages.map((m: any) =>
m.role === 'user'
? new HumanMessage(m.content)
: new SystemMessage(m.content)
);
// Create streaming response
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
const streamResponse = await model.stream(langchainMessages);
for await (const chunk of streamResponse) {
const text = chunk.content;
controller.enqueue(encoder.encode(text));
}
controller.close();
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/plain; charset=utf-8',
'Transfer-Encoding': 'chunked',
},
});
}
Usar Vercel AI SDK (Recomendado)
El Vercel AI SDK simplifica el streaming de forma significativa:
Usa el adaptador de LangChain para convertir streams del modelo en una respuesta compatible con el SDK.
// app/api/chat/route.ts
import { ChatOpenAI } from "@langchain/openai";
import { LangChainAdapter, Message } from "ai";
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages }: { messages: Message[] } = await req.json();
const model = new ChatOpenAI({
modelName: "gpt-4",
streaming: true,
});
const stream = await model.stream(
messages.map(m => ({
role: m.role,
content: m.content,
}))
);
return LangChainAdapter.toDataStreamResponse(stream);
}
Componente de cliente
El cliente usa useChat para gestionar la entrada y renderizar respuestas en streaming.
// app/components/Chat.tsx
'use client';
import { useChat } from 'ai/react';
export default function Chat() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
return (
<div className="flex flex-col h-screen max-w-2xl mx-auto p-4">
<div className="flex-1 overflow-y-auto space-y-4">
{messages.map(m => (
<div
key={m.id}
className={`p-4 rounded-lg ${
m.role === 'user' ? 'bg-blue-100 ml-auto' : 'bg-gray-100'
} max-w-[80%]`}
>
<p className="font-semibold text-sm mb-1">
{m.role === 'user' ? 'You' : 'AI'}
</p>
<p className="whitespace-pre-wrap">{m.content}</p>
</div>
))}
</div>
<form onSubmit={handleSubmit} className="mt-4">
<div className="flex gap-2">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask something..."
className="flex-1 p-3 border rounded-lg"
/>
<button
type="submit"
disabled={isLoading}
className="px-6 py-3 bg-blue-600 text-white rounded-lg disabled:opacity-50"
>
Send
</button>
</div>
</form>
</div>
);
}
Acciones de servidor
Usa Server Actions para operaciones simples sin streaming:
Las acciones son ideales para tareas como resumir o traducir donde no se requiere streaming.
// app/actions.ts
'use server';
import { ChatOpenAI } from "@langchain/openai";
import { HumanMessage } from "@langchain/core/messages";
const model = new ChatOpenAI({ modelName: "gpt-4" });
export async function generateSummary(text: string) {
const response = await model.invoke([
new HumanMessage(`Summarize this text: ${text}`),
]);
return response.content as string;
}
export async function translateText(text: string, language: string) {
const response = await model.invoke([
new HumanMessage(`Translate to ${language}: ${text}`),
]);
return response.content as string;
}
Usar acciones de servidor
Llama acciones desde formularios del cliente y gestiona el estado de la UI alrededor del resultado async.
// app/page.tsx
'use client';
import { useState } from 'react';
import { generateSummary } from './actions';
export default function Page() {
const [summary, setSummary] = useState('');
const [loading, setLoading] = useState(false);
async function handleSummarize(formData: FormData) {
setLoading(true);
const text = formData.get('text') as string;
const result = await generateSummary(text);
setSummary(result);
setLoading(false);
}
return (
<form action={handleSummarize}>
<textarea name="text" className="w-full p-2 border" />
<button type="submit" disabled={loading}>
{loading ? 'Summarizing...' : 'Summarize'}
</button>
{summary && <p>{summary}</p>}
</form>
);
}
RAG con Next.js
Combina un retriever con un prompt y un modelo para responder preguntas desde tu store de documentos.
// app/api/rag/route.ts
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "langchain/vectorstores/memory";
import { ChatPromptTemplate } from "@langchain/core/prompts";
import { StringOutputParser } from "@langchain/core/output_parsers";
import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
// Initialize once (consider caching in production)
let vectorStore: MemoryVectorStore | null = null;
async function getVectorStore() {
if (!vectorStore) {
// Load your documents here
vectorStore = await MemoryVectorStore.fromTexts(
["Your documents...", "More content..."],
[{}, {}],
new OpenAIEmbeddings()
);
}
return vectorStore;
}
export async function POST(req: Request) {
const { question } = await req.json();
const store = await getVectorStore();
const retriever = store.asRetriever({ k: 4 });
const model = new ChatOpenAI({ modelName: "gpt-4" });
const prompt = ChatPromptTemplate.fromTemplate(`
Answer based on context:
{context}
Question: {question}
`);
const chain = RunnableSequence.from([
{
context: retriever.pipe(docs => docs.map(d => d.pageContent).join("\n")),
question: new RunnablePassthrough(),
},
prompt,
model,
new StringOutputParser(),
]);
const answer = await chain.invoke(question);
return Response.json({ answer });
}
Estructura del proyecto
Organiza rutas API, utilidades compartidas de LangChain y componentes UI en el App Router.
my-ai-app/
├── app/
│ ├── api/
│ │ ├── chat/
│ │ │ └── route.ts # Chat API
│ │ └── rag/
│ │ └── route.ts # RAG API
│ ├── components/
│ │ └── Chat.tsx # Chat UI
│ ├── lib/
│ │ └── langchain.ts # LangChain setup
│ ├── actions.ts # Server Actions
│ ├── page.tsx # Home page
│ └── layout.tsx
├── .env.local # API keys
└── package.json
💡 Puntos clave
- • Usa Route Handlers (app/api) para endpoints de LangChain
- • Vercel AI SDK simplifica el streaming de forma notable
- • Server Actions funcionan muy bien para operaciones sin streaming
- • El runtime edge ofrece menor latencia para usuarios globales
- • Mantén claves API en .env.local, nunca las expongas al cliente
📚 Más recursos
-
Guía de LangChain para Next.js →
Guía oficial para integración con Next.js.
-
Vercel AI SDK + Next.js →
Buenas prácticas para IA en apps Next.js.