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
Place the
index.html
file in thestatic
directory.Run the Go server:
go run main.go
Open your web browser and navigate to
http://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 ๐