Gin-Gonic#
Gin-Gonic is a Web framework for Go that I have chosen for a little side project (that keeps growing, but this is another storyπ)
Gin is an HTTP web framework written in Go (Golang). It features a Martini-like API with much better performance – up to 40 times faster. If you need smashing performance, get yourself some Gin.
One decision for my project was that I don’t want to write a dedicated front end but keep it old school and rather simple. Enter Server-side rendering (SSR) π
Gin has a nice support for HTML rendering and it was quite straight forward to work with (maybe because I was used to writing {{}}
from Helm files π)
Rendering HTML#
A Hello World with Gin rendered HTML templates looks like as follows.
index.tmpl
<html>
<h1>{{ .title }}</h1>
</html>
main.go
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.LoadHTMLFiles("index.tmpl")
router.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Hello Gin!",
})
})
router.Run(":8080")
}
Run the example with the following commands:
go mod init hello-gin
go mod tidy
go run .
You can visit http://localhost:8080
in your browser and should see your Hello Gin!
To build our application and distribute it to our server we run go build
.
This command will output a single binary hello-gin
in your project’s directory. For demonstrating purpose, move this file to another folder and run it:
mv hello-gin /tmp
cd /tmp
./hello-gin
Now, when you visit http://localhost:8080
you will see an empty screen and your application will throw an error like this
Obviously, Gin cannot find the template file which is only in our project’s directory together with the source files. By loading a template file I included an external dependency for my application. For me, this is a major drawback as I cannot emphasize the simplicity of only having to deal with a single binary. Therefore, I took some time and researched ways to bundle everything my Gin application requires in one single binary.
In the next chapter, I will to describe two methods to include your templates for rendering HTML as well as all the static assets like CSS, JS, you want to deliver from your Gin application.
Embed#
Before we continue we need to add new files to our application. We want to do some styling with CSS, implement a new route in addition to /index and serve a favicon.
βββ assets
βΒ Β βββ style.css
βΒ Β βββ favicon.png
βββ go.mod
βββ go.sum
βββ main.go
βββ templates
βββ index.tmpl
βββ ping.tmpl
Since version 1.16 Golang supports embed which basically allows you to create and include a file system (with files) which we will utilize for our goal.
Creating a new file system is very easy. You just add the go:embed
directive before a variable declaration.
.
and ..
in your directive. So you cannot include files files from parent directories just from the current directory or subdirectories!//go:embed assets templates
var embeddedFiles embed.FS
This results in the following structure of the embedded file system:
βββ assets
βΒ Β βββ style.css
βΒ Β βββ favicon.png
βββ templates
βββ index.tmpl
βββ ping.tmpl
HTML templates#
Now we can use the created file system to provide the templates to our Gin router:
templ := template.Must(template.New("").ParseFS(embeddedFiles, "templates/*"))
router.SetHTMLTemplate(templ)
Static files#
Providing static files like css, js, and images is even easier. Keep in mind
router.StaticFS("/public", http.FS(embeddedFiles))
We can reference our assets in our HTML templates like this
<link rel="stylesheet" href="/public/assets/style.css" />
Favicon#
Favicon has a special role since it should be served under the root. To accomplish this, I create a dedicated route that returns a FileFromFS
.
router.GET("/favicon.png", func(c *gin.Context) {
c.FileFromFS(".", FaviconFS())
})
FaviconFS
is a helper function that only return the subpath of the favicon itself:
func FaviconFS() http.FileSystem {
sub, err := fs.Sub(embeddedFiles, "assets/favicon.png")
if err != nil {
panic(err)
}
return http.FS(sub)
}
Now let’s build out app, move it to a different folder, and run it:
go build
mv hello-gin /tmp
cd /tmp
./hello-gin
When we open http://localhost:8080/
we see our functional web page. To confirm that all resources are loaded we can have a look at our browser’s dev tools.
Wrap up#
I was really surprised how easy it is to included static files in your Go binary and keep the build and the distribution so simple and straight forward. - Gin is also quite handy when you are building your Gin-gonic powered app. With Gin you can live reload your application by just running gin --appPort 8080 run