Build a Link Shortener with Cloudflare Workers: The front-end

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.

If you haven't gone through the back-end 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 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 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:

[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 into your public/static directory.

Afterwards, copy the index.html file from the finished project 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):

<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 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:

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:

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

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

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:

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

to:

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:

$ 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:

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 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!

17