Добавление реального времени в приложения Next.js
Внедрение функций реального времени в приложение Next.js сложнее, чем в традиционном сервере Node.js. Рассмотрим рабочие подходы, которые можно использовать в продакшене.
Вариант 1: Managed WebSocket-сервисы (Pusher или Ably)
Для большинства приложений лучше всего подойдет использование управляемого сервиса WebSocket, такого как Pusher или Ably. Это решение легко интегрируется и не требует настройки собственного сервера.
Установка:
npm install pusher pusher-js
Пример использования:
Создаем экземпляр клиента и сервера Pusher:
import Pusher from 'pusher'
import PusherClient from 'pusher-js'
export const pusherServer = new Pusher({
appId: process.env.PUSHER_APP_ID,
key: process.env.NEXT_PUBLIC_PUSHER_KEY,
secret: process.env.PUSHER_SECRET,
cluster: process.env.PUSHER_CLUSTER,
useTLS: true,
})
export const pusherClient = new PusherClient(
process.env.NEXT_PUBLIC_PUSHER_KEY,
{ cluster: process.env.PUSHER_CLUSTER }
)
Отправка сообщения от сервера:
async function sendMessage(channelId: string, text: string) {
const message = await db.message.create({
data: { channelId, text, userId: session.user.id }
})
await pusherServer.trigger(
`channel-${channelId}`,
'new-message',
message
)
}
Подписка на канал в клиентском компоненте:
'use client'
function ChatRoom({ channelId }: { channelId: string }) {
const [messages, setMessages] = useState<Message[]>([])
useEffect(() => {
const channel = pusherClient.subscribe(`channel-${channelId}`)
channel.bind('new-message', (message: Message) => {
setMessages((prev) => [...prev, message])
})
return () => pusherClient.unsubscribe(`channel-${channelId}`)
}, [channelId])
return <MessageList messages={messages} />
}
Вариант 2: Серверные события (SSE)
Если требуется только односторонняя передача данных (например, уведомления), то можно воспользоваться встроенной поддержкой серверных событий (SSE). Этот подход проще и не требует установки дополнительных библиотек.
Пример реализации маршрута для отправки уведомлений:
// app/api/events/route.ts
export async function GET(request: Request) {
const session = await getServerSession()
if (!session) return new Response('Unauthorized', { status: 401 })
const stream = new ReadableStream({
start(controller) {
const encoder = new TextEncoder()
const send = (data: unknown) => {
controller.enqueue(
encoder.encode(`data: ${JSON.stringify(data)}
`)
)
}
}
})
}