From a server
Blackevin.Rest in Node, Bun, Deno or an edge runtime — the token endpoint, publishing without a socket, history and presence.
Blackevin.Rest is the client for code that should not hold a socket: a request
handler, a job, a cron, an inbound webhook. It is a separate class from
Realtime rather than a mode of it, because half of a realtime client has no
meaning without a connection.
Publish and read
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 });A job, a cron or an inbound webhook publishes over HTTP with no connection to keep open. The same client reads back:
const channel = rest.channels.get('orders:new');
const messages = await channel.history({ limit: 50 });
const members = await channel.presence.get();See the REST overview for what each call needs.
The token endpoint
The one route almost every app adds. Your server signs a short-lived, scoped token request, so the browser never holds the key:
app.get('/api/blackevin-token', async (req, res) => {
const user = await currentUser(req);
res.json(
await rest.auth.createTokenRequest({
clientId: String(user.id),
ttl: 3_600_000,
capability: {
[`chat:team-${user.teamId}`]: ['subscribe', 'publish'],
},
})
);
});Signing is local — no call to Blackevin — and uses WebCrypto rather than
node:crypto, so the same code runs on an edge runtime.
Authentication walks through the whole flow.
A token for the server itself
Usually a browser exchanges the token request. A server that wants to act as one client, inside a narrow capability, can do the exchange itself:
const details = await rest.auth.requestToken(
await rest.auth.createTokenRequest({
clientId: 'worker-1',
capability: { 'jobs:*': ['publish'] },
})
);
const worker = new Blackevin.Rest({ token: details.token });A token wins over a key when both are given, so a client acting as one user never silently acts as the whole account.