Realtime

Get live hints about new notifications, changes on posts you show and new posts in a feed with unet.api.realtime(). Topics, events, reconnecting, and what never travels on it.

Realtime tells your app that something changed, so you can reload only what you show. It runs over server-sent events: one-way, with the access token in a request header and never in an address. The stream carries no post text, no message and no counts; you read the data from the ordinary API, so every privacy rule still applies.

Start a stream

const rt = unet.api.realtime({
  notifications: true, // needs notifications:read
  posts: [123], // posts you are showing; needs content:read
  feeds: ["following"], // "global" and/or "following"; needs content:read
});

rt.on("notification", (e) => console.log("new notification", e.kind, e.postId));
rt.on("post", (e) => console.log("post changed", e.postId, e.change));
rt.on("feed", (e) => console.log("new post in", e.feed, e.postId));

The stream opens at once. Call rt.close() when you no longer need it.

Topics

OptionStreamEventWhat it says
notifications: truenotificationsnotificationA new notification for the person: kind, id, postId, commentId.
posts: [ids]contentpostA post you show changed: change is "engagement", "comment", "edited" or "deleted". Up to 12 posts, and each must be one the person may see.
feeds: [...]contentfeedA new post appeared in "global" or "following": feed, change: "new_post", postId.

Notifications and content are two separate streams, so a missing permission for one does not stop the other.

Change what is watched without closing: rt.update({ notifications: true, posts: [other] }). A stream whose topics did not change keeps running.

React to events by reloading

An event is a hint, not the data. Reload what you show:

const rt = unet.api.realtime({ notifications: true });

rt.on("notification", async () => {
  const n = await unet.api.notifications({ unreadOnly: true });
  badge(n.unreadCount);
});

declare function badge(count: number): void;

Connection events

EventPayloadUse it to
openstream, reconnectedKnow a stream is up. With reconnected: true it was up before, so anything may have changed in the gap: reload what is on screen.
errorstream, error, fatalReport a problem. When fatal is true the stream will not be retried.
const rt = unet.api.realtime({ notifications: true });

rt.on("open", (e) => {
  if (e.reconnected) reloadEverythingOnScreen();
});

rt.on("error", (e) => {
  if (e.fatal) {
    console.warn(`${e.stream} stream stopped for good`, e.error);
  }
});

declare function reloadEverythingOnScreen(): void;

A stream stops for good (fatal) when the session is gone (SessionExpired), the permission is missing (InsufficientScope), the topic is refused (for example a post the person may not see), or the server has no such stream. Other errors are retried by the SDK.

Reconnecting and lifetime

You do not manage connections:

  • A dropped connection is retried after a pause that starts at one second and doubles, up to 30 seconds. Set minDelayMs and maxDelayMs in the options to change them.
  • A stream lives about 50 minutes, then the server ends it on purpose with a hint to renew, so a token never outlives its stream. The SDK reconnects at once with a fresh token. That reconnect also triggers open with reconnected: true.
  • A person may have a few streams open per app and kind (for example several tabs). When there are too many, the server closes the oldest.

What is not on it

Direct messages, calls, and any other private event never travel on the stream. Event payloads carry no text, actor or counts.

In a React component

import { useEffect } from "react";
import { unet } from "./unet";

export function useNotificationHints(onHint: () => void) {
  useEffect(() => {
    const rt = unet.api.realtime({ notifications: true });
    const off = rt.on("notification", onHint);
    return () => {
      off();
      rt.close();
    };
  }, [onHint]);
}

Close the stream when the component unmounts; otherwise each remount opens another one.

Common mistakes

  • Treating an event as the new state. It is only a signal to reload.
  • Forgetting close(). Streams count against a per-person limit and hold a connection.
  • Ignoring reconnected: true. After a gap you may have missed events; hints are not replayed.
  • Listing more than 12 posts. Watch the ones on screen and update the list as the person scrolls.

What next