How to Build a Reusable Cloudflare Worker component

ni
nitsan7703 weeks ago

TL;DR

The process of spawning and deploying a serverless Cloudflare worker is as simple as:

  • Fork an existing Cloudflare worker component to your scope (this creates a new component with the same source files as the original component)
bit fork nitsan770.cloudflare-workers/counter
Copied
  • Configure the app in the workspace.json file.
"nitsan770.cloudflare-workers/counter": {},
Copied
  • Edit the counter.cf-worker.ts file with your credentials.
  • Edit the counter.app-root.ts file with your logic.
  • Tag the component. Don't forget to add your Cloudflare token as an env variable.

Congrats on your new Cloudflare worker component that deploys itself whenever you release a new version with Bit. 🎉

Introduction

My role as a frontend developer often requires me to use backend services. Very rarely, I could find an API that fit my requirements in the backend. Most of the time, that API does not exist or requires a major refactor to meet my needs.

This is one of the main reasons I decided to develop my projects in the JAMstack approach. JAMstack (stands for JavaScript, API, and Markup) is an architecture pattern that decouples the frontend from the backend and relays on using micro-services (or serverless functions) as its backend. Both the frontend and microservices are hosted on CDNs, very close to the end user. The result is super fast web pages that are easy to maintain and are very reliable.

Cloudflare workers do just that. They enable serverless functions to run as close to the end user as possible. The serverless code itself is 'cached' on the network, and it runs when the right type of request is received.

Throughout this guide, I will show you how you can use Cloudflare Workers along with Bit to create your own serverless components which will serve as a backend for your web pages.

The serverless workers you create with Bit can be very easily maintained since each worker is versioned, built, and deployed separately. Bit lets you document the services you build so that anyone wanting to interact with the workers' API (or fork them to reuse and extend them) will have a pleasant experience. As a result, it is easier to reuse and share the component with other developers.

Let's begin the service!

What are we going to build?

Cloudflare worker components are used by Bit for a wide range of purposes. This tutorial will show you how to create a simple reusable counter worker component. At Bit, we use similar components to perform tasks such as tracking the popularity of labels in the blog in order to display the most relevant content. Bit allows us to spawn new workers very easily and quickly, reuse them, upgrade them, and modify them. Here's a sneak peek of the component you will be creating (have a look at the code tab):

Setting up our Workspace

You will need to have Bit installed locally, a bit.cloud account, a Cloudflare account and know how to open a remote scope to follow along with us. Almighty Gosh, that's a long list.

First, we need to set up our workspace. Open the designated folder and run:

bit init
Copied

Ensure that your workspace.json file has the default scope set to your remote scope. For example, here are the adjustments I made to my workspace:

- "defaultScope": "my-scope"
+ "defaultScope": "nitsan770.cloudflare-workers"
Copied

Now it's time to create your first Cloudflare worker component! I'm really excited for you. 🤗

To keep things simple, we're going to fork an existing component I made for this tutorial. It's going to be your own after you fork it so feel free to make and changes you want. In your Workspace folder, run:

bit fork nitsan770.cloudflare-workers/counter
Copied

Now add this line at the top level of your workspace.json file:

"nitsan770.cloudflare-workers/counter": {},
Copied

Make sure your replace nitsan770 with your username and cloudflare-workers with the name of your scope.

After running bit install && bit compile, Bit will be able to identify your as of app type:

bit app list
┌────────────────────────────────────────────┬─────────┐
│ id                                         │ name    │
├────────────────────────────────────────────┼─────────┤
│ nitsan770.cloudflare-workers/counter       │ counter │
└────────────────────────────────────────────┴─────────┘
Copied

We are all set to go 🙌 Let's start writing some code!

Building our Cloudflare worker

After you've forked the worker component in your workspace, let's examine its source code.

The counter.app-root.ts file represents the entry point for your worker. This is the file that will be executed when the worker is called.

It contains two handler functions:

  • handleOptions: This function is invoked when the worker is passed an options object.
  • handleRequest: This function is invoked when the worker is called with a request object.

We will be covering the handleRequest function in this guide, but feel free to check out the handleOptions function as well.

Here is how the handleRequest function works, let's take a look at it:

async function handleRequest(request: any) {
  let response: any;

  if (request.method === "OPTIONS") {
    response = handleOptions(request);
  } else {
    const { pathname } = new URL(request.url);
    const currentCount = await COUNTER.get("count");

    if (pathname.endsWith("/increment")) {
      const newCount = currentCount ? +currentCount + 1 : 1;
      await COUNTER.put("count", newCount);
    } else if (pathname.endsWith("/decrement")) {
      const newCount = currentCount ? +currentCount - 1 : -1;
      await COUNTER.put("count", newCount);
    }
    const updatedCount = await COUNTER.get("count");
    response = new Response(JSON.stringify({ count: updatedCount }));
    response.headers.set("Access-Control-Allow-Origin", "*");
    response.headers.set(
      "Access-Control-Allow-Methods",
      "GET, POST, PUT, DELETE, OPTIONS"
    );
  }
  return response;
}
Copied

What do we have here?

By extracting the pathname from the request.url property, we can access the path of the request. Next, we check if the path ends in /increment or /decrement. If it does, we increment or decrement the counter, respectively. Finally, we return the response to the client with the updated count.

Maybe you are wondering where that COUNTER variable comes from? Although we don't define it anywhere, we use it in the handleRequest function to update (put) and retrieve (get) the counter value.

Well, this is a nice feature of Cloudflare Workers called Workers KV. Workers KV is a global, low-latency key-value store. The data is stored in a small number of centralized data centers, and then cached in Cloudflare's data centers after being accessed. Since KV delivers extremely high read volumes with low latency, you can build highly dynamic APIs and websites that respond as quickly as a cached file. To make the KV storage accessible to your worker, you will need to bind the Worker to the KV storage.

Finally, the 'addEventListener' function receives the 'handleRequest' function:

addEventListener("fetch", (event: any) => {
  event.respondWith(
    handleRequest(event.request).catch(
      (err) =>
        new Response(err.stack, {
          status: 500,
        })
    )
  );
});
Copied

There we have a working worker. 🎉 As we move forward with deploying our worker to the rest of the world, let's take a minute to document it so that anyone who wants to use it will understand what it does.

I promise you, it will be a lot of fun!

Documenting the Worker

The documentation for Bit components is written in MDX (https://mdxjs.com/). Put anything you want into your component to make it more visually pleasing, no more boring sentences. Even service workers can be visualized!

Since this blog post is also written in MDX, I can simply display the component documentation here:


description: A Cloudflare worker that increments or decrements a counter.

labels: ["serverless", "counter", "cloudflare worker"]

Cloudflare Workers are serverless applications most often used to intercept and modify network requests (much like Service Workers in the browser).

This CF Worker sets a function to be triggered on a 'get' event that triggers an increment or decrement to the counters count.

The function returns a Response, containing the current count.

To edit the worker follow these steps:

  • fork the component to your scope
bit fork nitsan770.cloudflare-workers/counter
Copied
  • Configure the app in the workspace.json file.
"nitsan770.cloudflare-workers/counter": {},
Copied
  • Edit counter.cf-worker.ts file with your credentials.

  • Edit the counter.app-root.ts file to add your logic.

  • Edit the environment variables in your worker settings.

  • Tag the component. Don't forget to add your cloudflare token as an env variable.

Documenting components with Bit has the advantage of being part of the component itself. The documentation is available every time someone visits your component, whether it's to install it or to import and extend it.

The documentation of backend services can also show a real example of how its API is used, as we did with the counter component. This allows the consumer to see how the API works and if it is working properly.

Deploying the Worker to Cloudlfare

The last part of the guide explains how to deploy the worker to Cloudflare servers. This is actually the easiest part of the guide since the cloudflare-worker component(aspect) takes care of everything for you.

Just replace the credentials in the cloudflare-worker aspect with your own:

counter.cf-worker.ts
deployOptions: {
  // the auth token for cloudflare (https://developers.cloudflare.com/api/tokens/create)
  auth: { token: process.env.CLOUDFLARE_TOKEN },
  // your cloudflare account id
  accountId: '37ec40bec82f25b95678f21f01ca64a0',
  // the cloudflare hosting zone id
  zoneId: 'ec8d03e740264543065fad46cb2c528f',
  // the url for the deployed worker
  routes: ['api.bit.cloud/blog-examples/counter/*'],
}
Copied

When we tag a cloudflare worker, Bit will also deploy your worker. You can always be sure the worker is up to date that way.

bit tag -m "Deploying the worker to Cloudflare"
Copied

In the tagging process, your application (the worker) is also deployed:

✔ env "teambit.apps/envs/[email protected]", task "teambit.harmony/application:deploy_application" has completed successfully in 3s
✔ executing post-build for all tasks
✔ teambit.pipelines/builder, running tag pipe for 1 environments, total 3 tasks (completed in 5s)
1 component(s) tagged
(use "bit export [collection]" to push these components to a remote")
(use "bit untag" to unstage versions)

changed components
(components that got a version bump)
     > [email protected]0.0.4
Copied

Summary

We've covered the basics of building a cloudflare worker with Bit in this guide. In addition, we covered how to document the worker and deploy it to Cloudflare.

While it is easy to deploy the worker with Bit, the main benefit is that you always have a single source of data for your worker that is truly decoupled from the rest of your app.

If you have any questions, feel free to reach out on our Slack channel.

Thank you for reading this guide. I hope you enjoyed it. You can count on the Cloudflare worker we've built (OK, that's a good dad joke! 😂)