Building a URL Shortener with Go and a HTML Frontend

ยท

4 min read

Building a URL Shortener with Go and a HTML Frontend

In this tutorial, I'll build a URL shortener application with a backend written in Go and a simple HTML frontend. This project will demonstrate how to create, store, and retrieve short URLs, as well as how to serve static files using Go's net/http package.

Prerequisites

  • Basic understanding of Go programming

  • Go installed on your machine

  • Basic knowledge of HTML and JavaScript

Project Structure

We'll organize our project as follows:

/url-shortener
    /static
        index.html
    main.go

Step 1: Setting Up the Backend

First, let's create our Go backend in main.go. This file will handle URL shortening and redirection.

Define the URL Struct and In-Memory Database

We'll start by defining a URL struct to store information about the URLs and an in-memory map to act as our database.

package main

import (
    "crypto/md5"
    "encoding/hex"
    "encoding/json"
    "errors"
    "fmt"
    "net/http"
    "time"
)

type URL struct {
    ID           string    `json:"id"`
    OrignalURL   string    `json:"orignal_url"`
    ShortURL     string    `json:"short_url"`
    CreationDate time.Time `json:"creation_date"`
}

var urlDB = make(map[string]URL)

URL Shortening Function

Next, we'll create a function to generate a short URL using the MD5 hashing algorithm.

func generateShortURL(OrignalURL string) string {
    hasher := md5.New()
    hasher.Write([]byte(OrignalURL))
    hash := hex.EncodeToString(hasher.Sum(nil))
    return hash[:8]
}

Create and Retrieve URL Functions

We'll add functions to create a new short URL and retrieve an original URL from the short URL.

func createURL(orignalURL string) string {
    shortURL := generateShortURL(orignalURL)
    id := shortURL
    urlDB[id] = URL{
        ID:           id,
        OrignalURL:   orignalURL,
        ShortURL:     shortURL,
        CreationDate: time.Now(),
    }
    return shortURL
}

func getURL(id string) (URL, error) {
    url, ok := urlDB[id]
    if !ok {
        return URL{}, errors.New("URL not found")
    }
    return url, nil
}

HTTP Handlers

We'll set up HTTP handlers to manage the URL shortening and redirection.

func rootPageURL(w http.ResponseWriter, r *http.Request) {
    http.ServeFile(w, r, "./static/index.html")
}

func ShortURLHandler(w http.ResponseWriter, r *http.Request) {
    var data struct {
        URL string `json:"url"`
    }
    err := json.NewDecoder(r.Body).Decode(&data)
    if err != nil {
        http.Error(w, "Invalid Request body", http.StatusBadRequest)
        return
    }

    shortURL_ := createURL(data.URL)
    response := struct {
        ShortURL string `json:"short_url"`
    }{ShortURL: shortURL_}

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

func redirectURLhandler(w http.ResponseWriter, r *http.Request) {
    id := r.URL.Path[len("/redirect/"):]
    if id == "" {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }
    url, err := getURL(id)
    if err != nil {
        http.Error(w, "URL not found", http.StatusNotFound)
        return
    }
    http.Redirect(w, r, url.OrignalURL, http.StatusFound)
}

Main Function

Finally, we'll update the main function to start the HTTP server and handle static files.

func main() {
    http.HandleFunc("/", rootPageURL)
    http.HandleFunc("/shorten", ShortURLHandler)
    http.HandleFunc("/redirect/", redirectURLhandler)

    // Serve static files
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))

    fmt.Println("Starting server on Port 5000...")
    err := http.ListenAndServe(":5000", nil)
    if err != nil {
        fmt.Println("Error running the server:", err)
    }
}

Step 2: Creating the Frontend

Next, let's create the frontend in static/index.html. This HTML file will provide a form for users to input their URLs and display the shortened URLs.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>URL Shortener</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
        }
        .container {
            background: #fff;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            width: 300px;
            text-align: center;
        }
        .container h1 {
            margin-bottom: 20px;
        }
        .container input {
            width: 100%;
            padding: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .container button {
            width: 100%;
            padding: 10px;
            background-color: #28a745;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .container button:hover {
            background-color: #218838;
        }
        .short-url {
            margin-top: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>URL Shortener</h1>
        <input type="text" id="urlInput" placeholder="Enter URL here">
        <button onclick="shortenURL()">Shorten URL</button>
        <div class="short-url" id="shortUrlContainer"></div>
    </div>

    <script>
        async function shortenURL() {
            const url = document.getElementById('urlInput').value;
            const response = await fetch('/shorten', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ url })
            });
            const data = await response.json();
            const shortUrlContainer = document.getElementById('shortUrlContainer');
            if (response.ok) {
                const shortUrl = `${window.location.origin}/redirect/${data.short_url}`;
                shortUrlContainer.innerHTML = `<p>Short URL: <a href="${shortUrl}" target="_blank">${shortUrl}</a></p>`;
            } else {
                shortUrlContainer.innerHTML = `<p>Error: ${data.error}</p>`;
            }
        }
    </script>
</body>
</html>

Running the Project

  1. Place theindex.html file in the static directory.

  2. Run the Go server:

     go run main.go
    
  3. Open your web browser and navigate tohttp://localhost:5000 to access the frontend.

Conclusion

In this tutorial, i've built a simple URL shortener with a Go backend and an HTML frontend. This project demonstrates how to create, store, and retrieve short URLs, as well as how to serve static files using Go's net/http package. You can expand this project by adding features like persistent storage with a database, user authentication, and analytics.

I believe this blog will offer some value, providing unique insights and sharing new and engaging ideas. ๐Ÿ™

๐Ÿ˜Š Your support means a lot, so stay tuned for more!

Happy Learning ๐Ÿ˜Š

ย