Learn more
Receiving events
In this section, we'll go through the process of receiving events from Exoquic. More specifically, we'll look at subscriptions, how to authorize a subscription, and how to subscribe.
Prerequisites
- An API key. Don't have one?
- A backend server (See our list of supported languages). Do not expose your API key in your frontend.
Subscription token
A subscription token is a signed token that can be used to subscribe to events from your frontend. Subscription tokens contain the following fields:
- Topic
- Channel (optional)
- Subscription ID (optional)
Let's go through each field in more detail.
Topic
Topics are the way we group events in Exoquic. They can be anything you want, but it's a good idea to use them to group related events. For example, you might use a topic called payment-processing
to group all events related to payment processing, such as payment-succeeded
or payment-failed
.
Ordering of events
Important to note is that the order at which events are published and the order at which the events are consumed won't be the same. The ordering is only guaranteed within a channel. For more information on channels, see the Channel section below.
Channel
Channels are like topics, but they are used to group events by a specific property. For example, you might use a channel called user-123 to group all events related to user 123. The main difference between topics and channels is that events in the same channel are ordered. A subscriber will always receive events in the same order within a channel.
Channels are commonly used to target events to a specific user. For example, if you are building a chat application, you might want to target all events related to a specific user by using their user id as the channel. That user can then subscribe to all events in that channel.
Subscription ID
A string identification for the subscription. Exoquic tracks what event the subscriber has received from each topic/channel. By providing a subscription ID to Exoquic, the subscriber can resume receiving events from where it left off.
Setting up a subscriber
In order to receive events from Exoquic, you need to setup a subscriber. This involves two steps:
- Retrieving an authorization token (aka Subscription token)
- Subscribing with the subscription token.
This process is illustrated in the diagram below.
1. Authorizing a subscriber
Your backend server sends a Subscription Token Request to Exoquic. This request includes the topic, channel, and subscription ID, along with your API key, to obtain a token authorizing the subscriber. Exoquic provides an easy-to-use library for this process for our supported languages.
Example: Authorizing subscribers to the 'posts' topic in a housing cooperative
const express = require("express");
const exoquicAuth = require("@exoquic/auth");
const app = express();
exoquicAuth.initalizeSubscriptionAuthorizer(MY_API_KEY);
app.post("/api/v1/authorize-posts-subscription", async (req, res) => {
const { housingCooperativeId } = req.body;
const auth = getSession(req);
// Check if the user is in the housing cooperative.
if (!isUserInHousingCooperative(auth, housingCooperativeId)) {
return res.status(403).json({ error: "You do not have access to receive events from this housing cooperative." });
}
// All good, user has access to the posts in the housing cooperative.
// Retrieve a subscription token.
const subscriptionToken = await exoquicAuth.authorizeSubscription({
topic: "posts",
channel: `posts-in-housing-cooperative-${housingCooperativeId}`,
});
// Send the subscription token to the client.
return res.text(subscriptionToken);
});
Example: general endpoint for authorizing subscribers
const express = require("express");
const exoquicAuth = require("@exoquic/auth");
const app = express();
exoquicAuth.initalizeSubscriptionAuthorizer(MY_API_KEY);
app.post("/api/v1/authorize-subscription", async (req, res) => {
const { topic, subscriptionId, topicSpecificData } = req.body;
const auth = getSession(req);
if (!topic) {
return res.status(400).json({ error: "Missing topic" });
}
let channel = undefined;
// Handle the chat-message topic.
if (topic == "chat-message") {
// Channel between the caller and the user they are chatting with.
channel = `chat-msgs-between-
${Math.min(auth.userId, topicSpecificData.toUserId)}
-and-
${Math.max(auth.userId, topicSpecificData.toUserId)}`;
// Unset the subscriptionId because we want the user to receive
// all the chat messages ever written between the two users.
subscriptionId = undefined;
}
// Handle the notifications topic.
if (topic == "notifications") {
channel = `notifications-for-${auth.userId}`;
if (subscriptionId != `missed-notifications-${auth.userId}`) {
// If the subscriptionId is not for missed notifications,
// we want to unset the subscription, so that the user can
// read all notifications from the beginning.
subscriptionId = undefined;
}
}
// Retrieve a subscription token.
const subscriptionToken = await exoquicAuth.authorizeSubscription({ topic, channel, subscriptionId });
// Send the subscription token to the client.
return res.text(subscriptionToken);
});
2. Subscribing with the subscription token
After receiving a subscription token, the client can start receiving events. Exoquic provides an easy-to-use library for subscribing, see our supported languages. Subscribing is quite easy:
- Initialize a
SubscriptionManager
with a fetcher function that calls your subscriber-authorization endpoint that you created in step 1. - Call the
authorizeSubscriber
method to retrieve a subscription token. This will call your fetcher function to retrieve an authorization token. - Call the
subscribe
method to start receiving events.
Example: Subscribing to the 'notifications' topic from your frontend
import { SubscriptionManager } from "@exoquic/sub"
import { useState, useEffect } from "react";
// Setup the subscription manager with a fetcher function
// that will be called to retrieve an authorization token.
const subscriptionManager = new SubscriptionManager(async () => {
// Calling the authorization endpoint we setup in step 1
const response = await fetch("/api/v1/authorize-subscription);
// Return the authorization token
return await response.text();
});
const MissedNotifications = ({ session }) => {
const [notifications, setNotifications] = useState([]);
useEffect(() => {
let authorizedSubscriber;
const authorizeMissedNotificationsSubscription = async () => {
// Retrieve an authorization token for the 'notifications' topic.
authorizedSubscriber = await subscriptionManager.authorizeSubscriber({
topic: "notifications",
subscriptionId: `missed-notifications-${session.userId}`,
});
// Subscribe to the 'notifications' topic.
authorizedSubscriber.subscribe(message => {
setNotifications(notifications => [...notifications, message]);
});
};
authorizeMissedNotificationsSubscription();
// Cleanup function to unsubscribe from the 'notifications' topic
// when component unmounts.
return authorizedSubscriber.unsubscribe;
}, [session.userId]);
return (
<div>
<h1>Missed notifications</h1>
{notifications.map(notification => (
<div key={notification.id}>
{notification.message}
</div>
))}
</div>
);
}