May 8,2025

Today I’m going to try build Docker containers for a URL shortener application named Uturn-Go. Credits to Anirudh Rowjee for the Go application.

https://github.com/anirudhRowjee/uturn-go#

Understanding the URL shortener

This was surprisingly pretty straightforward .The application - main.go:

package main

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

// Jo: Creating a data structure to hold the URL and Shortcode (if we were using Redis it'd be the Key:Value)
type UrlStruct struct {
	gorm.Model
	Shortcode string `json:"shortcode"`
	URL       string `json:"url"`
}

var shortcode_sample_space = []rune("abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ0123456789")
var shortcode_sample_space_length = len(shortcode_sample_space)

func generate_random_shortcode(length int) string {

	new_shortcode := make([]rune, length)
	for i := range new_shortcode {
		new_shortcode[i] = shortcode_sample_space[rand.Int63n(int64(shortcode_sample_space_length))]
	}
	return string(new_shortcode)
}

func main() {

	// seed the random number generator
	rand.Seed(time.Now().UnixNano())
	fmt.Println("Random text >> ", generate_random_shortcode(10))

	// Test static data
	var store_static = []UrlStruct{
		{Shortcode: "abc", URL: "<https://www.google.com>"},
		{Shortcode: "def", URL: "<https://www.amazon.com>"},
		{Shortcode: "ghi", URL: "<https://www.twitter.com>"},
		{Shortcode: "lmno", URL: "<https://www.facebook.com>"},
	}
	fmt.Println(len(store_static))

	// Adding database connectivity
	// Jo: Using SQLite as the database for this example
	db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
	if err != nil {
		panic("Could not open Database!")
	}

	// Automigrate the schema
	db.AutoMigrate(&UrlStruct{})

	// Jo: Starting a new Gin router
	r := gin.Default()

	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})

	r.GET("/urls", func(c *gin.Context) {

		// get all the URLs in JSON.
		var results []UrlStruct
		db.Find(&results)
		c.JSON(200, results)

	})

	r.POST("/urls", func(c *gin.Context) {

		var currentUrl UrlStruct

		// parse request body to get the proposed URL Object
		//Jo: We're taking the input body parameters and binding them to the new struct
		if err := c.BindJSON(&currentUrl); err != nil {
			fmt.Println("Error ! Could not read JSON Formatting!")
			c.JSON(400, gin.H{
				"Error": "Improperly shaped JSON request body",
			})
		}

		//Jo: Generate shortcode for the URL
		if currentUrl.Shortcode == "" {
			currentUrl.Shortcode = generate_random_shortcode(5)
		}

		// insert this into the database
		result := db.Create(&currentUrl)
		if result.Error != nil {
			c.JSON(400, gin.H{
				"Error":  "Could not insert into database",
				"Reason": result.Error,
			})
		}

		// send a success confirmation
		c.JSON(200, currentUrl)
	})

	r.GET("/:shortcode", func(c *gin.Context) {

		// get the shortcode from the URL Body
		shortcode, success := c.Params.Get("shortcode")
		if success == false {
			c.JSON(404, gin.H{
				"Error": "Could not parse URL",
			})
		}

		// fetch the shortcode from the database
		current_url := UrlStruct{Shortcode: shortcode}
		db.Where("shortcode = ?", shortcode).First(&current_url)

		fmt.Println("Current URL Fetched: ", current_url)

		// send a redirect to there
		c.Redirect(301, current_url.URL)
	})

	r.Run(":8000")
}

<aside>

[]rune → its an alias for int32 OR a 32 bit long data type used to store Unicode code points (wider range than ASCII, including emojis, etc.)

</aside>

<aside>

GORM is a ORM(Object Relational Mapper) in Go. Helps you work with SQLite databases. Gin is a web framework allowing you to create REST APIs for the application (https://echo.labstack.com/ is an alternative to Gin)

</aside>

<aside>

fmt is to print to console.

</aside>

image.png

I ran the POST request using Postman and tada! I can access the url at https://localhost:8000/sTi2Q

image.png

Little bit about the database- SQLite

image.png

Working with Docker

How is a Dockerfile constructed?

General steps to build a Dockerfile