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

In this part of the tutorial, we'll build a front-end atop our back-end to let users input a long link and get a short link back. Our front-end will be a simple Vue application that just interfaces with our `POST /links` endpoint, and will be served up using [Workers Sites](https://developers.cloudflare.com/workers/platform/sites).

If you haven't gone through the [back-end](https://dev.to/mmascioni/build-a-link-shortener-with-cloudflare-workers-the-back-end-35bd) part of this tutorial yet to set up the routes this front-end depends on, head back to that part first!

## How Workers Sites works

In the back-end part of this tutorial, we used [Workers KV](https://developers.cloudflare.com/workers/learning/how-kv-works) to store key-value pairs of slugs for long URLs. What Workers Sites allows you to do is automatically upload your site's static content to a KV namespace as well, where it can be retrieved and displayed by your Worker. 

These assets are stored in another namespace that Wrangler can create for you, and your Worker can retrieve using the [kv-asset-handler](https://github.com/cloudflare/kv-asset-handler) package. In our Worker code, we can grab the correct asset depending on the request it receives. 

To get started, in your project directory, install the kv-asset-handler package: `npm i @cloudflare/kv-asset-handler`

## A bit of home renovation first

To make this work, we'll need to re-structure our project folder a bit:

1. Move the `index.js`, `package.json` and `package-lock.json` from the project root into their own folder, which we'll call `workers-site`. 

2. Create a `public` directory in your project root, with a `static` subdirectory in it.

3. Modify your `wrangler.toml` file to add this section at the bottom:

```toml
[site]
bucket = "./public"
entry-point = "workers-site"
```

Going forward, Wrangler will now upload your static assets in `public` to their own KV namespace.

At the end of these steps, your folder structure should look something like this (assuming the project root is called `redirect`):

```
redirect
| wrangler.toml
└───public
    └───static
└───workers-site
    └───index.js
    └───package.json
    └───package-lock.json
```

## Creating the Vue application

First, copy the stylesheet from the [finished project](https://github.com/mm/cf-shortener/blob/main/public/static/style.css) into your `public/static` directory. 

Afterwards, copy the `index.html` file from the [finished project](https://github.com/mm/cf-shortener/blob/main/public/index.html) directly into the `public` folder. This tutorial won't focus on the specifics of Vue too much, but let's explore what the code is doing. Looking at this section of the code (line 16-32):

```html
<form @submit.prevent="handleUrlSubmit">
    <input
    type="text"
    id="input-url"
    name="url"
    size="40"
    placeholder="https://google.com"
    required
    v-model="longUrl"
    />
    <input type="submit" id="input-submit" value="Shorten" />
</form>

<div class="message" v-if="shortUrl">
    <p>Your new shortened URL is:</p>
    <h2>{{ shortUrl }}</h2>
</div>
```

First, we've created a [data binding](https://v3.vuejs.org/guide/forms.html#form-input-bindings) on our form inputs using the `v-model` directive. Whenever the input box for the URL is updated, the `longUrl` data property will be updated.

We register an event listener for the `submit` event on this form. The `handleUrlSubmit` method we define will take care of interacting with the endpoint we defined before, and will update the `shortUrl` data property. Once this is available, the URL will be displayed to the user (visibility toggled by the `v-if` directive).

Taking a look at the `handleUrlSubmit` method on the Vue app:

```js
methods: {
    handleUrlSubmit() {
        fetch('/links', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            url: this.longUrl,
        }),
        })
        .then((response) => {
            if (response.status == 200) {
                this.error = false;
                return response.json();
            } else {
                throw new Error('Issue saving URL');
            }
        })
        .then((data) => {
            this.shortUrl = data.shortened;
        })
        .catch((error) => {
            this.error = true;
        });
    },
}
```

Here we're doing a `fetch` request (with very little error handling) to our `/links` endpoint, and retrieving the `shortened` value from the API response. The `shortUrl` data property gets set to this value, and the shortened URL is displayed to the user.

## Using `kv-asset-handler` to render our application

There are two scenarios where we'd want to render static assets:

- A user visits the homepage (i.e. the path is `/`)
- A static asset is requested (e.g `/static/style.css`)

To intercept these requests, while still responding to requests to our API endpoints, we can define a middleware function. This would either pass the fetch event to the router or `kv-asset-handler`'s `getAssetFromKV` function, which consumes a `FetchEvent` and returns a `Response` based on what's stored in the KV namespace for static assets.

Open up `index.js`. First, import the `getAssetFromKV` function:

```js
import { getAssetFromKV } from '@cloudflare/kv-asset-handler';
```

Then, let's write our function to pass the event/request around:

```js
async function handleEvent(event) {
  let requestUrl = new URL(event.request.url);
  if (requestUrl.pathname === '/' || requestUrl.pathname.includes('static')) {
    return await getAssetFromKV(event);
  } else {
    return await router.handle(event.request);
  }
}
```

Note that our route handlers currently take in a `Request` object, while the `getAssetFromKV` function takes in the whole `FetchEvent`. Next, let's hook into this function on our fetch event listener. Modify the listener from:

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

to:

```js
addEventListener('fetch', event => {
  event.respondWith(handleEvent(event));
});
```

With these changes made, it's time to take the Worker for a test spin! Run `wrangler dev`. Notice you'll get a bit of extra output as your static assets get populated into their own KV namespace:

```shell
$ wrangler dev

🌀  Using namespace for Workers Site "__redirect-workers_sites_assets_preview"
✨  Success
👂  Listening on http://127.0.0.1:8787
```

And, you should be able to see it in action:

![Redirecting to our favourite video](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g61spnwnuufm719x9z8d.png)

Note your URL might look a little different. If you now take this key and append it to the URL in your address bar (e.g `127.0.0.1:8787/nFXAau` for me), it should redirect! Our routing has been set up properly.

If you peek at your KV namespace for the static assets in your dashboard, you should see them listed:

![The static assets in their own namespace](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6kv22hv5sh5ohls1weaz.png)
 

## 🎉 The front-end is ready to rock!

The front-end is ready to go and now it's time to deploy our application with Wrangler. In the next part of this tutorial we'll deploy the link shortener -- we're almost there!

[➡️ Onward to deploying the application!](https://dev.to/mmascioni/build-a-link-shortener-with-cloudflare-workers-deployment-23be)