Build a Link Shortener with Cloudflare Workers: The back-end
Tags: serverless, tutorial, javascript, cloudflare
Published: 2021-07-23 21:10:33

In this part of the tutorial, we'll use Cloudflare Workers to accomplish two things and build the back-end of our app:

1. Creating a new short link and returning the slug of that short link (e.g given a link like `shorturl.at/gIUX4`, the slug is `gIUX4`)
2. Redirecting from a short link to the full link, given a slug

## Before we begin

If you haven't gone through the [introduction to the project](https://dev.to/mmascioni/build-a-link-shortener-with-cloudflare-workers-1j3i) part of this tutorial yet to set up your environment, head back to that part first!

Let's install two packages this project will depend on. We'll make use of these two very lightweight packages:

- [itty-router](https://github.com/kwhitley/itty-router): Will allow us to declare URL routes and parameters in those routes easily
- [nanoid](https://github.com/ai/nanoid): What we'll use to generate the random slugs to access the URLs at

Switch to the project directory we created in the last part of this tutorial, and use `npm` to install `itty-router` and `nanoid`:

```shell
npm i itty-router nanoid
```

Also, please check to ensure that you've populated your Account ID in your `wrangler.toml` file, and have set the `type` for your project to `webpack` to package up these dependencies.

## Setting up our Workers KV Store

To store slugs and the URLs they point to, we'll use [Workers KV](https://developers.cloudflare.com/workers/learning/how-kv-works). It's optimized for high-read applications where we'll need to access these keys frequently, and can be easily accessed from your Worker code. Each key will be a slug (e.g `gIUX4`), and the value the URL they point to (e.g `https://www.youtube.com/watch?v=dQw4w9WgXcQ`).

KV stores are organized in *namespaces*, and you can create them using Wrangler. Let's create one called `SHORTEN`:

```shell
$ wrangler kv:namespace create "SHORTEN"
```

You should get some output back from Wrangler on what to add to your `wrangler.toml` file:

```shell
🌀  Creating namespace with title "rd-test-SHORTEN"
✨  Success!
Add the following to your configuration file:
kv_namespaces = [ 
         { binding = "SHORTEN", id = "48ae98ff404a460a87d0348bb5197595" }
]
```

In my case, I'd add that `kv_namespaces` entry to the end of my `wrangler.toml` file. The `binding` key here lets you access the KV namespace by the `SHORTEN` variable in your Worker code. If you log in to the Cloudflare Workers dashboard, you should be able to see your namespace listed under KV too:

![The namespace listed on the Cloudflare dashboard](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hlrcqnsqua9bbcdfxr9c.png)
 
Clicking "View" lets you see all associated keys and values, though it should be empty now. In your Worker's code, you can now interface with this namespace through the `SHORTEN` variable, which we'll see in a moment.

Finally, create a *preview* namespace. This will be automatically used in development (i.e. when running `wrangler dev`) when previewing your Worker:

```shell
$ wrangler kv:namespace create "SHORTEN" --preview
```

Take the `preview_id` provided in the output of this command and add it to the `wrangler.toml` file, in the same entry as your `SHORTEN` KV namespace. For example, if my output was this:

```shell
🌀  Creating namespace with title "rd-test-SHORTEN_preview"
✨  Success!
Add the following to your configuration file:
kv_namespaces = [ 
         { binding = "SHORTEN", preview_id = "d7044b5c3dde494a9baf1d3803921f1c" }
]
```

Then my `wrangler.toml` file would now have this under `kv_namespaces`:

```toml
kv_namespaces = [ 
    { binding = "SHORTEN", id = "48ae98ff404a460a87d0348bb5197595", preview_id = "d7044b5c3dde494a9baf1d3803921f1c" }
]
```

If you check the Cloudflare Workers KV section of the dashboard, you should now see two namespaces, one appended with `_preview` and one not.

## Generating slugs and creating short links

We'll define an API endpoint, `POST /links`, that takes in a JSON payload like `{ "url": "https://google.com" }`, then:

1. Generates a random, alphanumeric slug using `nanoid`
2. Saves a new entry to the `SHORTEN` KV namespace with the key as the slug in (1) and the value as the URL in the request payload
3. Returns the slug, and the short URL

To do this, open up `index.js` in your project folder. Let's import some functions we'll need from `itty-router` and `nanoid`, create a router, and set up a custom alphabet to pick our URL slugs from (the `6` ensures they're 6 characters):

```js
import { Router } from 'itty-router';
import { customAlphabet } from 'nanoid';

const router = Router();
const nanoid = customAlphabet(
  '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
  6,
);
```

Now, let's define our API endpoint and walk through what it's doing:

```js
router.post('/links', async request => {
  let slug = nanoid();
  let requestBody = await request.json();
  if ('url' in requestBody) {
    // Add slug to our KV store so it can be retrieved later:
    await SHORTEN.put(slug, requestBody.url, { expirationTtl: 86400 });
    let shortenedURL = `${new URL(request.url).origin}/${slug}`;
    let responseBody = {
      message: 'Link shortened successfully',
      slug,
      shortened: shortenedURL,
    };
    return new Response(JSON.stringify(responseBody), {
      headers: { 'content-type': 'application/json' },
      status: 200,
    });
  } else {
    return new Response("Must provide a valid URL", { status: 400 });
  }
});
```

First, we're registering a route at `/links` to accept POST requests, and consuming the `request` object passed from our fetch event the worker listens for. We're using our custom alphabet `nanoid` to generate a 6-character random slug, and then pulling the URL out of the request body. We also add this to our KV namespace we generated earlier:

```js
await SHORTEN.put(slug, requestBody.url, { expirationTtl: 86400 });
```

Notice the `SHORTEN` variable is bound to your Worker after adding it in `wrangler.toml`. Here we're setting the key to automatically expire in a day, but you can choose to set this to any value you want (or not make it expire at all!) If all goes well, we'll return the slug and the full shortened URL so the front-end can make use of it.

Go to this part of `index.js` that came with the template Wrangler used to create the project:

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

This fetch event is what's received once an HTTP request is made to your Worker. We'll modify this handler to connect to your `router` function instead:

```js
addEventListener('fetch', event => {
  event.respondWith(router.handle(event.request))
})
```

In this way, your router will be passed the `Request` object in the fetch event and can act on it, passing the request off to the correct route (like the one we defined above). Let's test it out! Run `wrangler dev` and make a test request to your Worker:

```shell
$ curl -X "POST" "http://127.0.0.1:8787/links" \
     -H 'Content-Type: application/json; charset=utf-8' \
     -d $'{
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
}'

{"message":"Link shortened successfully","slug":"TCqff7","shortened":"https://redirect.mascioni.workers.dev/TCqff7"}
```

Note that the value in `shortened` depends on your Workers subdomain and project name you chose in the beginning. If you head over to your `SHORTEN_preview` KV namespace in the Workers dashboard, you should see the entry was added too!

![The TCqff7 key now has a value for a link to everyone's favourite video](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s2y7inmzahyqzhr1gfda.png) 

You can also remove the `handleRequest` function the template comes with from `index.js`.

## Redirecting from short links to full links

The implementation for this is similar to the one for generating the short link, except this time we're calling `.get` on our KV namespace and returning a redirect. 

Let's register another route on our router, in `index.js`:

```js
router.get('/:slug', async request => {
  let link = await SHORTEN.get(request.params.slug);

  if (link) {
    return new Response(null, {
      headers: { Location: link },
      status: 301,
    });
  } else {
    return new Response('Key not found', {
      status: 404,
    });
  }
});
```

This time, we're registering a `GET /:slug` route, and making use of the `slug` parameter to fetch a value from our KV namespace. If a value exists, we'll return it with a `301` status code and set the `Location` header appropriately to do the redirect, otherwise we'll throw a `404` error.

If `wrangler dev` isn't running anymore, run it again, and now make a `GET` request from your browser to try and retrieve the URL you stored earlier! In my case, my Worker is listening for requests on port `8787`, and I saved a URL with slug `TCqff7`, so I'd go to `http://127.0.0.1:8787/TCqff7`.

## 🎉 Back-end is finished!

With that, the back-end of our app is basically done! We can now generate short URLs and redirect from them easily. In the next part of this tutorial, we'll use [Workers Sites](https://developers.cloudflare.com/workers/platform/sites) to build a front-end on top of this.

[➡️ Onward to building the front-end!](https://dev.to/mmascioni/build-a-link-shortener-with-cloudflare-workers-the-front-end-3pae)