WebSocket
Subscribe to real-time MisarBlog channels over a persistent WebSocket connection.
Overview
MisarBlog runs a WebSocket server (a custom Next.js server) that accepts connections at wss://www.misar.blog/ws/*. Connect to a channel to receive real-time messages pushed by the platform without polling.
Host
WebSocket upgrades are served directly by the MisarBlog application server at www.misar.blog, not through the api.misar.io/blog REST gateway.
Authentication
Authenticate with your API key. In Node.js, send an Authorization: Bearer mbk_... header on the upgrade request. Browsers cannot set headers on a WebSocket handshake, so pass the key as a query parameter instead: ?token=mbk_.... A missing or invalid key causes the upgrade to be rejected and the socket to close immediately.
Channels
| Channel | Path | Description |
|---|---|---|
| User notifications (default) | wss://www.misar.blog/ws/notifications | Real-time messages for the authenticated user |
| Article updates | wss://www.misar.blog/ws/articles/:slug | Live updates for a specific article; substitute :slug with the article's URL slug |
The path segment after /ws/ selects the channel and defaults to notifications. For the articles channel, the next segment is the article slug.
Connect
const token = "mbk_your_api_key";
const ws = new WebSocket(`wss://www.misar.blog/ws/notifications?token=${token}`);
ws.onopen = () => console.log("Connected");
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log("Message:", data);
};
ws.onclose = (event) => console.log("Disconnected:", event.code, event.reason);
ws.onerror = (err) => console.error("WebSocket error:", err);import WebSocket from "ws";
const ws = new WebSocket("wss://www.misar.blog/ws/notifications", {
headers: { Authorization: "Bearer mbk_your_api_key" },
});const ws = new WebSocket(
`wss://www.misar.blog/ws/articles/my-first-post?token=${token}`
);Messages
All messages are JSON. Two message types are part of the connection protocol:
connected{ type: "connected", channel, userId }Sent by the server immediately after a successful connection. channel is notifications or articles; userId is the authenticated user's id.
pong{ type: "pong" }Sent by the server in reply to a client {"type":"ping"} message (see Heartbeat).
{ "type": "connected", "channel": "notifications", "userId": "..." }Beyond the connection protocol, the server pushes notification and article-update messages to subscribed clients as JSON objects carrying a type field. Route on type in your onmessage handler and ignore message types you don't recognize.
Heartbeat
The connection is kept alive at two levels:
- Protocol ping/pong — the server sends a WebSocket protocol
pingframe every 30 seconds and terminates the connection if the client hasn't answered by the next interval. Standard WebSocket clients answer protocol pings automatically. - Application ping/pong — you may also send
{"type":"ping"}as a text message at any time; the server replies with{"type":"pong"}.
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: "ping" }));
}
}, 25_000);Reconnection
The server does not send a custom close code on auth failure — the upgrade is simply rejected and the socket closes. Implement exponential backoff when reconnecting:
function connect(token: string, slug?: string) {
const path = slug
? `wss://www.misar.blog/ws/articles/${slug}`
: "wss://www.misar.blog/ws/notifications";
const ws = new WebSocket(`${path}?token=${token}`);
let retryDelay = 1000;
ws.onclose = () => {
setTimeout(() => connect(token, slug), retryDelay);
retryDelay = Math.min(retryDelay * 2, 30_000);
};
ws.onopen = () => {
retryDelay = 1000; // reset on successful connection
};
return ws;
}