Next.js is the recommended framework for building production Polpo apps. Use Server Components for secure API calls and Client Components for real-time UI.
Install
npm install @polpo-ai/sdk @polpo-ai/react
Environment variables
# .env.local
POLPO_API_URL=https://api.polpo.sh # server-side only
POLPO_API_KEY=pk_live_... # server-side only
NEXT_PUBLIC_POLPO_API_URL=https://api.polpo.sh # client-side (public)
Keep POLPO_API_KEY server-side only (no NEXT_PUBLIC_ prefix). Use a route handler to proxy authenticated requests.
Server-side: Route Handler
Create an API route that proxies chat completions with the API key injected server-side:
// app/api/chat/route.ts
import { PolpoClient } from "@polpo-ai/sdk";
const client = new PolpoClient({
baseUrl: process.env.POLPO_API_URL!,
apiKey: process.env.POLPO_API_KEY!,
});
export async function POST(req: Request) {
const { messages, agent } = await req.json();
const stream = client.chatCompletionsStream({ agent, messages });
return new Response(
new ReadableStream({
async start(controller) {
for await (const chunk of stream) {
controller.enqueue(
new TextEncoder().encode(`data: ${JSON.stringify(chunk)}\n\n`)
);
}
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"));
controller.close();
},
}),
{
headers: {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
},
}
);
}
Server-side: Server Components
Fetch agent data directly in Server Components — no client-side API key exposure:
// app/agents/page.tsx
import { PolpoClient } from "@polpo-ai/sdk";
const client = new PolpoClient({
baseUrl: process.env.POLPO_API_URL!,
apiKey: process.env.POLPO_API_KEY!,
});
export default async function AgentsPage() {
const agents = await client.getAgents();
return (
<div>
<h1>Agents</h1>
{agents.map(a => (
<div key={a.name}>
<h2>{a.name}</h2>
<p>{a.role}</p>
</div>
))}
</div>
);
}
Client-side: Real-time with PolpoProvider
For live updates, wrap client components with PolpoProvider. Inject the API key from the server via a layout:
// app/dashboard/layout.tsx (Server Component)
import { DashboardShell } from "./shell";
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
// Pass API config from server environment
return (
<DashboardShell
apiUrl={process.env.NEXT_PUBLIC_POLPO_API_URL!}
>
{children}
</DashboardShell>
);
}
// app/dashboard/shell.tsx (Client Component)
"use client";
import { PolpoProvider } from "@polpo-ai/react";
export function DashboardShell({ apiUrl, children }: { apiUrl: string; children: React.ReactNode }) {
return (
<PolpoProvider baseUrl={apiUrl}>
{children}
</PolpoProvider>
);
}
// app/dashboard/page.tsx (Client Component)
"use client";
import { useTasks } from "@polpo-ai/react";
export default function DashboardPage() {
const { tasks, isLoading } = useTasks();
if (isLoading) return <p>Loading tasks...</p>;
return (
<ul>
{tasks.map(t => <li key={t.id}>{t.title} — {t.status}</li>)}
</ul>
);
}
Pattern: Secure chat proxy
The recommended pattern for production chat apps:
Browser (Client Component)
│
├── POST /api/chat (your Next.js route)
│ │
│ └── PolpoClient.chatCompletionsStream()
│ │ (POLPO_API_KEY injected server-side)
│ │
│ └── Polpo API
│
└── SSE /api/events (optional, for live task updates)
This keeps the API key server-side while streaming responses to the browser.