Golang Embedding
Go introduced embedding into the core language in version 1.16
which provides a way to embed files directly within the binary during compilation via a directive.
This article will demonstrate how to leverage this feature with a practical example.
Here's the standard library docs: Go Embedding
The key benefit to using embeds is being able to bundle assets at compile time from the package source code tree which can then be accessed at runtime. This allows for not having to access / be dependent on the file system. Now days, code will often be distributed, containerized, and even ran serverless and bundling any essential assets within the binary itself could be worthwile.
Let's illustrate a simple example of where an issue could arrise:
package main
import (
"os"
)
func main() {
config, err := os.Open("test.config")
if err != nil {
panic(err)
}
defer config.Close()
}
Here we are dependent on a test.config
file that is a necessary dependency. Being dependent on the file system could cause issues where the current working directory is different from the source code directory.
If for instance, this file were to be mounted as a volume after the binary boots up, a panic would occur:
panic: open test.config: no such file or directory
.
There are many cases where having configuration files like in this example would make more sense as an external dependency as it can easily be altered being dynamic in nature.
There are also many cases where the assets are static and bundling them within the binary itself can simplify the deployment process by not having to rely on any external dependencies.
Some examples include:
- Templated html files
- Truly static config files
- Documentation
- Media assets such as images / audio
Practical Go Embedding Example
Here is a practical example of how to leverage go embeds.
Let's say we want to deploy a simple web server. The static html
we want to expose resides in an index.html
file like so:
<!DOCTYPE html>
<html>
<head>
<title>Go Embedding</title>
</head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
We can place this index file within a templates
directory.
We can then leverage the embed
package from Go's standard library to import this template via a go directive.
Then parse all html files via the template
package, create an HTTP handler to serve the HTML template, and finally run the server on port 8080
.
package main
import (
"embed"
"net/http"
"text/template"
)
//go:embed templates/*
var content embed.FS
func main() {
tmpl, err := template.ParseFS(content, "templates/*.html")
if err != nil {
panic(err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
tmpl.ExecuteTemplate(w, "index.html", nil)
})
// Start the server
http.ListenAndServe(":8080", nil)
}
Via go:embed templates/*
the Go compiler will embed all files within the templates
directory. It's just that simple with only 1 line of detail!
The binary now does not require any external dependencies as the html
file is all bundled within the binary itself.
Hope this gives a good starting point on go embeds and how to leverage this feature! 😊