DEV Community

TekniksMila
TekniksMila

Posted on

Building a URL Shortener in Go: A Step-by-Step Guide

Introduction:

  • URL shortening is a technique used used to convert lengthy and complex URLs into shorter user-friendly versions. These shortened URLs are preferably advantageous compared to the original URLs, since they reduce visual complexity of the original URLs making them easier to read and remember thus promoting user engagement and click-through rates. Additionally, shortened URLs can be easily shared on spaces where available space is limited.
  • In this article I am going to walk you through the process of building a URL shortener in Go.

Prerequisites:

  • Basic understanding of Go, HTTP servers and web development concepts.
  • You must have Go installed in your computer system. If you do not have Go installed, first navigate to link to install Go.

Creating Project Directory and files:

  1. Open terminal, create a project directory and name it "URL-Shortener".
  2. Navigate inside the "URL-Shortener" directory.
  3. Initialize a new Go module.
  4. Create a main.go file. Make sure the commands are entered line by line as shown below.
mkdir URL-Shortener
cd URL-Shortener
git mod init url-shortener
code main.go
Enter fullscreen mode Exit fullscreen mode

Writing the Go code:

  • Open the main.go file. This is where you will write the code.
  • Start off by declaring the main package. Import the necessary packages for running the program. "fmt" for formatting and printing, "math/rand" is for random number generation, "net/http" is for HTTP server functionality, "strings" is for string manipulation, and "time" for time-related functions.
package main
import (
    "fmt"
    "math/rand"
    "net/http"
    "strings"
    "time"
)
Enter fullscreen mode Exit fullscreen mode
  • Create a global variable "ShortURLs" which is a map that will store the shortened URLs of type string as key, and their corresponding original URLs of type string as value.
// ShortURLs is a map that stores shortened URLs with their corresponding original URLs.
var shortURLs = make(map[string]string)
Enter fullscreen mode Exit fullscreen mode
  • The main function sets up HTTP request handlers for different endpoints and starts the HTTP server on port 3030. The http.handleFunc() is used to map HTTP request paths to corresponding handler functions.
func main() {
    // Set up HTTP request handlers
    http.HandleFunc("/", handleForm)           //Handle root endpoint
    http.HandleFunc("/shorten", handleShorten) // Handle URL shortening endpoint
    http.HandleFunc("/short/", handleRedirect) // Handle redirecting to original URL

    // Start HTTP server
    fmt.Println("URL Shortener running on: 3030")
    http.ListenAndServe(":3030", nil)
}
Enter fullscreen mode Exit fullscreen mode
  • The handleForm function is the handler function for GET requests to the root endpoint ("/"). It displays an HTML form for submitting a URL that needs to be shortened.
// handleForm handles GET requests to the root endpoint and displays the URL shortening form
func handleForm(w http.ResponseWriter, r *http.Request) {
    if r.Method == http.MethodPost {
        http.Redirect(w, r, "/shorten", http.StatusSeeOther)
        return
    }

    w.Header().Set("Content-type", "text/html")
    fmt.Fprint(w, `
        <!DOCTYPE html>
        <html>
        <head>
            <title> URL-Shortener</title>
        </head>
        <body>
            <h2>URL-Shortener</h2>
            <form method= "post" action="/shorten">
                <input type="url" name="url" placeholder="Enter a URL" required>
                <input type="submit" value="Shorten">
            </form> 
        </body>
        </html>
    `)
}
Enter fullscreen mode Exit fullscreen mode
  • The handleShorten function is the handler for POST requests to the "/shorten" endpoint. It generates a shortened URL, stores the URL and its original counterpart in a map and renders an HTML response that displays both URLs.
// handleShorten handles POST requests to the /shorten endpoint and generates a shortened URL
func handleShorten(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
        return
    }

    // Get the original URL from the form
    originalURL := r.FormValue("url")
    if originalURL == "" {
        http.Error(w, "URL parameter is missing", http.StatusBadRequest)
        return
    }

    // Generate a short key and store the original URL
    shortKey := generateShortKey()
    shortURLs[shortKey] = originalURL

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

    // Render HTML response with original and shortened URLs
    w.Header().Set("Content-Type", "text/html")
    fmt.Fprint(w, `
        <!DOCTYPE html>
        <html>
        <head>
            <title> URL-Shortener</title>
        </head>
        <body>
            <h2>URL-Shortener</h2>
            <p>Original URL: `, originalURL, `</p>
            <p>Shortened URL: <a href="`, shortenedURL, `">`, shortenedURL, `</a></p>
        </body>
        </html>
        `)
}
Enter fullscreen mode Exit fullscreen mode
  • handleRedirect handles requests to shortened URLS ("/short"). It extracts the short key from the request path, looks up the original URL, and redirects the client to the original URL.
// handleRedirect handles requests to shortened URLs and redirects to the original URL.
func handleRedirect(w http.ResponseWriter, r *http.Request) {
    // Extract the short key from the request path
    shortKey := strings.TrimPrefix(r.URL.Path, "/short/")
    if shortKey == "" {
        http.Error(w, "Shortened key is missing", http.StatusBadRequest)
        return
    }

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

    // Redirect to the orginal URL
    http.Redirect(w, r, originalURL, http.StatusMovedPermanently)
}
Enter fullscreen mode Exit fullscreen mode
  • The generateShortKey function is responsible for generating a short random key for URL shortening. It uses a predefined character set and a key length of 6 characters to create the short key.
// generateShortKey generates a short random key for URL shortening
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)
}
Enter fullscreen mode Exit fullscreen mode

Executing the URL Shortener Program

  • Save the main.go file.
  • Run the program by typing on the terminal the command below:
go run main.go
Enter fullscreen mode Exit fullscreen mode
  • It will display that the URL Shortener is running on port 3030.
URL Shortener running on: 3030
Enter fullscreen mode Exit fullscreen mode
  • Open your browser and on the address bar type http://localhost:3030 then search.
  • Once the new page loads, enter the URL you want to shorten in the text field, and click the "shorten" button.
  • Find the shortened URL displayed on the screen.

Resources.

  • You can find the whole working code on my Github profile. Here is the link
  • Also find a short video demonstration showing the end result on Twitter. Here is the link

Top comments (0)