Why I'm Building bxios: REST Over WebSocket

Why high-frequency HTTP gets chatty and wasteful, and how bxios bridges axios-style REST APIs with binary MessagePack over a single WebSocket connection.

If you build real-time dashboards, microservice nodes, or high-frequency telemetry tools, you can run into a familiar friction point: many small HTTP requests may carry more protocol work than the payload itself.

Every client request carries protocol work. Connection reuse, HTTP version, header compression, TLS, payload size, and server behavior all affect the cost. For an app making many small calls, that overhead can become noticeable, but it is a workload-dependent tradeoff rather than a fixed property of HTTP.

The typical alternative is switching to raw WebSockets. But raw WebSockets give you a plain message pipe. You lose HTTP verbs (GET, POST, PUT, DELETE), URL routing, status codes, and request-response correlation. Suddenly you are writing custom JSON wrappers with manually tracked message IDs just to figure out which server response belongs to which client request.

That is why I am building bxios: an open-source JavaScript and TypeScript library designed to run REST requests over a single long-lived WebSocket connection using binary MessagePack.


The Case Against Chatty HTTP for Real-Time Apps

Traditional REST over HTTP works great for standard CRUD applications. If a user clicks a button once every few seconds to save a profile or fetch a page, opening an HTTP connection and sending JSON headers is completely fine.

Problems start when your access pattern changes. Consider these common setups:

  1. Real-time monitoring dashboards requesting telemetry updates, status updates, or metric logs every few hundred milliseconds.
  2. Browser-to-node streaming components where small control commands and query frames are pushed back and forth constantly.
  3. Microservice meshes and internal tools that execute frequent request-reply calls between services.

With standard fetch or axios calls over HTTP/1.1 or HTTP/2, each request still carries request metadata such as authorization, cookies, or accepted formats. Connection reuse and header compression can reduce the repeated bytes, so the actual overhead depends on the client, protocol version, and request shape.

HTTP also brings connection-pool behavior, stream limits, retries, and failure handling that the client has to manage.

WebSockets can reduce repeated connection setup and request framing by establishing a long-lived socket during the initial upgrade. Once open, data flows in both directions, but the total cost still depends on the frame format, headers, compression, and connection behavior.

However, raw WebSockets lack semantics. A WebSocket frame is just binary or text data. There is no built-in concept of a URL path, a method, a 200 OK vs a 404 Not Found response, or an async callback waiting for a specific payload.


What is bxios?

The goal of SujalChoudhari/bxios is to combine the ergonomic API of axios with the transport efficiency of WebSockets and binary serialization.

The repository is structured into two main parts:

1. bxios (The Client)

bxios was intended to provide an axios-style REST client for JavaScript and TypeScript. The August 3 snapshot was still developing that surface; its goal was to route requests through a single persistent WebSocket connection instead of making individual HTTP calls via fetch or XMLHttpRequest.

The intended developer-facing API was designed to resemble standard axios:

import { bxios } from 'bxios';

// Illustrative target API shape; the full axios-compatible method suite was still work in progress.
const response = await bxios.get('/api/v1/metrics/cpu', {
  params: { detailed: true }
});

console.log(response.data);

Under the planned design, bxios would serialize the request path, parameters, headers, and body into a binary MessagePack frame and transmit it over the established WebSocket pipe. When a server responded, the client was intended to correlate that frame with the originating request and resolve its Promise.

2. bxios-server (The Server Framework)

To process REST requests sent over a WebSocket tunnel, the server side needs a handler that understands how to parse frame headers and route requests to controller endpoints.

bxios-server was planned as a FastAPI-style or Express-style framework for WebSocket-tunneled REST routing. The August 3 snapshot had the transport-agnostic IDriver abstraction from PR #15, while concrete server-driver implementations and the framework surface were still being developed.

import { BxiosServer } from 'bxios-server';

// Illustrative target API; concrete server drivers were still being developed.
const app = new BxiosServer();

app.get('/api/v1/metrics/cpu', (req, res) => {
  res.json({ usage: 42.5, coreCount: 8 });
});

app.listen(8080);

Wire Format and Internals

The design and active work around REST over WebSocket centered on three internal mechanisms:

1. Binary MessagePack Frame Encoding

JSON is readable, but parsing text strings and escaping characters on every frame wastes CPU cycles and bandwidth. bxios uses MessagePack frame encoding for the request and response framing described in this snapshot. MessagePack packs integers, strings, arrays, and objects into compact binary formats, but the actual size and speed tradeoffs depend on the payload and implementation.

2. Planned PendingMap Correlation

WebSocket messages are inherently asynchronous and non-blocking. When a client sends frame A and then frame B over the same socket, the server might finish processing frame B before frame A and send the response for frame B first.

Without a correlation layer, the client would have no way to match incoming response frames with the original async promises.

bxios was designed to use a lightweight correlation engine centered around an internal structure called PendingMap. The planned flow assigned each outgoing request frame a unique frame ID, registered the Promise handlers alongside that ID, and set an optional timeout.

In the planned flow, when a binary frame arrived from the server, bxios would read the frame header, use the correlation ID to look up the matching handlers, and resolve the Promise with the decoded body and status code.

3. Planned Isomorphic Connection Manager

Network connections drop, servers restart, and mobile clients switch networks. The roadmap called for an isomorphic connection manager inside bxios to monitor socket state across browser and Node.js environments. If the underlying WebSocket dropped, it was intended to trigger auto-reconnect routines with configurable retry backoff strategies, while queuing or rejecting pending frames based on configuration.


The Repository Snapshot Behind This Post

The repository snapshot described here was still in active development on GitHub.

In that snapshot, Milestone 0 was marked complete, with two foundational PRs merged into main:

  • PR #14 (MessagePack Frame Codec & FrameType Specs): Establishes the binary serialization rules, frame structures, header definitions, and frame type identifiers for request, response, error, and ping/pong control frames.
  • PR #15 (Universal IDriver Abstraction Layer): Defines the transport agnostic driver interface (IDriver), allowing bxios and bxios-server to run on top of uWebSockets.js, standard browser WebSocket, or ws without tying core logic to any single engine.

At that point, 11 out of 13 issues remained open on the project issue tracker. The work items on the board covered:

  • Authentication handshake protocols and token propagation over persistent connections
  • Backpressure management and socket drain handling
  • Multiplexed streaming frames
  • Zod schema validation middleware for request/response payloads
  • Decorator-based router for bxios-server
  • Complete axios-compatible method suite (post, put, patch, delete, head, options) and interceptor pipelines
  • Production-grade connection manager and PendingMap edge case hardening
  • Alternative server driver implementations
  • End-to-end test suite and performance benchmark suite
  • Documentation site and README guides

The design goal is to keep the familiar REST API developers already know while exploring whether one persistent WebSocket connection can reduce overhead for this access pattern.

Older writing

Also read

Mic Drop Should Let One Player Be the Referee