Xin chào các bạn! Mình là Minh, tác giả blog kỹ thuật tại HolySheep AI. Tuần vừa rồi mình ngồi cả đêm để tích hợp Server-Sent Events (SSE) vào một dự án Next.js 14 nhằm gọi Claude Opus 4.7 với phản hồi streaming thời gian thực — và mình muốn chia sẻ lại toàn bộ quá trình, kể cả những lỗi ngớ ngẩn nhất, để các bạn mới học đỡ mất thời gian vật lộn như mình. Bài viết này hướng đến người chưa từng động vào API lần nào, nên mình sẽ giải thích từng khái niệm, từng bước một.
SSE là gì và tại sao phải dùng?
Hãy tưởng tượng khi bạn xem phim trên Netflix, đoạn video không cần tải xong cả bộ phim rồi mới phát, mà phát từng phần ngay khi dữ liệu về tới. SSE (Server-Sent Events) cũng hoạt động y như vậy với văn bản: máy chủ gửi từng mẩu nhỏ về trình duyệt, người dùng thấy câu trả lời hiện ra từ từ giống ChatGPT.
So với WebSocket (vốn cho phép gửi hai chiều), SSE đơn giản hơn nhiều vì chỉ cần máy chủ "đẩy" dữ liệu về trình duyệt. Với một chatbot, điều đó là quá đủ.
Bước 1: Tạo dự án Next.js 14
Mở Terminal (Command Prompt trên Windows) và gõ:
npx create-next-app@14 claude-sse-demo
cd claude-sse-demo
npm install
Lệnh trên sẽ tạo thư mục claude-sse-demo chứa toàn bộ khung dự án. Mở thư mục đó bằng VS Code là xong.
Gợi ý ảnh chụp màn hình: chụp lại cửa sổ VS Code với cây thư mục bên trái để bạn dễ hình dung cấu trúc.
Bước 2: Đăng ký HolySheep AI và lấy API Key
HolySheep AI là nền tảng cung cấp truy cập các mô hình AI hàng đầu với mức giá cực kỳ cạnh tranh. Điểm mình thích nhất: tỷ giá ¥1 = $1 (tiết kiệm hơn 85% so với thanh toán USD qua các nền tảng khác), hỗ trợ thanh toán WeChat và Alipay, độ trễ trung bình dưới 50ms, và bạn được tín dụng miễn phí khi đăng ký tại đây.
Sau khi đăng ký, bạn vào menu "API Keys", bấm "Create new key", rồi sao chép chuỗi dạng sk-xxxx.... Lưu ý quan trọng: chuỗi này giống như mật khẩu, tuyệt đối không chia sẻ công khai trên GitHub hay mạng xã hội.
Bước 3: Tạo API Route xử lý SSE
Trong VS Code, tạo file app/api/chat/route.ts rồi dán đoạn code bên dưới. Đừng hoảng nếu thấy dài — mình sẽ giải thích ngay sau đây.
import { NextRequest } from 'next/server';
export const runtime = 'nodejs';
export const maxDuration = 60;
export async function POST(req: NextRequest) {
const { messages } = await req.json();
const upstream = await fetch('https://api.holysheep.ai/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': Bearer ${process.env.HOLYSHEEP_API_KEY},
},
body: JSON.stringify({
model: 'claude-opus-4.7',
messages,
stream: true,
temperature: 0.7,
}),
});
if (!upstream.ok || !upstream.body) {
const err = await upstream.text();
return new Response(Lỗi upstream: ${err}, { status: 500 });
}
const reader = upstream.body.getReader();
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const stream = new ReadableStream({
async start(controller) {
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) {
controller.close();
break;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed.startsWith('data:')) continue;
const payload = trimmed.slice(5).trim();
if (payload === '[DONE]') {
controller.close();
return;
}
try {
const json = JSON.parse(payload);
const content = json.choices?.[0]?.delta?.content || '';
if (content) {
controller.enqueue(
encoder.encode(data: ${JSON.stringify({ content })}\n\n)
);
}
} catch (_) {
// bỏ qua dòng lỗi để luồng không bị đứt
}
}
}
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream; charset=utf-8',
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
},
});
}
Giải thích ngắn gọn từng phần để bạn hiểu bản chất:
export const runtime = 'nodejs': yêu cầu Next.js chạy ở môi trường Node.js thay vì Edge, để có thể dùng ReadableStream ổn định.stream: true: bảo API trả về từng phần, không đợi sinh xong toàn bộ câu trả lời.- Vòng lặp
while (true): đọc từng đoạn dữ liệu, tách theo dòng, đẩy nội dung về trình duyệt. [DONE]: tín hiệu từ máy chủ báo đã gửi xong.
Bước 4: Giao diện chat phía client
Mở file app/page.tsx, xóa hết nội dung mặc đ