# ⛅ Key-Value Storage With Cloudflare Workers KV (lesson 4)

Serverless key-value storage or KV provides an easy way to store / retrieve data for your Cloudflare Workers. Best of all, Cloudflare has recently introduced a FREE tier for KV!

%[https://www.youtube.com/watch?v=5dkzFrJ044o]

## Cloudflare Workers Course Outline

1. [Getting Started with Serverless Cloudflare Workers](https://joel.net/getting-started-with-serverless-cloudflare-workers-lesson-1)
2. [Cloudflare Workers as a Web Server (with Webpack)](https://joel.net/cloudflare-workers-as-a-web-server-with-webpack-lesson-2)
3. [Making API Calls From a Cloudflare Worker](https://joel.net/making-api-calls-from-a-cloudflare-worker-lesson-3)
4. **Key-Value Storage With Cloudflare Workers KV**
5. [[Bonus] Smart Routing with Cloudflare Workers](https://joel.net/smart-routing-with-cloudflare-workers-and-webpack-bonus-content)


## About Workers KV

Workers KV reads respond incredibly fast. According to Cloudflare, as quickly as a cached static file would.

Workers KV is an eventually-consistent database. Changes may take up to 60 seconds to propagate (though in my experience it's much less). This means KV cannot be used in applications that require atomic transactions.

Workers KV is a key-value storage. Typical of KV storage solutions, there is no search. But there are list operations.

[More on How KV Works](https://developers.cloudflare.com/workers/learning/how-kv-works)

## What We'll Be Making

For this lesson, we will be using Cloudflare KV to `put` a key, `get` a key, and `list` keys.

## Starting Where We Left Off

In case you haven't been following along with the previous lessons (you don't have to), you can start here:

```bash
# continue where we left off
$ git clone https://github.com/joelnet/cloudflare-worker-website.git
$ cd cloudflare-worker-website
$ git checkout f6a224b37010d76d694320d4748ac07cb75f7369
$ npm ci
```

## Create KV Namespace

Namespaces are a way to segregate KV stores. This is useful if you run many applications under Cloudflare or separation within an application. For example, you might want to create LOGINS and PROFILES Namespaces. You could also do this with a key prefix like `logins:<login>` or `profiles:<profile>`.

A Namespace can be created within the Cloudflare website or with the `wrangler` CLI.

I'm going to create a new Namespace called `FILES`.

```bash
$ wrangler kv:namespace create "FILES"
```

The output should look something like this. Be sure to paste that bottom piece into your `wrangler.toml`.

```text
🌀  Creating namespace with title "my-website-FILES"
✨  Success!
Add the following to your configuration file:
kv_namespaces = [ 
         { binding = "FILES", id = "529b4c9be8b344f59adfe89cc9765879" }
]
```

## ESLint

Add an ESLint Global by opening `.eslintrc.yml`. This will prevent ESLint from complaining about the `FILES` global.

```yaml
globals:
  FILES: readonly
```

## The Setup

The plan is to create 3 files. One each to read, write, and list the keys. Then use KV to store markdown files and then display them as HTML.

First, setup the routing to the new files.

Open [src/index.js](https://github.com/joelnet/cloudflare-worker-website/blob/f6a224b37010d76d694320d4748ac07cb75f7369/src/index.js) and add the imports.

```javascript
import filesPost from './pages/files.post'
import files from './pages/files'
import filesList from './pages/files.list'
```

Then add a new routes. `/?` is RegEx for optional `/`. `.+` is RegEx for one or more character.

```
router.post('/files/?', () => filesPost(request))
router.get('/files/.+', () => files(request))
router.get('/files/?', () => filesList(request))
```

## Adding a Key

Create `src/pages/files.post.js`. To create a key use the format: `await NAMESPACE.put(key, value)`.

```javascript
import { htmlResponse } from '../lib/responses'

const filePost = async request => {
	const { filename, contents } = await request.json()
	await FILES.put(filename, contents)
	return htmlResponse('SUCCESS')
}

export default filePost
```

Keys can auto-expire by setting the `expiration`. [Expiring Keys](https://developers.cloudflare.com/workers/runtime-apis/kv#writing-key-value-pairs)

## Reading a Key

Create `src/pages/files.js`. Reading a key uses the format: `await NAMESPACE.get(key)`.

Note: There are more sophisticated ways to pull the filename off the URL, I'm using `substring` for the simplicity of this demo.

```javascript
import marked from 'marked'
import { htmlResponse } from '../lib/responses'

import marked from 'marked'
import { htmlResponse } from '../lib/responses'

const files = async request => {
	const url = new URL(request.url)
	const id = url.pathname.substring(7)
	const file = await FILES.get(id)
	const html = marked(file)
	return htmlResponse(html)
}

export default files
```

## Listing Keys

Create `src/pages/files.list.js`. The list uses the format: `await NAMESPACE.list()`.

```javascript
import { htmlResponse } from '../lib/responses'

const fileToLi = file => `
<li>
  <a href="/files/${file.name}">${file.name}</a>
</li>
`

const fileList = async () => {
	const files = await FILES.list()
	const lis = files.keys.map(fileToLi).join('')
	return htmlResponse(`<ul>${lis}</ul>`)
}

export default fileList
```

Lists also support paging and listing by prefix. [Check the docs for more on lists](https://developers.cloudflare.com/workers/runtime-apis/kv).

## Extra Credit

- Using the same pattern, implement a delete with the method `await NAMESPACE.delete(key)`.

- Implement pagination on the list page to support > 1000 entries.

- Experiment with Expiring Keys.

[Refer to the API](https://developers.cloudflare.com/workers/runtime-apis/kv) for reference.

## Summary

We used Workers KV to create a file upload to upload `markdown` files as well as display those `markdown` files as `HTML`. As a bonus there is a page that lists the `FILES` uploaded.

Browse [the repository at this point in history](https://github.com/joelnet/cloudflare-worker-website/tree/b3dd8bb888f709fa30bf37bf63ac18b9d7ae30a4).

Subscribe to my [Newsletter](https://joel.net/newsletter) to continue learning about Cloudflare Workers!

Cheers 🍻

- Join my 📰 [Newsletter](https://joel.net/newsletter)
- Subscribe to my 📺 YouTube, [JoelCodes](https://www.youtube.com/c/JoelCodes?sub_confirmation=1)
- Say hi to me on Twitter [@joelnet](https://twitter.com/joelnet)

