cloudflare serverless javascript
This post is part of a learning series: Serverless Magic

Build a URL redirector application with Cloudflare Workers and Cloudflare D1

Posted by Vikash Patel on Sunday, Jan 7, 2024 (Updated Sunday, Jan 7, 2024) Reading time: 7 Min

Lorbic.com - Cloudflare Workers and D1 database used to build a simple url redirector app.
Clopudflare Workers and D1 database - lorbic.com

In today’s fast-paced digital world, where attention spans are short, optimizing website performance and user experience is crucial. Imagine asking someone to visit a lengthy URL (https://lorbic.com/setting-up-llama-2-with-docker-on-your-gaming-laptop-using-ollama/) – their reaction might be a disappointed face (🙁). Typing such URLs can lead to errors, resulting in users landing on a dreaded 404 page.

To address this, we’ll build a simple and powerful solution – a URL redirector application using Cloudflare Workers and D1 database. This application will not only shorten your links but also provide a seamless and error-free redirection feature.

For the sake of simplicity, we’ll keep the set of features to a minimum and add whatever necessary at some later time.

Main features:

  • Create a short code for a URL.
  • Redirect users to the original link.

Understanding Cloudflare Workers:

Cloudflare Workers allow developers to run code at the edge of the Cloudflare network, enabling the execution of custom logic for HTTP requests. This allows for enhanced performance and reduced latency, as the code runs closer to the end-users.

That’s what Workers are, basically intercepting requests before they reach your origin server. This allows for lightning-fast redirects based on URL paths, query parameters, headers, and even cookies. You can put workers between USER and ORIGIN SERVER. Then handle the request accordingly.

Here’s how Workers handle redirects:

  • Interception: The Worker intercepts incoming requests based on your chosen route configuration.
  • Rule Matching: The Worker analyzes the request against your predefined rules, stored in JavaScript code, or you can connect Cloudflare KV, Cloudflare D1 or other database via Cloudflare Hyperdrive and get the configuration.
  • Redirection Magic: If a rule matches, the Worker constructs a redirect response, specifying the new target URL and status code (e.g., 301 for permanent move, 302 for temporary).
  • Speedy Delivery: The redirect response is instantly delivered back to the user, bypassing the origin server for optimal performance.

Prerequisites:

Before diving into the implementation, ensure you have the following:

  • Node JS
  • A Cloudflare account.
  • A domain configured with Cloudflare.
  • Cloudflare Wrangler CLI (check here)
  • Familiarity with JavaScript, Node.js, and ExpressJS (we’ll use HonoJS, similar to ExpressJS).
  • Install Hono js.

Create Cloudflare D1 Database

Login to Cloudflare Wrangler CLI by running the following command and allow Cloudflare permissions.

wrangler login
Create a D1 database

Run the command below to create a database. Change with your actual database name.

wrangler d1 create <my-db-name>

This returns following response, with details of your database.
Paste these into your wrangler.toml

[[d1_databases]]
binding = "DB" # i.e. available in your Worker on env.DB
database_name = "<my-db-name>"
database_id = "5283redacted23-4a04-456b-af3f-redacted"
Create database schema

We will keep it very simple with just 1 table and 3 columns in the table. Here is the command to create the database table. Note: you’ll have to paste the database configuration in wrangler.toml and have to be inside the project directory.

wrangler d1 execute DB  \
  --command "CREATE TABLE IF NOT EXISTS urls (short_code TEXT PRIMARY KEY, created_at INTEGER, url TEXT);"

Columns:

  • short_code: A string column to store the short code associated with the URL.
  • url: A string column to store the full URL.
  • created_at: An integer column to store the Unix timestamp representing when the URL was created.

Check your database by running the following command:

wrangler d1 execute DB \
 --command "select * from urls"

Create Cloudflare Worker

Go to this page and create the Cloudflare worker. Or simple install Wrangler CLI and run following command to initialize a project.

Change <project-name> with actual name. (I’ve chosen lrbc).

npx wrangler init <project-name>

Select, “hello world” example in the list and chose other options accordingly.

Now go to the lrbc directory (your project directory).

cd lrbc

Now open the wrangler.toml file in the root of your directory. Add the following code to connect your Cloudflare D1 database. If you do not have it set-up check the Create Cloudflare D1 Database of this blog.

Replace the name of your database and database id with actual values. The binding is how you will refer to this configuration in your worker code.

[[d1_databases]]
binding = "DB"
database_name = "<YOUR_DATABASE_NAME>"
database_id = "<YOUR_DATABASE_ID>"
Install Honojs:

Install hono js by running following code npm install --save hono.

The Code:

Next, open the src/index.js file and add the following code. It uses Hono JS. Hono is a tiny and super fast framework for creating web application and it is very easy to integrate with Cloudflare Workers. It’s feels native.

import { Hono } from 'hono';

const app = new Hono();

app.get('/:code', async (c) => {
    try {
        const code = c.req.param('code');
        console.log(code);
        const { results } = await c.env.DB.prepare('SELECT url FROM urls WHERE short_code = ?').bind(code).all();

        if (results.length > 0) {
            const websiteUrl = results[0].url;
            console.log('Redirecting to ' + websiteUrl);
            return c.redirect(websiteUrl);
        } else {
            console.log('Short code not found');
            return c.json({ status: 404,  error: 'Short code not found' });
        }
    } 
    catch (e) {
        console.log('Error fetching URL:', e);
        return c.json({ status: 500, error: 'Failed to fetch URL' });
    }
});


export default app;

The provided code is written in JavaScript and uses the Hono library. Let’s break down its key components:

  1. Route Handling:

    The code defines a route using the ‘app.get’ method, which responds to incoming HTTP GET requests with a specific parameter in the URL.

    app.get('/:code', async (c) => {
      // Code logic goes here
    });
    
  2. Database Query:

    Inside the route handler, the code attempts to fetch the full URL associated with the provided short code from a database. Notice, the DB in the code c.env.DB . This is the binding we’ve defined in wrangler.toml file. Here c is the context of Hono. Everything related request and response is stored in this object.

    const code = c.req.param('code');
    const { results } = await c.env.DB.prepare('SELECT url FROM urls WHERE short_code = ?').bind(code).all();
    
  3. URL Redirection or Error Handling:

    Based on the query results, the code decides whether to redirect the user to the corresponding website URL or return a JSON response with an error message.

    if (results.length > 0) {
        // Redirect to the full website URL
        return c.redirect(websiteUrl);
    } else {
        // Short code not found, return a JSON response
        return c.json({ status: 404, error: 'Short code not found' });
    }
    
  4. Error Handling:

    The code includes basic error handling, logging any encountered errors and returning an appropriate JSON response. You can check these logs in the Workers dashboard. And by running wrangler tail

    } catch (e) {
        console.log('Error fetching URL:', e);
        return c.json({ status: 500, error: 'Failed to fetch URL' });
    }
    

Deploy your worker:

You can deploy your worker by running just one command wrangler deploy. It will create a .workers.dev URL.

 wrangler deploy

Create Short URLs:

You can either create the URLs by running a following code and directly inserting them into your D1 database.

app.post('/store', async (c) => {
	try {
		// get the long url from json payload in request body
		const { url } = await c.req.json();

		// Generate a random id of length = NANOID_SIZE
		const shortCode = nanoid();
		const created_at = Date.now();

		console.log(shortCode);
    // Validation
    if (!shortCode || !url) {
      return c.json({status: 400,  error: 'Missing short code or URL' });
		}

		// Save the url and
		const { results } = await c.env.DB.prepare("INSERT INTO urls (short_code, url, created_at) VALUES (?, ?, ?)").bind(shortCode, url, created_at).run();

		return c.json({ message: `URL stored successfully. short: ${shortCode}, url: ${url}` });
  } catch (e) {
    console.error('Error storing URL:', e);
    return c.json({ status: 500, error: 'Failed to store URL. There is some error or the code already exists.' });
  }
});

What’s Next

  • Create a GitHub repository and store this project there.
  • Extend the functionality by adding the short URL creation feature.
  • Integrate this worker (project) into your existing website and create a share functionality, so when someone clicks the share button they get a short URL.
  • Extend the database schema and add a hash of URL, so creating existing URLs will return the short existing code instead of creating new code, reducing the number of duplicate URLs.

If you have any question, write it down in the comments below. Your response really matters 😅.

Share it with your friends. Have a nice Sunday. Cheers ✌



comments powered by Disqus