Using SyncRelay with Next.js

Next.js is one of the most popular frameworks for building modern front-end applications. As Next.js is powered by React, the SyncRelay SDK for React is fully supported.

We prepared a complete example to integrate SyncRelay with Next.js, which we'll talk through in this guide. Let's go over what we want to achieve, then dive right in!

Room-based real-time notifications

In our example, users can enter rooms, and receive all messages sent to those rooms. This is useful for messaging applications, or all types of room-based services (videos, livestreams, audio rooms, etc.).

Getting Started

Preparation

After setting up our Next.js application, we can prepare all SyncRelay resources. To store our configuration, we'll create a local environment file, called .env.

For this example, you need to create a new SyncRelay project if you haven't done so yet. For this, head over to the dashboard, go into your organization, and click on Create Project. After entering a name and confirming with Create, you will be redirected to the project dashboard.

There, you can already copy both the Project ID and Live Endpoint by clicking on the values and store them in the env file like this

# Used by the API route (backend)
SYNCRELAY_PROJECT_ID=Your Project ID

# Used by the frontend
NEXT_PUBLIC_SYNCRELAY_LIVE_ENDPOINT=Live Endpoint

In addition to the live endpoint, every client attempting to connect is required to include a consumer token, which you will have to generate on the server-side, after verifying the user's identity and making sure they are allowed to receive notifications for the room. In this guide, we will perform all server-side logic in Next.js API routes, but you can substitute this for any Node.js backend service.

To contact the SyncRelay API to authorize a consumer, which will return a consumer token valid for an hour, you need to generate an access token.

You can do this by going back to the organization dashboard, and clicking Create Token. Enter a name and optionally a description, then select Allow to authorize consumers, and if you want to send messages with the same token, Allow to send messages. Confirm by clicking Create, then copy the token by clicking Copy on the success dialog.

Now that we have the access token, we can add it to the environment file.

...
SYNCRELAY_ACCESS_TOKEN=Access Token

This concludes all preparation work, so we can dive right into authorizing our users and connecting to SyncRelay to receive messages.

Authorizing Consumers

Every time a user connects to a room, we need to authorize them. This allows you to check for permissions and ensure you are in control of who connects to your endpoint. To authorize our users, we'll create an API route in pages/api/token.ts, which maps to /api/token. In this file, we'll add the following handler

import type { NextApiRequest, NextApiResponse } from 'next';
import { Client } from '@syncrelay/node';

type Data = {
  consumerToken: string;
};

const accessToken = process.env.SYNCRELAY_ACCESS_TOKEN;
const projectId = process.env.SYNCRELAY_PROJECT_ID;

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse<Data>
) {
  if (!accessToken || !projectId) {
    throw new Error('Missing SyncRelay access token or project');
  }

  const client = new Client({
    accessToken,
  });

  const roomName = req.query['room'];
  if (!roomName) {
    throw new Error('Missing room');
  }

  // TODO Authorize the user to see if they are to subscribe to this room

  const { consumerToken } = await client.authorizeConsumer({
    data: {
      projectId,
      allowedTopics: [`/rooms/${roomName}`],
    },
  });

  res.json({ consumerToken });
}

We use the environment variables added to our .env file to create a new SyncRelay client using the SDK for Node.js. The client can be safely created for every request, as constructing it has nearly no overhead.

To make sure the requesting user only receives messages sent to the room they are entering, the request includes the room name in the query parameters.

Once we know about the user and room they are trying to enter, we can add some additional logic to check if they have all the required permissions. If not, we can throw an error, rejecting the request.

Once we're confident the user is allowed to enter the room, we call authorizeConsumer and pass our project ID and a list of topics, in our case only the topic associated to the room. This means that the user may only subscribe to messages sent to this topic and its sub-topics.

Once the API returns the consumerToken, we can return it to the user.

Note: You can try out the handler by starting up Next.js and dispatching a GET request to http://localhost:3000/api/token?room=<roomName>. The response should contain a stringified object with the consumerToken.

Connecting to SyncRelay

Now that we have our consumer token ready, we can establish a connection to SyncRelay on the client-side.

import {
  useSyncRelay,
  ReceivedMessage
} from "@syncrelay/react";
import { useState } from "react";

const liveUrl = process.env.NEXT_PUBLIC_SYNCRELAY_LIVE_ENDPOINT;

export function LiveView({
  consumerToken,
  room,
  liveUrl,
}: {
  consumerToken: string;
  room: string;
  liveUrl: string;
}) {
  const [messages, setMessages] = useState<ReceivedMessage<unknown>[]>([]);
  const [connectionState] = useSyncRelay(
    {
      receivedMessage: (message) => {
        setMessages((prev) => [message, ...prev]);
      },
    },
    {
      liveUrl,
      subscribeTo: [`/rooms/${room}`],
      consumerToken,
    }
  );

  ...
}

Connecting to SyncRelay is as easy as calling the useSyncRelay hook exposed by the SDK for React with the Live Endpoint URL, the topic(s) we want to subscribe to, and the consumer token we acquired by calling the API.

Once connected, we handle received messages by registering a handler for receivedMessage. In our case, we'll just update an internal state hook, prepending the new message.

And that's it! With fewer than 60 lines of code, we were able to authorize our users, making sure permissions are respected when connecting to the live endpoint, and receiving all messages sent to the current room. In the next step, we'll dispatch a message to the room.

Sending a message

Before we send a message, make sure to start up Next.js and join a room.

There are two ways of how you can send messages to a project. You can either draft and send it via the project dashboard or by using the API and SDKs we provide to send messages programmatically.

Manually

Head over to the project dashboard again and fill out the form with your message contents. Make sure the topic matches the structure you're using and the current room name you're in.

The message kind allows clients to perform different operations for different events, but for now, you can enter any arbitrary text. The payload has to be valid JSON (e.g. "Hello World!" or {"content":"Hello World!"}).

Using the SDK for Node.js

Similar to how we authorized consumers before, we can use the SDK to send a message programmatically. You could put this in another route that is called when a user sends a message, for example. Managing the logic to send messages to the project on the server-side once again allows you to control who may send a message at all, and persist messages in your backend.

Once again, we will pass the Project ID, as well as kind, topic, and our stringified message payload. This has to be valid, stringified JSON.

const { sentMessage } = await client.sendMessage({
  data: {
    projectId: '<your project id',
    kind: '<message kind>',
    topic: '<message topic>',
    payload: JSON.stringify({
      content: 'Hello World',
    }),
  },
});

We are planning to create ways for clients to send messages, if this is an important use case for you, please reach out to us via the feedback form in the navigation bar, we'll take your input into account when designing the feature.


Thank you for going through this guide. If you have any questions, suggestions, or other feedback, feel free to reach out to us!