Skip to main content

How we use Cloudflare Workers KV when building MVPs

Published on August 31st, 2022 by Daniel Ramirez

What are Cloudflare Workers KV data objects and how you can use them to avert decision-paralysis and a lot of re-work in early stages of your product lifecycle.

Introduction

Cloudflare Workers KV is a global, low-latency, key-value data store offered by Cloudflare and makes it super easy to manage JSON data on the cloud without much hassle. In this guide, we’ll show you how you can use it to create your very own JSON object database. While it may not be as robust as a traditional database like MySQL or MongoDB, it does act like a database where you can store and access JSON objects in the cloud.

Who should use KV?

While KV isn’t a replacement from traditional database systems like MySQL, SQLite, MongoDB, etc… it does provide the quickest and simplest way to store data on the cloud. However, it does come with its limitations that are noted on the official documentation. Some of the key limitations include:

  • Values can only be maximum of 25 MiB, which is equivalent to about 26K kilobytes. That’s more than enough for small side projects.
  • Free accounts are only allowed a maximum of 1GB overall KV storage, unless you upgrade to Pro or higher for unlimited storage.
  • Free accounts are only allowed up to 100,000 reads per day and only 1,000 writes per day. Paid plans allow for unlimited read and writes per day.

Getting started with KV

To start off, you’ll first need a Cloudflare account. You can make a free account at dash.cloudflare.com/sign-up. With a free account, you’ll have access to almost all of Cloudflare’s features, including access to Workers and KV. Throughout this tutorial, you may refer to Cloudflare’s official Worker documentation at workers.cloudflare.com.

1. Create a KV namespace

Start by creating a new KV namespace. Click the Create namespace button within the “Workers KV” page. Give your KV an easily recognizable name that is both uppercase and contains underscores in place of spaces. Some examples of namespace formats are: “DOG_TREATS” or “PEOPLE”. Then click Add once you’ve decided on a name for your project.

cloudflare-1.jpg

Now, you may click the “View” button to view/edit the newly created KV namespace. This is where you’ll be adding new key-value data that you want to store on the cloud.

cloudflare-2.jpg

For testing purposes, we’ll add the john-doe as the key field. Then copy this JSON object into the value field:

{
	"first_name": "John",
	"last_name": "Doe",
	"age": 25
}

Your newly created KV entry should look something like this:

cloudflare-3.jpg

Notice that we’ve kept the key value as snake-cased while the value field is a JSON object that’s stored in plain-text. Later on, we’ll serialize the plain-text JSON object from within the Worker itself so that JSON data is returned from a GET request.

2. Creating a Worker to access your KV

Now that you have a KV namespace with dummy data, you’ll need to access the KV using a Worker. Start by navigating to the Workers page and clicking the Create a Service button.

cloudflare-4.png

Give your Worker a name like “hello-world” for testing purposes. Once created, this Worker should now live under the workers.dev subdomain.

cloudflare-5.png

Once you’ve created your Worker, navigate to the Settings > Variables tab and click on Add binding to allow your Worker to access this KV namespace.

cloudflare-6.png

cloudflare-7.png

After allowing your Worker access to the KV namespace, navigate back to the Resources tab and click Quick edit to modify the existing default Worker code. Copy and paste the code below we’ve provided to get up and running quickly.

const KV = PEOPLE; // Replace with your KV namespace

const responseHeaders = {
  'Content-Type': 'application/json;charset=UTF-8'
}

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS',
  'Access-Control-Max-Age': '86400',
}

// Handle the incoming request
async function handleRequest(request) {
  if (request.method === 'OPTIONS') {
    return handleOptions(request);
  }

  const url = new URL(request.url);
  let key = url.searchParams.get('key');
  let value;

  if ( !url.searchParams.has('key') ) {
      let _value = await KV.list();
      value = JSON.stringify(_value.keys);
  } else {
      if (request.method === 'POST') {
          const data = await request.text();
          value = await KV.put(key, data);
          value = data;
      } else if (request.method === 'DELETE') {
          value = await KV.delete(key);
      } else {
          value = await KV.get(key);
      }
  }

  const response = new Response(value, { headers: responseHeaders });
  response.headers.set('Access-Control-Allow-Origin', '*');
  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  return response;
}

// Handle the CORS policy
function handleOptions(request) {
  // Make sure the necessary headers are present
  // for this to be a valid pre-flight request
  let headers = request.headers;
  if (
    headers.get('Origin') !== null &&
    headers.get('Access-Control-Request-Method') !== null &&
    headers.get('Access-Control-Request-Headers') !== null
  ) {
    // Handle CORS pre-flight request.
    // If you want to check or reject the requested method + headers
    // you can do that here.
    let respHeaders = {
      ...corsHeaders,
      // Allow all future content Request headers to go back to browser
      // such as Authorization (Bearer) or X-Client-Name-Version
      'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers'),
    };

    return new Response(null, {
      headers: respHeaders,
    });
  } else {
    // Handle standard OPTIONS request.
    // If you want to allow other HTTP Methods, you can do that here.
    return new Response(null, {
      headers: {
        Allow: 'GET, HEAD, POST, OPTIONS',
      },
    });
  }
}

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

3. Testing your Worker KV

Now that everything has been set-up, navigate to your *.workers.dev subdomain. You should now see a list of all your existing KV keys. Try out the various methods on listing, creating, updating, and deleting your key-value objects:

Method

URL Endpoint

Description

GET

*.workers.dev

Lists all existing key-value objects.

GET

*.workers.dev?key=john-doe

List a single key-value object.

POST

*.workers.dev?key=john-doe

Create/update a key-value object. If the provided key does not exist, it will be created.

DELETE

*.workers.dev?key=john-doe

Deletes an existing key-value object.

Quick recap

  • Get started by creating a Cloudflare account to access Cloudflare’s Worker and KV features.
  • Create a KV instance to store all of your key-value JSON objects.
  • Create a Worker to read and write to the KV instance.
  • Modify the Worker to allow for an easy API that your application can use to read/write/delete your key-values.
  • Refer to the API table to learn how your application can send requests to the Worker.

Take-away

Building an MVP is no easy task, especially when you’re in the planning stage where deciding on an SQL schema can be time consuming. Schemas change all the time during the early phases of planning and development, and it becomes a hassle to manage over time as your application grows and scales. Storing data as JSON objects is the quickest and simplest solution in avoiding decision-paralysis when it comes to planning your MVP. By eliminating the need to define a schema early on, you end up saving time and money. Once you’re satisfied with your JSON schema, converting it to an SQL schema should be a breeze.

Here at AKOS, time is money and we use JSON objects to plan and build MVPs early on, without the need to commit to a SQL schema. JSON objects are flexible and change all the time.

Insight Bytes.