Tutorial
Vercel AI SDK 4.0: Bikin Chatbot Streaming di Next.js Cuma 30 Baris Kode
Panduan praktis membangun AI chatbot streaming di Next.js App Router menggunakan Vercel AI SDK 4.0, hook useChat, dan OpenAI/Anthropic provider.
Dulu, bikin fitur chatbot streaming ala ChatGPT itu nightmare. Kamu harus set up Server-Sent Events (SSE) atau WebSocket manual, ngurusin chunk parsing dari OpenAI API, kelola array messages di React state supaya nggak re-render secara brutal, dan nanganin edge cases seperti koneksi terputus di tengah jalan. Kalau kodenya nggak rapi, React state kamu langsung berantakan dan performa aplikasi meluncur turun.
Di Vercel AI SDK 4.0, semua kompleksitas itu dibabat habis. Vercel menyajikan abstraksi yang matang tanpa mengorbankan fleksibilitas. Dengan kombinasi App Router Next.js dan AI SDK 4.0, kamu cuma butuh kurang dari 30 baris kode untuk membangun UI chatbot production-ready yang mendukung real-time streaming, multi-provider (OpenAI, Anthropic, Google Gemini), serta error handling yang tangguh.
Artikel ini akan membedah arsitektur Vercel AI SDK 4.0 dan membimbing kamu step-by-step membuat chatbot streaming berkinerja tinggi.
Arsitektur Vercel AI SDK 4.0 & Kenapa Harus Upgrade
Vercel AI SDK 4.0 bukan sekadar update minor. Ada pergeseran paradigma utama dibanding versi 2.x atau 3.x awal. Dulu SDK ini sangat terikat erat dengan format OpenAI dan Next.js Server Actions. Sekarang, SDK dipecah menjadi dua layer independen:
- AI SDK Core (`ai`): Menyediakan API universal untuk memanggil LLM (Large Language Models), generate text, generate object (structured data), dan stream response. Layer ini murni JavaScript runtime dan bisa berjalan di Node.js, Edge Runtime, Cloudflare Workers, atau Bun.
- AI SDK UI (`ai/react`): Framework-agnostic hooks (seperti
useChatdanuseCompletion) untuk mengelola state UI, input form, optimistic updates, dan konsumsi stream data dari backend secara otomatis.
Keunggulan utama Vercel AI SDK 4.0 terletak pada Unified Provider Interface. Kode backend kamu tidak lagi bergantung pada SDK spesifik milik vendor. Ganti model dari gpt-4o ke claude-3-5-sonnet cukup dengan mengubah satu baris impor provider, tanpa menyentuh logika streaming atau UI.
Setup Project & Dependensi
Mari kita mulai dari nol. Buat proyek Next.js baru dengan App Router dan TypeScript, lalu install package yang dibutuhkan.
npx create-next-app@latest my-ai-chatbot --typescript --tailwind --eslint --app
cd my-ai-chatbot
# Install Vercel AI SDK versi 4.0+ dan Provider Adapter
npm install ai @ai-sdk/openai @ai-sdk/anthropicJangan lupa untuk menyiapkan API Key kamu di environment variable .env.local:
OPENAI_API_KEY=sk-proj-your-openai-api-key-here
ANTHROPIC_API_KEY=sk-ant-your-anthropic-api-key-here@ai-sdk/openai dan @ai-sdk/anthropic secara otomatis membaca process.env.OPENAI_API_KEY dan process.env.ANTHROPIC_API_KEY dari server environment. Kunci API kamu aman di sisi server dan tidak akan pernah bocor ke client-side bundle.
Menjalankan Backend Route Handler
Langkah berikutnya adalah membuat Route Handler di Next.js App Router. Buat file baru di app/api/chat/route.ts.
Di file ini, kita mengimpor fungsi streamText dari package ai dan instans model dari @ai-sdk/openai.
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
// Mengatur runtime ke edge untuk kineja latency terendah (opsional)
export const runtime = 'edge';
export async function POST(req: Request) {
try {
const { messages } = await req.json();
// Memanggil LLM dan membuat response stream
const result = streamText({
model: openai('gpt-4o-mini'),
system: 'Kamu adalah asisten teknis senior yang ahli, ramah, dan selalu memberikan jawaban yang lugas serta praktis.',
messages,
});
// Mengembalikan Data Stream Response yang dipahami oleh UI SDK
return result.toDataStreamResponse();
} catch (error) {
console.error('Error on POST /api/chat:', error);
return new Response(
JSON.stringify({ error: 'Terjadi kesalahan saat memproses permintaan AI.' }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
}Detail Implementasi Backend:
- `streamText`: Fungsi utama dari AI SDK Core untuk menginisiasi koneksi streaming ke provider LLM.
- `model: openai('gpt-4o-mini')`: Menggunakan model cost-effective milik OpenAI. Jika ingin ganti ke GPT-4o penuh, cukup ganti string menjadi
'gpt-4o'. - `system`: Prompt instruksi awal untuk mengatur persona dan batasan perilaku model AI.
- `result.toDataStreamResponse()`: Fungsi ajaib dari Vercel AI SDK 4.0. Fungsi ini mengonversi stream dari LLM ke format standar SSE (Server-Sent Events) khusus Vercel AI Data Stream Protocol, yang secara otomatis dipahami oleh hook
useChatdi frontend.
Menjalankan Client UI dengan useChat Hook
Sekarang masuk ke bagian ajaib di frontend. Kita buat komponen chat interface lengkap di app/page.tsx.
Hook useChat dari ai/react akan menangani seluruh state pesan (messages), nilai input form (input), status loading (isLoading), submission handler (handleSubmit), serta auto-appending chunk text yang masuk secara real-time.
'use client';
import { useChat } from 'ai/react';
export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error, reload } = useChat({
api: '/api/chat',
});
return (
<main className="flex flex-col max-w-2xl mx-auto h-screen p-4">
<header className="py-4 border-b">
<h1 className="text-xl font-bold">Vour Chatbot Assistant</h1>
<p className="text-sm text-gray-500">Powered by Vercel AI SDK 4.0 & Next.js</p>
</header>
{/* Container Chat Messages */}
<div className="flex-1 overflow-y-auto py-4 space-y-4">
{messages.length === 0 && (
<p className="text-center text-gray-400 mt-8">Belum ada percakapan. Ketik sesuatu untuk memulai!</p>
)}
{messages.map((m) => (
<div
key={m.id}
className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] rounded-lg p-3 ${
m.role === 'user'
? 'bg-blue-600 text-white'
: 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100'
}`}
>
<span className="font-semibold text-xs block mb-1">
{m.role === 'user' ? 'You' : 'AI Assistant'}
</span>
<p className="whitespace-pre-wrap text-sm">{m.content}</p>
</div>
</div>
))}
{/* Loading Indicator */}
{isLoading && (
<div className="flex justify-start">
<div className="bg-gray-100 dark:bg-gray-800 rounded-lg p-3 animate-pulse text-sm text-gray-500">
Sedang mengetik...
</div>
</div>
)}
{/* Error Handler State */}
{error && (
<div className="p-3 bg-red-50 text-red-600 rounded-lg text-sm flex justify-between items-center">
<span>Terjadi kesalahan saat memuat respon.</span>
<button
onClick={() => reload()}
className="underline font-medium hover:text-red-800 ml-2"
>
Coba lagi
</button>
</div>
)}
</div>
{/* Form Input Message */}
<form onSubmit={handleSubmit} className="flex gap-2 pt-2 border-t">
<input
value={input}
onChange={handleInputChange}
placeholder="Tanyakan sesuatu tentang Next.js..."
className="flex-1 border rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-900 dark:border-gray-700"
disabled={isLoading}
/>
<button
type="submit"
disabled={isLoading || !input.trim()}
className="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium disabled:opacity-50 hover:bg-blue-700 transition"
>
Kirim
</button>
</form>
</main>
);
}Detail Implementasi Frontend:
- `useChat()`: Hook ini otomatis menargetkan
/api/chatsecara default (bisa kita passing opsi{ api: '/api/custom-route' }). Hook mengelola arraymessagesyang selalu di-update begitu stream chunk baru tiba dari backend. - `handleInputChange` & `handleSubmit`: Mengikat input HTML secara deklaratif. Saat form di-submit,
handleSubmitotomatis mengosongkan input, menambahkan pesan user ke state secara optimistic, lalu mengirim seluruh riwayat percakapan ke backend viaPOST. - `reload()`: Memberikan kemampuan untuk retry jika ada error koneksi saat streaming.
Total kode pada file React di atas hanya berkisar 60-70 baris dengan styling Tailwind Tailwind CSS lengkap. Jika tanpa styling, komponen dasar murni di bawah 30 baris kode!
Handling Edge Cases: Multi-Provider & Custom Fallback Error
Aplikasi production-ready tidak boleh hancur begitu provider utama seperti OpenAI sedang mengalami kecemasan kapasitas (rate limit atau outage). Salah satu kekuatan terbesar Vercel AI SDK 4.0 adalah kemudahan dalam mengimplementasikan fallback mechanism atau dynamic provider switching.
Mari kita tingkatkan Route Handler app/api/chat/route.ts agar secara otomatis beralih (fallback) ke Anthropic Claude 3.5 Sonnet jika OpenAI mengalami kegagalan.
import { openai } from '@ai-sdk/openai';
import { anthropic } from '@ai-sdk/anthropic';
import { streamText } from 'ai';
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
// Validasi payload pesan sederhana
if (!messages || !Array.isArray(messages)) {
return new Response(JSON.stringify({ error: 'Payload messages tidak valid.' }), {
status: 400,
headers: { 'Content-Type': 'application/json' },
});
}
try {
// Upaya 1: Panggil OpenAI API (Primary Provider)
const result = streamText({
model: openai('gpt-4o-mini'),
system: 'Kamu adalah AI engineer berpengalaman yang menjawab secara singkat dan tepat.',
messages,
maxTokens: 1000,
});
return result.toDataStreamResponse();
} catch (primaryError) {
console.warn('OpenAI gagal atau Rate Limited, beralih ke Anthropic Claude...', primaryError);
try {
// Upaya 2: Fallback ke Anthropic API (Secondary Provider)
const fallbackResult = streamText({
model: anthropic('claude-3-5-sonnet-20240620'),
system: 'Kamu adalah AI engineer berpengalaman yang menjawab secara singkat dan tepat.',
messages,
maxTokens: 1000,
});
return fallbackResult.toDataStreamResponse();
} catch (fallbackError) {
console.error('Kedua provider (OpenAI & Anthropic) gagal dipanggil:', fallbackError);
return new Response(
JSON.stringify({ error: 'Layanan AI sedang tidak dapat diakses saat ini. Silakan coba lagi nanti.' }),
{ status: 503, headers: { 'Content-Type': 'application/json' } }
);
}
}
}Tips Senior Dev: Selalu atur parameter maxTokens untuk mencegah runaway request dari pengguna malisius yang memicu generasi teks sangat panjang sehingga membengkakkan tagihan API kamu.Tips & Best Practices Production-Ready
Sebelum kamu men-deploy aplikasi ini ke Vercel atau infrastructure pilihanmu, perhatikan beberapa aturan main penting ini:
1. Jangan Pernah Panggil API Key dari Client-Side
Jangan pernah tergiur membuat tombol atau halaman setting di client tempat pengguna/dev memasukkan apiKey langsung ke dalam instance createOpenAI({ apiKey }) di komponen browser ('use client'). Semua interaksi AI wajib melalui Route Handler server-side untuk menyembunyikan credential.
2. Manfaatkan System Prompts dan Message Truncation
Semakin panjang riwayat percakapan (messages), semakin besar context window yang dikirimkan dan semakin mahal biaya per-request. Untuk aplikasi produksi dengan percakapan panjang, potong array pesan di backend sebelum dikirim ke SDK:
// Ambil hanya 10 pesan terakhir untuk menghemat token cost
const recentMessages = messages.slice(-10);3. Tangani Rate Limiting dengan Upstash Redis
Jika aplikasi kamu publik, pasang rate limiter berbasis IP address di Route Handler menggunakan @upstash/ratelimit dan @upstash/redis. Tanpa ini, bot atau scraper dapat menguras limit API kamu dalam hitungan detik.
4. Edge Runtime vs Serverless Runtime
- Gunakan
export const runtime = 'edge'jika target kamu adalah latency secepat mungkin dan respon pendek. - Gunakan Node.js Serverless runtime bawaan jika kamu butuh membaca file lokal, menggunakan SDK native Node.js yang kompleks, atau butuh timeout request yang lebih longgar (>30 detik).
Kesimpulan & Action Items
Vercel AI SDK 4.0 mendemokratisasi pembangunan fitur berbasis AI. Hal-hal kompleks seperti SSE protocol handling, React stream state management, dan multi-provider switching kini diselesaikan hanya dengan beberapa baris kode yang elegan.
Action Items untuk Kamu Sekarang:
- Clone atau jalankan
create-next-applalu installaidan@ai-sdk/openai. - Salin kode Route Handler dan Client Component dari artikel ini.
- Tambahkan
OPENAI_API_KEYdi.env.localdan jalankannpm run dev. - Eksperimen dengan mengganti model ke
anthropic('claude-3-5-sonnet-20240620')atau coba fiturgenerateObjectdari AI SDK Core untuk menghasilkan keluaran berformat JSON terstruktur!
Butuh penyesuaian khusus untuk project Anda?
Jika situasi operasional atau arsitektur sistem bisnis Anda membutuhkan solusi kustom, diskusikan langsung bersama tim engineer kami. Anda juga bisa melihat rincian layanan vour.dev atau menghitung estimasi biaya project lebih dulu.
Mulai Project