Building a realtime logistics service simulator with Cloudflare Workers

The Ably Reactor provides a way to trigger events and to stream data from Ably’s pub/sub channels. This tutorial will go over how to use the Ably Reactor with Cloudflare workers.

Imagine a delivery driver delivering parcels around the neighborhood. Every time a parcel is delivered, we could trigger a serverless function to track and manage the time and date of delivery, to perhaps track how many more deliveries the driver has to make that day, even give the driver the location of the next delivery.

We’ll be building a postal service delivery game using React and Reactor Functions. Players use their arrow keys to drive a delivery van around a map to each of the postboxes that appear. Reactor events will be triggered every time the player ‘delivers’ a parcel to a postbox, this will call a Cloudflare worker which will notify a separate ‘admin page’ that the delivery has been made.


screenshot of the game we will be making

Step 1 – Set up a free account with Ably

In order to run these tutorials locally, you will need an Ably API key. If you are not already signed up, you should sign up now for a free Ably account. Once you have an Ably account:

  1. Log into your app dashboard
  2. Under “Your apps”, click on “Manage app” for any app you wish to use for this tutorial, or create a new one with the “Create New App” button
  3. Click on the “API Keys” tab
  4. Copy the secret “API Key” value from your Root key and store it so that you can use it later in this tutorial

    Copy API Key screenshot

Step2 – Building the Game

To get you up and running quickly, we’ve implemented this 2D game and made it available via this tutorial’s repository. You can clone this repository and install its dependencies as per its readme.

Step 3 – Creating your function

Cloudflare workers is a Function-as-a-Service (FaaS) platform operated by Cloudflare that lets you run pieces of code when they are triggered. They’re comparable to AWS Lambda, or Azure Functions.

Cloudflare have an npm tool called Wrangler that they use to create their Workers. You can install this using:

npm install -g @cloudflare/wrangler

You can then create new Workers by using the command wrangler generate <my-worker-name>.

If you want to learn more about Wrangler and Cloudflare Workers, you can read their documentation on the Cloudflare developer site.

For this example, we’ve already done this for you and you’ll find a Worker in the ./cloudflare-worker/ directory in your cloned repository. It looks a little like this:

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    try {
        const body = await request.text();   // Get the body from the response
        const data = JSON.parse(body);       // Parse body to JavaScript object

        if (!data) {
            return jsonResponse(null, 'Request Body cannot be empty!');
        }

        /* Ably specific code here in the sample */

        return new Response ({ text: "I'm ok!" });

    } catch (error) {
        return new Response(error, { status: 500 });
    }
}

How the game works

The game is a React app, created using Create-React-App, that uses the react-router-dom npm package for routing.

It’s made up of two top level React components, one for the game and the other for an admin dashboard.

The player uses the arrow keys on their keyboard to drive a post van around five pre-set maps. The objective of the game is to deliver a parcel to a postbox on each level. Every time a parcel is delivered, we’re publishing a message to an Ably channel and a new postbox is shown to the user.

The Ably Reactor will listen for these publish events and call our Cloudflare Worker with the body of the published message. Once our Cloudflare Worker receives the message from the Ably Reactor, it will publish its own message to the Ably Channel. The dashboard is subscribed to these messages and will display the number of moves it took for the player to complete each level. In a real world scenario this might be the number of deliveries left on our driver’s shift.


Webapp sequence diagram

Step 4 – Configuring the game

The first thing we need to do to be able to use the provided code is to create a key.js file in the ./src/ directory. Inside this file you’ll export your API key as a variable:

export const ABLY_API_KEY = "YOUR_API_KEY"

Add your own Ably API key in between the quotation marks!

You will also need to add the API key into the ./cloudflare-worker/index.js file. Replace the variable near the top of the file.

Step 5 – Configuring Cloudflare

Configure your Cloudflare Worker as per these instructions in the Cloudflare Docs.

During configuration of your Cloudflare worker, you’re going edit the ./cloudflare-worker/wrangler.toml file to include your Cloudflare account_id. (You might be wondering where your Cloudflare API key goes – in order to deploy a Worker using Wrangler, you’ll configure Wrangler using its config subcommand so your API keys never touch the Worker code itself.)

Once you’re done with configuration, publish your Worker using Wrangler. After publishing your Worker to your Cloudflare account, it will have a URL, you will need this to set up the Ably Reactor.

Creating an Ably Reactor Rule for a Reactor Event

The Ably Reactor is a scalable service to trigger events and stream data from Ably’s pub/sub channels. Creating an Integration Rule will allow your serverless functions or your own servers to be invoked following any events on Ably.

  1. Log into your app dashboard
  2. Go to your Ably Dashboard
  3. Choose the “Reactor” tab
  4. Click “New Reactor Rule” button
  5. Click the “Choose” button under “Reactor Event” type


Click on the 'New Reactor Rule' button


Click on the 'Choose' button under 'Reactor Event' type

Choose the “Cloudflare Workers” option:


Select the Cloudflare Workers option

Fill out the form to provide Ably with the details it needs to invoke your Cloudflare Worker.

Your Reactor event will now be created, and you’ll be able to continue developing the game.

Publishing to an Ably Channel when a delivery is made

Open the file ./src/feature/player/movement.js

This file contains the React code for handling player movements, but it also contains a connection to our Ably Channel, firstly, on line 18

window.Ably.connection.on(function(stateChange) {
    console.log('New connection state is ' + stateChange.current);
})

const outboundChannel = window.Ably.channels.get('cloudflareworkerdemo');

This code establishes a connection to our “cloudflareworkerdemo” channel.

When the player completes a level the handleWin function is called, and on line 64 we publish a message to our channel.

outboundChannel.publish('player', { playerSteps },
    err => {
        if (err) {
            return console.error('Failed to publish', err);
        }
        console.log('published');
    }
)

Our message contains the number of moves the player used to deliver the parcel.

Processing the message in the Worker

Ably Reactor is monitoring the channel and calls the Cloudflare Worker with the message contents.
We can see how this works by taking a look at the Worker file again ./cloudflare-worker/index.js

async function publishToAbly(data) {
    try {
        const URL = `https://rest.ably.io/channels/cloudflare-bot/messages?key=${key}`;

        await fetch(`${URL}`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ data });
        });

    } catch (error) {
        return error;
    }
}

The Worker is re-publishing our initial message to a channel called cloudflare-bot using the Ably REST API. We would usually recommend that you use the Client library SDKs rather than make a direct POST to the Ably Rest API, since it is more discoverable and easier to use. In this particular case however, installing the library files in the Worker would mean bundling the entire library with your Worker, which would increase the size of your script unnecessarily.

We’ll subscribe to this channel on our dashboard to receive these notifications.

Connecting the dashboard

In our game app, there’s a React Component called Scoreboard in ./src/feature/scoreboard/index.js which contains the following code on line 12

const ably = new Realtime(ABLY_API_KEY);

ably.connection.on('connected', () => {
    console.log('Successfully connected!');

    const inboundChannel = ably.channels.get('cloudflare-bot');
    inboundChannel.subscribe(message => {
        this.scoreboard.push(message.data.playerSteps);
        this.setState(this.scoreboard);
    })
})

This is similar to the code we used to originally publish the message, but this time, we’re subscribing to the channel cloudflare-bot.

When the Cloudflare Worker re-publishes our initial message onto this channel, the code in our subscription fires.

This is a stateful React component which keeps track of the moves – “playerSteps” that our players make when they finish each level.

When a message appears on the channel, we’re updating our scoreboard array (defined when the component is created), and updating the React-managed state.

As our state is modified, React will re-draw our component with the updated move counts displayed on the page.

Congratulations, you’re done!


Animated image of the browser app in action

Next Steps

1. Take a look at the Reactor Functions documentation for further details about what was described in this tutorial
2. Find out more about Ably Reactor features and capabilities
3. Learn more about Ably features by stepping through our other Ably tutorials
4. Gain a good technical overview of how the Ably realtime platform works
5. Get in touch if you need help