Skip to content

Runtime and services

Realtime and WebSockets

Connections held open so a page updates without polling; Socket.IO carries the messaging layer in Starterflare.

Realtime here means a connection that stays open so the server can speak first. In practice that is WebSockets, and Socket.IO is a library sitting on top of them that adds rooms, automatic reconnection, acknowledgements and a fallback to long polling for networks that will not pass a socket through. It is not raw WebSocket: both ends have to speak Socket.IO, which is the trade you make for the reconnection logic you would otherwise write yourself.

How we use it

Starterflare is the build that uses it. Its messaging module handles one to one conversations over Socket.IO, served through the NestJS socket adapter with the browser client in the Next.js front end. Conversations, members and messages are ordinary database rows; the socket layer only carries delivery and presence, which is the split we would keep in any design.

One caveat we would rather state than have found. That codebase has not been released, and the site currently on the same domain does not match it, so this is an implementation we can walk you through rather than something with a production load history behind it. The push features we have actually run at any scale are in atHeartbeat, and they use Durable Objects with no Socket.IO involved, which is the comparison worth having before choosing.

What it costs you

A connection is state, and state is what makes scaling awkward. As soon as there are two backend instances, a message published on one does not reach a subscriber connected to the other, so you need a Redis adapter or a broker to fan out between them, and sticky sessions at the load balancer if you allow the polling fallback. That is two pieces of infrastructure added to support one feature.

The billing shape changes too. You are paying for connection time and per-connection memory rather than requests, so an open tab nobody is looking at costs the same as an active user. We took a timeline apart on this basis in which parts of a social timeline actually cost money, and held-open connections were one of the two genuinely expensive items.

Deployments are noisy. Restart the process and every client reconnects at once, so the first seconds after a release are the heaviest traffic of the day unless reconnection is jittered. Authentication has to happen on the handshake and be rechecked, because a socket authorised an hour ago is still open after the user's session has been revoked. And delivery guarantees are yours: Socket.IO reconnects, it does not replay. Anything that must not be lost is written to the database first and pushed second.

When we would choose something else

On Cloudflare we would reach for Durable Objects instead, because the coordination point and the socket handler are then the same object and there is no adapter to run. Where the requirement is really "the page should not be stale", polling on an interval with the caching layer described on the client state page is cheaper, simpler, survives a deployment without noticing, and is what we would suggest for notification counts and feeds. The honest default is that most products asking for realtime need a refresh button and a sensible cache, and the ones that genuinely need a socket know exactly why.

Where we have used it

Every build below lists this in its stack, so the claim is checkable.