TL;DR
Разбираем реализацию real-time чата на Next.js с использованием WebSockets через Apinator — hosted-решение для real-time коммуникации. Покроем настройку private/presence-каналов, server-side авторизацию и оптимистичные UI-обновления. Готовый пример поддерживает список онлайн-пользователей и мгновенную доставку сообщений.
Введение: зачем ещё один чат?
В 2024 году real-time функциональность стала must-have для многих приложений: от чатов до collaborative editing. Но нативные WebSockets требуют:
- Серверной инфраструктуры
- Механизмов реконнекта
- Масштабируемости при росте нагрузки
Apinator решает эти проблемы как hosted-альтернатива с Pusher-совместимым API. В этом гайде мы используем его с Next.js App Router для создания полноценного чата с:
- Presence-каналами (онлайн-статусы)
- Server-side авторизацией
- Оптимистичными обновлениями UI
Инициализация проекта
Создаём Next.js 14 проект с TypeScript и Tailwind:
npx create-next-app@latest realtime-chat --typescript --tailwind --app
Устанавливаем Apinator SDK:
npm install @apinator/client @apinator/server
Конфигурация в .env.local:
NEXT_PUBLIC_APINATOR_KEY=your_key
NEXT_PUBLIC_APINATOR_CLUSTER=eu
APINATOR_APP_ID=your_app_id
APINATOR_SECRET=your_secret
Server-side: авторизация каналов
Private/presence-каналы требуют server-side авторизации. Создаём API route:
// app/api/auth/channel/route.ts
import { Apinator } from "@apinator/server";
const apinator = new Apinator({
appId: process.env.APINATOR_APP_ID!,
secret: process.env.APINATOR_SECRET!,
cluster: process.env.NEXT_PUBLIC_APINATOR_CLUSTER! as "eu" | "us",
});
export async function POST(req: NextRequest) {
const { socket_id, channel_name } = await req.json();
const channelData = channel_name.startsWith("presence-")
? JSON.stringify({
user_id: socket_id,
user_info: { name: req.headers.get("x-username") ?? "Anonymous" }
})
: undefined;
const auth = apinator.authenticateChannel(socket_id, channel_name, channelData);
return NextResponse.json(auth);
}
Обработка сообщений
API route для broadcast сообщений:
// app/api/messages/route.ts
await apinator.trigger({
name: "new-message",
channel: "presence-chat-room",
data: JSON.stringify({
text: text.trim(),
username,
timestamp: Date.now()
}),
socketId // исключаем отправителя
});
Client-side: хук useChat
Реализуем кастомный хук для управления WebSocket-соединением:
// hooks/useChat.ts
const client = new Apinator({
key: process.env.NEXT_PUBLIC_APINATOR_KEY!,
cluster: process.env.NEXT_PUBLIC_APINATOR_CLUSTER! as "eu" | "us",
authEndpoint: "/api/auth/channel",
authHeaders: { "x-username": username }
});
const channel = client.subscribe("presence-chat-room") as PresenceChannel;
channel.bind("realtime:subscription_succeeded", () => {
setMembers(channel.getMembers() as OnlineMember[]);
});
UI-компонент чата
Интегрируем хук в страницу:
// app/chat/page.tsx
const { messages, members, sendMessage } = useChat(username);
<form onSubmit={(e) => {
e.preventDefault();
sendMessage(input);
setInput("");
}}>
<input
value={input}
onChange={(e) => setInput(e.target.value)}
/>
</form>
Оптимизации и прод
- Оптимистичные обновления:
setMessages(prev => [...prev, {
id: `${Date.now()}-self`,
text,
self: true
}]);
- Троттлинг событий:
const typingTimeout = useRef<NodeJS.Timeout>();
channel.bind("client-typing", ({ username }) => {
setTypingUser(username);
clearTimeout(typingTimeout.current);
typingTimeout.current = setTimeout(() => setTypingUser(null), 1000);
});
- Error boundaries для обработки разрывов соединения:
<ErrorBoundary fallback={<ReconnectButton />}>
<Chat />
</ErrorBoundary>
Альтернативы и когда что выбрать
| Решение | Плюсы | Минусы | Use case |
|---|---|---|---|
| Native WS | Полный контроль | Сложность масштабирования | Внутренние сервисы |
| Apinator | Быстрый старт | Vendor lock-in | Стартапы, MVP |
| Ably | Гибкость | Цена | Критичные системы |
| Socket.io | Fallback polling | Устаревший подход | Легаси проекты |
Заключение
Полученное решение даёт:
- Мгновенную доставку сообщений
- Список онлайн-пользователей
- Оптимистичный UI
- Готовую инфраструктуру
Для прод-использования добавьте:
- Rate limiting на API routes
- Мониторинг соединений
- Механизм retry для failed messages
Apinator особенно хорош для стартапов — вы получаете production-ready real-time функциональность без необходимости настраивать собственные WebSocket-серверы.
Источник: https://dev.to/itsshpetim/build-a-real-time-chat-app-with-nextjs-5794