Quickstart
Connect, subscribe, publish, and see who else is on the channel.
The whole thing
import * as Blackevin from '@blackevin/client';
const realtime = new Blackevin.Realtime({
key: process.env.BLACKEVIN_KEY,
clientId: 'alice',
});
const channel = realtime.channels.get('chat:lobby');
channel.subscribe(message => {
console.log(message.name, message.data);
});
await channel.publish('message', { text: 'hello' });That is a working realtime app. The rest of this page is what each line is actually doing.
Connecting
You never call connect(). The client opens the socket on the first operation
that needs one and works its way through the handshake:
initialized → connecting → connectedIf you want to watch it, connection state is an event:
realtime.connection.on('state', (state, reason) => {
console.log(state, reason?.message);
});clientId is your presence identity. Omit it and you get a random
client-a3f9…, which is fine until you want to know who is on a channel.
Channels
channels.get(name) is local — it returns a handle and touches no network. The
wire work happens on the first subscribe or publish.
const channel = realtime.channels.get('chat:lobby');Get the channel before you connect if you need listeners ready for early
messages. A Message frame for a channel the client has never heard of is
dropped, because there is nothing to dispatch it to.
Subscribing
channel.subscribe(message => { /* every message on this channel */ });
channel.subscribe('order.created', message => { /* just that name */ });The name form filters client-side. The server still delivers every message on the channel and the SDK discards the ones that do not match — so it saves you a branch, not bandwidth.
Publishing
await channel.publish('order.created', { id: 7 });
await channel.publish({ name: 'order.created', data: { id: 7 } });The promise resolves when the server acknowledges the frame, and rejects on a
Nack or a timeout (realtimeRequestTimeout, 10s by default). Awaiting it is
worth doing: it is the only signal that the message was accepted rather than
merely written to a socket.
Publishing while the connection is down does not throw — the frame is held and sent on reconnect. See offline publishes.
Presence
const channel = realtime.channels.get('chat:lobby');
channel.presence.subscribe((member, action) => {
console.log(member.clientId, action);
});
channel.presence.enter({ status: 'online' });
const members = await channel.presence.get();enter announces you, get asks the server who is there. Both are covered in
presence.
Replaying what you missed
A subscriber that joins late can be handed the last N persisted messages immediately:
const channel = realtime.channels.get('chat:lobby', { params: { rewind: 20 } });
channel.subscribe(message => {
console.log(message.data);
});The rewound messages arrive as ordinary Message frames, oldest first, before
the live ones — your callback cannot tell them apart, which is the point. The
server caps rewind at 100.
From a server, without a socket
A job, a cron, or an inbound webhook can publish over HTTP with no connection to hold open:
import * as Blackevin from '@blackevin/client';
const rest = new Blackevin.Rest({ key: process.env.BLACKEVIN_KEY });
await rest.channels.get('orders:new').publish('order.created', { id: 7 });It goes through the same fanout a socket publish does, so subscribers, account queues and integrations all see it identically.
Next
Your browser must not hold the key you just used. Authentication is how it connects without one.