Building a URL Shortener Service using Golang

July 6, 2024 4 min read

Go LanguageSystem DesignWeb Development

This tutorial will show how to build a URL shortener similar to TinyURL, covering both the design and implementation aspects.

Technologies

  • Golang

  • Gin Framework

  • GORM

  • Redis

  • PostgreSQL

High-level design

API Endpoints

API endpoints enable communication between clients and servers. We will design the APIs following the REST architecture. If you’re not familiar with RESTful APIs, you can refer to external resources, such as restfulapi. A URL shortener mainly requires two API endpoints.

  1. URL shortening: To generate a new short URL, the client makes a POST request that includes a single parameter: the original long URL.

  2. URL redirecting: To redirect a short URL to its associated long URL, the client issues a GET request.

URL shortening

To support the URL shortening use case, we have to find a hash function that maps a long URL to the short URL. We are assuming the hash table stores <shortURL, longURL> pairs.

URL redirecting

The API supports two status codes for redirects: 301 (permanent redirect) and 302 (temporary redirect):

  • 301 redirect: A 301 redirect indicates that the requested URL has been “permanently” moved to the long URL. Because of the permanent nature of the redirect, the browser caches the response, and future requests for the same URL bypass the URL shortening service, directing traffic straight to the long URL’s server.

  • 302 redirect: A 302 redirect indicates that the URL has been “temporarily” moved to the long URL. As a result, future requests for the same URL will continue to go through the URL shortening service first before being redirected to the long URL’s server.

To reduce the server load, 301 redirect is a good choice when only the first request of the same URL is sent to URL shortening service.

Low-level design

Hash algorithm

As with the normal hash algorithm, we will have some collisions. Each time of this, it is expensive to query the database to check if a shortURL exists for every query. A common approach used for this is base62 conversion. Base62 conversion is used as there are 62 possible characters [0-9, a-z, A-Z] for hashValue. The example below to illustrates how the conversion works:

I decided to use the Twitter snowflake approach to generate a unique ID as a input of hash function. I’ll explain the reasons for this choice in a future blog post to avoid making this one too lengthy.

URL shortening flow

As one of the core pieces of the system, I build the diagram below to demonstrate the flow.

  1. Verify if the longURL is present in the cache and return the corresponding shortURL. If it’s not in the cache, check the database, store it in the cache, and return it to the user. If it’s still not found, treat the longURL as new and proceed to step 2

  2. Generate a new unique ID using the Twitter Snowflake algorithm and use it as input for the Base62 function. Then proceed to step 3

  3. Store the <shortURL, longURL\> mapping in the database and cache, then provide the new shortURL to the user

URL redirecting flow

  1. A user accesses shortURL

  2. The load balancer forwards the request to web servers

  3. If a shortURL already in the cache, return the longURL directly

  4. If a shortURL is not in the cache. fetch the longURL from the database

  5. Return 301 status code with location longURL to the user

Development

Step 1: Set up the project

Open your terminal and create a new directory for the project, then init a Go module to manage dependencies

Step 2: Define the URL struct

We create a URL struct to manage the mapping between shortURL and longURL

Step 3: Implement URL Shortening

We created a GenerateShortURL function that processes POST requests, generates a unique short URL, and returns a JSON response to the user.

Step 4: Implement URL redirecting

We created a RedirectShortURL function that handles GET requests, takes a short URL as a parameter and redirects to the corresponding long URL if it exists.

Notes:

  • The caching method is managed within the service layer for each function call to it.

  • This code above is just for illustration, the real code may be differ than that. You can get full source code here

Conclusion

That is it for this article. I hope you found this article useful, if you need any help please let me know by directly inbox.

Let's connect on Twitter and LinkedIn.

👋 Thanks for reading, See you next time