Oct 23, 20266 min read

Best TypeScript Framework for Real-Time APIs in 2026

Socket.IO, ws, Fastify, Hono and NestJS compared for WebSocket APIs in TypeScript, plus how Encore adds typed streaming endpoints and the databases and topics a real-time backend needs.

Real-time features in TypeScript usually run over WebSockets, the protocol standardized in RFC 6455, and the framework choice decides how connections are typed, grouped and scaled past one server. A single process can hold many connections, and the work grows with the second instance, when a message received on one server has to reach clients connected to another, and when messages have to be stored for clients that reconnect.

What to weigh when choosing:

  • Whether messages are typed and validated, or plain strings you parse.
  • Whether rooms, acknowledgements and reconnection are built in.
  • How messages fan out across several instances.
  • Where history is stored and how the broker runs locally and in production.

TypeScript real-time options at a glance

OptionApproachBest fit
Socket.IOEvent-based protocol with rooms and reconnectionChat, presence and collaborative features
wsMinimal WebSocket server and clientTeams that want a small, fast base
FastifyHTTP framework with a WebSocket pluginFastify apps that add real-time routes
HonoMulti-runtime framework with a WebSocket helperApps on Bun, Deno, Cloudflare Workers or Node
NestJSDecorator-based gatewaysTeams already on NestJS
Compare features across all five options
CapabilitySocket.IOwsFastifyHonoNestJS
Typed messagesTyped events with genericsUntypedUntypedUntypedDTOs with pipes
Rooms and broadcastBuilt inYou build themYou build themYou build themThrough Socket.IO adapter
Fan-out across instancesAdapters such as RedisYou build itYou build itYou build itThrough adapters
Local infrastructureYou run itYou run itYou run itYou run itYou run it
Production infrastructureYou provision itYou provision itYou provision itYou provision itYou provision it

Socket.IO: rooms and reconnection

Socket.IO

Socket.IO adds an event protocol on top of WebSockets with rooms, acknowledgements, automatic reconnection and an HTTP long-polling fallback.

import { Server } from "socket.io"; const io = new Server(3000); io.on("connection", (socket) => { socket.on("join", (room: string) => socket.join(room)); socket.on("message", (room: string, text: string) => { io.to(room).emit("message", text); }); });

Key features

  • Rooms and namespaces for grouping connections.
  • Adapters, such as the Redis adapter, for broadcasting across instances.
  • Typed events through TypeScript generics.

Good to know

Clients must use a Socket.IO client, since the protocol is not plain WebSocket, and the adapter's broker is another service to run. With several nodes, the Socket.IO docs on using multiple nodes list sticky sessions, disabling HTTP long-polling or client-side load balancing as the ways to keep sessions working, and load balancers such as AWS's Application Load Balancer support sticky sessions for this.

Try Socket.IO →

ws: the minimal base

ws

ws is a fast, small WebSocket implementation for Node.js that many frameworks and plugins build on.

Key features

  • Plain WebSocket protocol, compatible with any client.
  • Small dependency footprint.
  • Works with an existing HTTP server through the upgrade event.

Good to know

Routing, typing, heartbeats and broadcast are left to you.

Try ws →

Fastify: WebSocket routes in an HTTP app

Fastify

The @fastify/websocket plugin, built on ws, lets a Fastify route accept WebSocket connections with the same hooks as HTTP routes.

Key features

  • WebSocket handlers declared as routes.
  • Fastify hooks for auth before the upgrade.
  • Shares the plugin ecosystem of the HTTP app.

Good to know

Messages are untyped by default, and fan-out across instances is yours to build.

Try Fastify →

Hono: WebSockets across runtimes

Hono

Hono's WebSocket helper exposes one API across Bun, Deno, Cloudflare Workers and Node.

Key features

  • upgradeWebSocket with onMessage, onOpen and onClose events.
  • The same app runs on several runtimes.
  • Typed RPC client for the HTTP routes.

Good to know

Each runtime has its own adapter and limits, so check the one you deploy to.

Try Hono →

NestJS: gateways

NestJS

NestJS gateways use decorators such as @SubscribeMessage and support Socket.IO or ws as the underlying platform.

Key features

  • Decorators for message handlers.
  • Guards, pipes and interceptors shared with HTTP controllers.
  • Socket.IO adapters for scaling.

Good to know

It brings the full NestJS module system, which is a lot of structure if real-time is the only thing you need.

Try NestJS →

Encore: typed streams and the infrastructure behind them

Encore

Encore combines typed streaming endpoints with the infrastructure behind them through an infrastructure SDK for TypeScript, whose API layer includes streaming endpoints that run over WebSockets with typed messages in each direction. Whichever option above you pick, Encore can add the database and topics to the same app. The same service declares the Postgres database that stores history and a Pub/Sub topic for events other services react to, encore run starts Postgres locally with the topic, and a deploy provisions both in your own AWS or GCP account.

import { api, StreamInOut } from "encore.dev/api"; import { Subscription, Topic } from "encore.dev/pubsub"; import { SQLDatabase } from "encore.dev/storage/sqldb"; const db = new SQLDatabase("chat", { migrations: "./migrations" }); interface ChatMessage { room: string; user: string; text: string; } // Carries posted messages to subscribers. const posted = new Topic<ChatMessage>("chat-posted", { deliveryGuarantee: "at-least-once", }); // Streams connected to this instance. const connected = new Set<StreamInOut<ChatMessage, ChatMessage>>(); export const chat = api.streamInOut<ChatMessage, ChatMessage>( { path: "/chat", expose: true }, async (stream) => { connected.add(stream); try { for await (const msg of stream) { await db.exec` INSERT INTO messages (room, sender, body) VALUES (${msg.room}, ${msg.user}, ${msg.text})`; await posted.publish(msg); } } finally { connected.delete(stream); } }, ); const _ = new Subscription(posted, "deliver", { handler: async (msg) => { for (const stream of connected) { try { await stream.send(msg); } catch { connected.delete(stream); } } }, });
Messages are typed in both directions, stored in Postgres and published to a topic. This is the single-instance shape.

Key features

  • Incoming messages are checked against the TypeScript types before the handler sees them.
  • History lives in a database declared next to the endpoint, so reconnecting clients can fetch what they missed.
  • Tracing records the stream and the queries it runs.
  • A coding agent adding typing indicators can declare a second stream type and test it against the local database before the change is pushed.

Good to know

Pub/Sub subscriptions deliver each message to one instance of the subscribing service, so delivering to every instance takes a broadcast mechanism of its own, such as a cache channel or a per-instance subscription, and the snippet above shows the single-instance shape. Rooms and presence are yours to build. When Encore provisions the database and topic, the cloud access sits with Encore's IAM role, not the developer or agent.

Try Encore →

How to choose

  • Socket.IO for rooms, acknowledgements and reconnection out of the box.
  • ws for the smallest plain WebSocket base.
  • Fastify or Hono to add real-time routes to an existing HTTP app.
  • NestJS when the rest of the backend is NestJS.

If the backend already uses Encore's API layer, its streaming endpoints (api.streamInOut) cover the WebSocket side and the same service declares the database and topics; otherwise choose among the libraries above.

Frequently asked questions

What is the best TypeScript framework for real-time APIs?

Socket.IO is the most complete option, with rooms, reconnection and fallbacks, while ws is the minimal, fast WebSocket library many others build on. Fastify and Hono add WebSocket routes to an HTTP framework, and NestJS offers gateways with decorators. Separately, Encore is an infrastructure SDK whose API layer includes typed streaming endpoints, and it declares the database and Pub/Sub topics a real-time backend uses in your own cloud.

Should I use Socket.IO or plain WebSockets?

Use Socket.IO when you want rooms, acknowledgements and automatic reconnection without writing them. Use plain WebSockets through ws or your framework when clients are not Socket.IO clients, or you want the smallest protocol surface.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders