MisarMisar Docs
MisarMailMisar.BlogMisarReachMisarPostMisar.DevMisarSEOMisar PlatformMisar SSO
Streaming

WebSocket Streaming

Receive real-time notifications and per-article live updates over a persistent WebSocket connection.

Overview

MisarBlog runs a WebSocket server on its own application host (the custom Next.js server), not on the REST API host. Connect to wss://www.misar.blog/ws/* to receive events without polling. Two channels are available: user notifications and per-article live updates.


Channels

The connection path is /ws/<channel>/<channelId?>. If no channel segment is given, the connection defaults to notifications.

wss://www.misar.blog/ws/notifications

User-scoped events for the authenticated key owner (the account whose API key opened the connection).

const ws = new WebSocket(
  `wss://www.misar.blog/ws/notifications?token=${apiKey}`
);

ws.onmessage = (e) => {
  const event = JSON.parse(e.data);
  // event.type === "connected" on open; other objects are pushed notifications
};

wss://www.misar.blog/ws/articles/:slug

Live updates (view counts, reactions) for a single article, identified by its slug in the path.

const ws = new WebSocket(
  `wss://www.misar.blog/ws/articles/my-first-post?token=${apiKey}`
);

ws.onmessage = (e) => {
  const event = JSON.parse(e.data);
  // updates for the "my-first-post" article are delivered here
};

Push payloads are delivered as JSON objects to every connection subscribed to the relevant user or article. Handle each message defensively by inspecting its fields — the only messages the server guarantees are the connected handshake and pong replies described below.


Authentication

Authentication happens during the HTTP upgrade, using an API key (mbk_…). An invalid or missing key is rejected with 401 Unauthorized and the socket is closed.

Query parameter vs header

Browser clients cannot set an Authorization header on a WebSocket connection — pass the key as ?token=mbk_…. Node.js clients may use either the query parameter or the Authorization: Bearer header.

const ws = new WebSocket(`wss://www.misar.blog/ws/notifications?token=${apiKey}`);
import WebSocket from "ws";
const ws = new WebSocket("wss://www.misar.blog/ws/notifications", {
  headers: { Authorization: `Bearer ${apiKey}` },
});

Connection handshake

Immediately after a successful upgrade, the server sends a connected message:

{ "type": "connected", "channel": "notifications", "userId": "…" }
FieldTypeDescription
typestringAlways "connected" for the handshake.
channelstring"notifications" or "articles".
userIdstringThe authenticated account id.

Application-level ping

You can send a ping message and the server replies with a pong. This is separate from the protocol-level heartbeat below.

ws.send(JSON.stringify({ type: "ping" }));
// server → { "type": "pong" }

Heartbeat

The server also runs a protocol-level heartbeat, sending a WebSocket ping frame every 30 seconds. Browsers and standard WebSocket libraries reply with a pong automatically. A connection that fails to respond by the next interval is terminated by the server.


Reconnection

function connectWs(apiKey: string, channel: string) {
  let delay = 1_000;
  let ws: WebSocket;

  function connect() {
    ws = new WebSocket(`wss://www.misar.blog/ws/${channel}?token=${apiKey}`);
    ws.onopen = () => { delay = 1_000; };
    ws.onclose = () => setTimeout(connect, delay = Math.min(delay * 2, 30_000));
    return ws;
  }

  return connect();
}