Skip to main content

Presence

Presence lets you know which objects are currently "online". Whether an object is online depends on whether a data provider for the object is currently connected.

Isn't a "data provider" just a normal client?

Yes-ish. Here's the problem data providers solve: Imagine you run a food delivery service, and you want to track the location of each of your delivery riders. Each rider is represented as an object within Hivekit. And each rider has a smartphone (client) that connects to Hivekit and updates the object representing them.

These two concepts need to be connected to each other. And that's exactly where data providers come in.

How data providers work

When a client connects to Hivekit, it needs to authenticate via a JWT. This JWT can contain additional information, such as permissions or data provider patterns. Here's what this looks like for the example above:

{
// user john doe is a delivery driver in London
"sub": "johndoe-123",
// here are his permissions
"per": {
// these relate to the realm "london"
"london": {
// john can (R)ead all the other delivery drivers position in the app
"deliveryRiders/*":"R",
// but he can only (C)reate and (U)pdate information for himself
"deliveryRides/johndoe-123": "CU"
}
},
// John is a data provider, but only for himself
"dpv": {
// within the realm London. You can also use a wildcard "*" for all realms
"london": [
// John provides data for an object with his own ID
"deliveryRides/johndoe-123"
]
}
}

Now, whenever a client with data provider patterns connects or disconnects, two things happen:

  • the Hivekit server will update the connectionStatus property of all objects matching this pattern.
  • the Hivekit server will publish a connectionStatusChanged event for that realm to all clients subscribed to this event

Here's what that looks like on the client side:

import HivekitClient from "@hivekit/client-js";

const client = new HivekitClient();
client.on('connectionStatusChanged', () => {
console.log('client connection status is: ' + client.connectionStatus);
});
await client.connect('wss://hivekit.io/api/v1/ws');
await client.authenticate('my_api_token');

// get a realm
realm = await client.realm.get(realmId);

// get the object for our delivery rider
const johnDoe = await realm.object.get('deliveryRides/johndoe-123');
johnDoe.connectionStatus // either 'connected' or 'disconnected'

Many to many mappings

There is an n:m relationship between data providers and objects. This means that a client can provide data for many objects, and an object can have multiple data providers.

Here's why this is helpful:

  • When integrating with an API that provides bulk updates, e.g. an endpoint for aircraft positions, shipment tracking information or vessel locations at sea, it is helpful to have a single data provider for thousands of objects.
  • When using a load balancer, an API Gateway or some other proxy between the Hivekit Server and the Web, this proxy might bundle multiple data providers together.
  • When a user installs your app on multiple devices, he might be online and connected on these devices simultaneously. Hivekit will aggregate the data provider status and only set a connectionStatus to disconnected if all data providers are offline.

You can register a client as a data provider for multiple objects by using wildcards (*) in your id patterns.

"dpv": {
// this client is a data provider for all realms
"*": [
// it provides data for all delivery riders that are cyclists
"deliveryRides/cyclists/*",

// as well as all maintenance vehicles
"maintenanceVehicles/*"
]
}

How to react to connection status changes

When you read an object, e.g. via the JS client with

const johnDoe = await realm.object.get('deliveryRides/johndoe-123');
johnDoe.connectionStatus // either 'connected' or 'disconnected'

you'll get the objects current connectionStatus - but you won't be notified of changes. To react to connection status changes, you have two options:

Subscribing to objects

A regular subscription to objects will provide you with updates whenever the connectionStatus of one of the matching objects changes, e.g.

// subscribe to updates for all objects within a realm
const subscription = await realmA.object.subscribe();
subscription.on('update', objects => {
for( let id in objects ) {
console.log( id, objects[id].connectionStatus);
}
})

Using this approach, you get an up to date list of all objects and their connectionStatus. This is great to update e.g. icons on a map, but its less helpful if you want to trigger specific actions whenever a client connects or disconnects. In this case, you're better off using the approach below:

Subscribing to the connectionStatusChanged event

If you want to explicitly trigger an action whenever an object comes online or goes offline, subscribe to the connectionStatusChanged event on the realm.

await realmInstanceA.pubsub.subscribe('connectionStatusChanged', (data, id) => {
// data = {connectionStatus: 'connected'}
// idPattern is the id of the object that changed
})

If you only want to receive notifications for a subset of objects, you can also provide an id pattern when subscribing

await realmInstanceA.pubsub.subscribe('connectionStatusChanged', 'deliveryRiders/*', (data, id) => {
// only receive notifications of a deliveryRider changes its connection status
})