Gin Framework

How to make Go Web a bit more developer-friendly and more performant
Share this page:

About Gin Web framework for Go

Gin is a HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance – up to 40 times faster.

Why are we using Gin? Gin is a framework that contains a set of commonly used functionalities, e.g. routing, middleware support, rendering, which help reduce boilerplate code that would normally go into building the applications. It also lends itself very well to creating reusable and extensible pieces of code. Aside from the better benchmarked performance, just look at how clean and readable the code for API GET, POST etc is:

 router := gin.Default()

 router.GET("/someGet", getting)
 router.POST("/somePost", posting)
 router.PUT("/somePut", putting)
 router.DELETE("/someDelete", deleting)
 router.PATCH("/somePatch", patching)
 router.HEAD("/someHead", head)
 router.OPTIONS("/someOptions", options)

 router.Run()

Gin does it’s magic by allowing you to write middleware that can be plugged into one or more request handlers or groups of request handlers.

Code

Control flow of a typical web app looks like this:

  • Gin first parses the route.
  • If a matching route definition is found, Gin invokes the route handler.
  • Middleware may be also invoked.

In Unicorn Pursuit, we will make use of the following functionalities:

  • Routing — to handle various URLs.
  • Custom rendering — to handle the response format.
  • Middleware — to handle authentication using AWS Cognito.

Reusable Templates

Unicorn Pursuit Git repo is structured as follows: Tree shown below:

unicorn
├── iac
│   ├── cdk.out
│   └── iac
│       └── __pycache__
└── templates

Go HTML Templates are in the template folder. The header, the footer and the menu will be the common pieces that will be reused across all templates.

If you focus on the index.html, check out how it imports the templates:

<!--index.html-->

{{ template "header.html" .}}

<img src="https://s3-prod-unicorn.s3-eu-west-1.amazonaws.com/unicorn+description.png" alt="Unicorn Description" style="width:700px;height:450px;">

  <!--Loop over the `payload` variable, which is the list of projects-->
  {{range .payload }}
    <!--Create the link for the project based on its ID-->
    <a href="/project/view/{{.ID}}">
      <!--Display the title of the project -->
      <h2>Idea: {{.Title}}</h2>
    </a>
    <!--Display the content of the project-->
    <p>Description: {{.Content}}</p>
  {{end}}

{{ template "footer.html" .}}

And now, check out the template folder:

templates
├── about.html
├── create-project.html
├── footer.html
├── header.html
├── index.html
├── leaderboard.html
├── login-successful.html
├── login.html
├── menu.html
├── otp.html
├── project.html
├── register.html
└── submission-successful.html

Routing

Entire Routing function is handled by the router.go package.

In Unicorn Pursuit, we are using 3 groups of routes:

  • /u/ for User Related routes (Login, Register, Logout, One Time Password)
  • /g/ for General Routes (About Unicorn, Documentation, Leaderboard)
  • /project/ for Project Related Routes (Create, Modify, View projects)

We copied this code, and some other parts, from a blog by Semaphore, cited in the reference. I’m not sure who wrote their code, but it was awesome.

For example, the following code:

  • routes everyone who clicks on “About” to the /g/about.html, handled by showAboutPage function.
  • routes everyone who is logged in (by using middleware), and clicks on “Leaderboard”, to the /g/leaderboard handled by showLeaderboard function.
 //Group Global routes (About, Leaderboard)
 globalRoutes := router.Group("/g")
 {
  // Handle the GET requests at /g/about, visible to anyone
  globalRoutes.GET("/about", showAboutPage)

  // Handle POST requests at /g/leaderboard
   // Ensure that the user is logged in by using the middleware
  globalRoutes.GET("/leaderboard", ensureLoggedIn(), showLeaderboardPage)
 }

Now that all this is clear, check out the beauty of main.go package. It doesn’t implement any functions, it just:

  • Creaters a Router in Gin.
  • Loads the templates.
func main() {
 // Set Gin to production mode
 gin.SetMode(gin.ReleaseMode)

 var StaticContent string

 StaticContent = "templates/*"

 // Set the router as the default one provided by Gin
 router = gin.Default()

 // Process the templates at the start so that they don't have to be loaded
 // from the disk again. This makes serving HTML pages very fast.
 router.LoadHTMLGlob(StaticContent)

 // Initialize the routes
 initializeRoutes()

 // Start serving the application, default port in Gin is 8080
 router.Run()
}

Middleware

Unicorn Pursuit has a very simple middleware, all in middleware.auth.go package.

Middleware is a piece of code that can be executed at any stage while handling an HTTP request. It is typically used to encapsulate common functionality that you want to apply to multiple routes.

We want to make sure that Leaderboard and Voting for Projects is available only to logged in users. Some other options, like “Log In” or “Register” route, need to be only available to the ones who are not logged in. Here is how Middleware handles this using setUserStatus function that verifies token to set is_logged_in boolean:

func setUserStatus() gin.HandlerFunc {
 return func(c *gin.Context) {
  if token, err := c.Cookie("token"); err == nil || token != "" {
   c.Set("is_logged_in", true)
  } else {
   c.Set("is_logged_in", false)
  }
 }
}

ensureLoggedIn and ensureNotLoggedIn functions just make sure if the value of the is_logged_in is as expected.

Where to find more info




Last modified May 23, 2020: AddingUnicornRunning (f6c1e6a)