How to build a URL Shortener with Go
Tags: go, backenddevelopment, programming, url
Published: 2023-08-11 21:16:14

Say you've created a Google doc or saw a link to a video and want to share it with your friend, but the link needs to be shorter. That's where URL shorteners help. Some even go a step further and allow you to customise the link.

I often shorten and customise my links using [bitly](https://bit.ly/).

In my [previous article](https://dev.to/envitab/ekeminis-tech-journey-10an), I shared that I'll focus on two things - _Go for backend development and Technical writing._ This is to show I'm committed. What better way to learn than to apply and teach?

We'll be building a _simple_ URL shortener with Go :smiley:, my fave programming language.

Shall we?... Yes!


## Prerequisites 

- Go installed on your system, download [here](https://go.dev/doc/install)
- A code editor - try [VS Code](https://code.visualstudio.com/download)
- The willingness to be a [gopher](https://go.dev/blog/gopher) :grin: 

### Design 

The URL shortener will be a basic web application that handles [`HTTP requests`](https://www.ibm.com/docs/en/cics-ts/5.3?topic=protocol-http-requests#:~:text=An%20HTTP%20request%20is%20made,needed%20to%20access%20the%20resource), shortens URLs, and redirects users to the original URLs using unique shortened keys. We will us an [in-memory](https://hackernoon.com/in-memory-caching-in-golang) map to store the relationships between the shortened keys and original URLs.

## The Build Flow

Let's start by writing the code for our URL shortener. We'll break down the implementation into the following steps:

### Step 1: Set up the Project

Open your termial and create a new directory for the project, then initialize a [Go module](https://go.dev/blog/using-go-modules) to manage dependencies.

```bash
mkdir go-url-short

cd go-url-short

go mod init go-url-short
```

`go mod init` is to create a new `go.mod` file, which will be a module in the project folder.


![snip of go.mod file in VS code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ibrdr4ljjrmrwc5ewiup.png)

You can open up the folder from VS Code, or while in your terminal, enter `code .`

### Step 2: Import the Packages

Go is all about packages - they're the core of every Go program.
Create a filed named `main.go` in the project directory, and enter the code below:

```go
package main

import (
    "fmt"
    "math/rand"
    "net/http"
    "time"
)
```

In this initial step, we import the necessary packages for our Go program. We're using `fmt` for formatting and printing, `math/rand` for generating random keys, `net/http` for handling HTTP requests, and `time` for seeding the random number generator.

### Step 3: Define the URL Shortener Struct

```go
type URLShortener struct {
    urls map{string}string
}
```

We create a `URLShortener` struct to manage the mapping between original URLs and their shortened versions. This struct will have a `urls` field, which is a map with shortened keys as keys and original URLs as values.


### Step 4: Implement URL Shortening

```go
func (us *URLShortener) HandleShorten(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
		return
	}

	originalURL := r.FormValue("url")
	if originalURL == "" {
		http.Error(w, "URL parameter is missing", http.StatusBadRequest)
		return
	}

	// Generate a unique shortened key for the original URL
	shortKey := generateShortKey()
	us.urls[shortKey] = originalURL

	// Construct the full shortened URL
	shortenedURL := fmt.Sprintf("http://localhost:8080/short/%s", shortKey)

	// Render the HTML response with the shortened URL
	w.Header().Set("Content-Type", "text/html")
	responseHTML := fmt.Sprintf(`
		<h2>URL Shortener</h2>
		<p>Original URL: %s</p>
		<p>Shortened URL: <a href="%s">%s</a></p>
		<form method="post" action="/shorten">
			<input type="text" name="url" placeholder="Enter a URL">
			<input type="submit" value="Shorten">
		</form>
	`, originalURL, shortenedURL, shortenedURL)
	fmt.Fprintf(w, responseHTML)
}

```

Here, we define a `HandleShorten` method for the `URLShortener` struct. This method handles POST requests, validates the input URL, generates a unique short key, and displays the original and shortened URLs in an HTML response along with an input form for users to enter more URLs.

### Step 5: Implement URL Redirection

```go
func (us *URLShortener) HandleRedirect(w http.ResponseWriter, r *http.Request) {
	shortKey := r.URL.Path[len("/short/"):]
	if shortKey == "" {
		http.Error(w, "Shortened key is missing", http.StatusBadRequest)
		return
	}

	// Retrieve the original URL from the `urls` map using the shortened key
	originalURL, found := us.urls[shortKey]
	if !found {
		http.Error(w, "Shortened key not found", http.StatusNotFound)
		return
	}

	// Redirect the user to the original URL
	http.Redirect(w, r, originalURL, http.StatusMovedPermanently)
}

```

### Step 6: Generate Short Keys

```go
func generateShortKey() string {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	const keyLength = 6

	rand.Seed(time.Now().UnixNano())
	shortKey := make([]byte, keyLength)
	for i := range shortKey {
		shortKey[i] = charset[rand.Intn(len(charset))]
	}
	return string(shortKey)
}
```

We create a `generateShortKey` function to generate unique short keys for the original URLs. This function generates a random alphanumeric key of length 6 characters, ensuring the uniqueness of the keys.

### Step 7: Main Function and Server Setup

```go
func main() {
	shortener := &URLShortener{
		urls: make(map[string]string),
	}

	http.HandleFunc("/shorten", shortener.HandleShorten)
	http.HandleFunc("/short/", shortener.HandleRedirect)

	fmt.Println("URL Shortener is running on :8080")
	http.ListenAndServe(":8080", nil)
}

```

In the `main` function, we create an instance of the `URLShortener` struct and set up the HTTP handlers for URL shortening and redirection. We listen on port `8081`, and the server starts running.

## Running the URL Shortener

To run the URL shortener, follow these steps:

- Save the provided Go code in a file named `main.go`.
- Open a terminal or use the one in your code editor, an navigate to the directory where the `main.go` file is located.
- Run this `go build` command:
```go
go build -o go-url-short main.go
```
The `-o` flag is used to specify the output binary's name, which is named `go-url-short`.


![server is running](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ucrg8j87qfj8ysibe915.png)

- Open a web browser and go to the URL:
```go
http://localhost:8081

```

- You should see the URL shortener web page with an input form. Enter a long URL, click "Shorten," and the page will display the original and shortened URLs.

> Here's a GIF that shows the app running 

![GIF showing the app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rdagbcdx9017twkaqms.gif)

The full code can be accessed on this [GitHub repo.](https://github.com/Tabintel/go-url-short)

## Conclusion

Building a URL shortener with Go is a great way to learn about web development, HTTP handling, and working with data structures. Our simple implementation includes the essentials: URL shortening, redirection, and a basic HTML form for user interaction. 

This project can be a starting point for enhancing the user interface, adding analytics, and exploring more advanced features.

Happy coding! :computer: