DEV Community

Shivam
Shivam

Posted on

Building Real-Time Group Chat with Redis Streams and SSE in Next.js

One of the features we’re most proud of at Fledgr is Circles — real-time campus group chats designed to feel instant without relying on traditional WebSockets.

Getting there was a lot less straightforward than we expected.


Why We Didn’t Use WebSockets

At first, WebSockets seemed like the obvious choice.

The problem is deployment constraints.

Since Fledgr runs on Vercel, persistent WebSocket connections inside serverless functions aren’t really an option. You either introduce a separate socket infrastructure or work within the platform’s limits.

We chose the second route.

Instead of WebSockets, we built the real-time layer using Server-Sent Events (SSE).

SSE is essentially a long-lived HTTP response where the server continuously streams updates while the client listens using the browser’s native EventSource API.

It’s unidirectional, which works perfectly for chat message delivery:

  • Messages are streamed from server → client
  • Sending messages still happens through normal POST requests

Simple architecture. Fewer moving parts.

At least in theory.


The 60-Second Problem on Vercel

The first issue showed up immediately.

Vercel serverless functions time out after roughly 60 seconds.

That means your SSE stream gets terminated every single minute.

For a real-time chat system, that’s obviously a problem.

The mistake would’ve been trying to “fight” the platform.

Instead, we designed around the limitation.


Designing Around Forced Disconnects

Rather than waiting for Vercel to kill the connection unexpectedly, we close the SSE stream ourselves after ~55 seconds.

The browser automatically reconnects using EventSource, usually within a second or two.

On reconnect, the client sends the last received message ID, and the server resumes streaming from that point forward.

The result:

  • No visible interruptions
  • No dropped messages
  • No manual reconnect logic on the client
  • Reconnection feels invisible to users

Once we stopped treating reconnects as failures and started treating them as part of the architecture, the system became much simpler.


Why We Chose Redis Streams Instead of Pub/Sub

The backend streaming layer uses Redis Streams with XREAD.

We specifically avoided Redis Pub/Sub for one reason:

Reconnection safety.

Pub/Sub is fire-and-forget.

If a client disconnects for even a moment, any messages sent during that gap are permanently lost.

That doesn’t work well with SSE reconnect behavior.

Redis Streams solve this cleanly because clients can reconnect using the last processed message ID and continue reading from where they left off.

```txt id="pw1jlwm"
XREAD STREAMS circle:messages




That tiny detail ended up being one of the most important architectural decisions in the system.

---

## Stream Persistence Strategy

We don’t use Redis as permanent message storage.

Messages are still written to the main database.

Redis Streams only exist as the real-time transport layer.

Because of that, we aggressively cap stream lengths and let Redis automatically trim older entries.



```txt id="rk5q3sj"
MAXLEN ~ 1000
Enter fullscreen mode Exit fullscreen mode

This keeps memory usage predictable while still giving reconnecting clients enough history to catch up safely.


What We’d Probably Do Differently

If we weren’t on Vercel, we’d probably use WebSockets.

The SSE + reconnect pattern works surprisingly well, but it introduces complexity that native socket infrastructure simply avoids.

A lot of architectural decisions end up being shaped around the 60-second execution ceiling:

  • Reconnect handling
  • Message replay
  • Stream resumption
  • Client synchronization

Those aren’t impossible problems, but they’re problems you wouldn’t normally choose to have.


Final Thoughts

That said, this setup has been extremely solid for us in production at Fledgr.

Circles now supports real-time campus conversations without running a dedicated socket server or maintaining separate infrastructure outside Vercel.

If you’re building on Vercel and need real-time features without introducing WebSocket infrastructure, this pattern is absolutely viable.

It’s not the most obvious solution — but under real-world constraints, it ended up being one of the most reliable ones we found.

Top comments (0)