As a developer, I occasionally need a simple URL-shortening service. I decided to finally build my own. At the end of this post be capable of building your own URL shortening service and hosting it for essentially free!
The tech stack.
- CloudFlare
- Firebase
CloudFlare.
I chose CloudFlare over Vercel for the serverless functions because of their documentation and easy routing. CloudFlare workers allow you to deploy serverless code instantly across the globe to give it exceptional performance, reliability, and scale.
Firebase.
Firebase could realistically be replaced with any JSON endpoints formatted as follows:
{ "$shortID": "$longUrl" }
The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in real time to every connected client. The primary reason for choosing this is the existing API and the ease of reading data via different URL endpoints.
Getting Started.
Log in to CloudFlare and pay a visit to your Worker's dashboard. If you’re unfamiliar with this process please check out my post on Free Custom Domains for Notion on CloudFlare. It outlines the worker process and at the same time, you can add a custom domain to your Notion page for free!
Gather Your Resources
You’ll need your
firebaseUrl
, you can this from your Firebase dashboard under the Realtime Database tab. Example: https://PROJECTID.firebaseio.com/
.To verify this URL is accurate add some data to your Realtime Database that is structured like this:
{ "$shortID": "$longUrl" }
Visiting the URL followed by your
$shortID.json
should return your $longUrl
Example:
https://PROJECTID.firebaseio.com/{$shortID}.json
Build Your Worker Script.
Replace the
"LINK NOT FOUND URL"
with your 404 page, or other link. This is just a catchall for when there is no supplied long URL. You also need to replace your firebaseUrl
with the link you just copied above. It should look similar to https://PROJECTID.firebaseio.com/
.const firebaseUrl = 'YOUR FIREBASE RTB URL'; // without .json const statusCode = 301; async function handleRequest(request) { const url = new URL(request.url); const { pathname } = url; const path = pathname.slice(1); // fetch the short url from firebase and redirec to it const response = await fetch(firebaseUrl + path + '.json'); const data = await response.json(); let redirectUrl = ""; data !== null ? redirectUrl = data : redirectUrl = "LINK NOT FOUND URL"; // Replace here too! return Response.redirect(redirectUrl, statusCode); } addEventListener('fetch', async event => { event.respondWith(handleRequest(event.request)); });
⚠️ Set Your Database Rules.
Below is a set of Firebase rules to only allow writing new data. I recommend this setup plus additional rules for authentication so only your worker script can read and provision new URLs.
{ "rules": { "$property": { ".read": true, ".write": "!data.exists()" } } }
Triggering Your Worker Script.
In your, Workers Dashboard select your worker and select triggers.
Set your route as
yourdomain.com/*
to listen on all routes for your domain.Save your triggers and head on over to your DNS settings for that domain.
Create a proxied
A
record for your domain and point it towards 192.0.2.0
Add Short URLS.
Head over to your Firebase dashboard and add some data!
Here is what setup looks like:
I threw in some random short IDs and long URLs just to test with.
Now that you have some data in your database visit
yourdomain.com/$shortID
to verify your Worker and database are setup properly. If they are you should be redirected to your long url defined in your database.A Finished Product.
For a finished example of this project you can visit Egg.Quest