Skip to main content

One file web application in Golang

With the version 1.16 we have a way to include in the binary an arbitrary files at compile time using embed standard package.

Package embed provides access to files embedded in the running Go program.

That feature is very useful in order to create a one-file web application.

There are many libs provides that way, for example: pkger, go.rice go-bindata.

In this post, I'll describe how to do it without external dependencies.

The main goal is to have a file that we can run with no additional dependencies and a runtime environment.

There are many applications built in the same way, for examplee:

Let's embed the directory embed with two files inside: index.html and styles.css.

<html>
	<head>
		<title>Title</title>
		<link rel="stylesheet" href="styles.css">
	</head>
	<body>
		<h1>This is a header</h1>
		<h1>The body has lightblue color</h1>
		<hr>
	</body>
</html>
body {
  background-color: lightblue;
}

h1 {
  color: navy;
  margin-left: 20px;
}

I have created a simple main.go, it looks as follows:

package main

import (
	"fmt"
	"net/http"

	"embed"

	"github.com/gorilla/mux"
	"github.com/rs/cors"
)

//go:embed embed/*

var content embed.FS

func main() {
	r := &Router{&mux.Router{}}

	r.MustResponse("GET", "/", func(res http.ResponseWriter, req *http.Request) {
		data, _ := content.ReadFile("embed/index.html")
		res.Header().Set("Content-Type", "text/html")
		fmt.Fprint(res, string(data))
	})

	r.MustResponse("GET", "/styles.css", func(res http.ResponseWriter, req *http.Request) {
		data, _ := content.ReadFile("embed/styles.css")
		res.Header().Set("Content-Type", "text/css")
		fmt.Fprint(res, string(data))
	})

	r.Run(":8080", "*")
}

type Router struct {
	*mux.Router
}

func (r *Router) MustResponse(meth, path string, h http.HandlerFunc) {
	r.HandleFunc(path, h).Methods(meth)
}

func (r *Router) Run(address, origins string) {
	c := cors.New(cors.Options{
		AllowedOrigins:   []string{origins},
		AllowedMethods:   []string{"POST", "GET", "OPTIONS", "PUT", "DELETE"},
		AllowedHeaders:   []string{"Accept", "Content-Type", "If-None-Match", "Content-Length", "Accept-Encoding", "Authorization"},
		AllowCredentials: true,
	})

	handler := c.Handler(r)
	http.ListenAndServe(address, handler)
}

func vars(req *http.Request) map[string]string {
	return mux.Vars(req)
}

Compile code with command go build main.go and run it ./main. Open browser at address http://localhost:8080 you'll see a page like below:

An example image rendered from golang source code

Currently, we have one executable file with index.html and styles.css embedded in the binary.

Feel free to play around. For example, you can include some rich js frameworks like Nuxt.

Happy coding!