A solution to serving static files from an embedded file system in Go using Fiber web framework

I really like using Fiber but sometimes have to find solutions for problems that could be solved in a somewhat straightforward way using other frameworks like gin or go-chi. Anyways, I do enjoy trying to find solutions even though they sometimes feel hacky.

UPDATE: So it seems I missed the memo and didn't know that the Fiber team already had the functionality in v2. So please see this page for more on THE RIGHT WAY

Serving files from embed.FS in Fiber not straightfoward

One of the problems that I ran into is serving static assets from an embedded file system or embed.FS. While Fiber does allow for serving static assets it only allows you to pass the path to a directory which is read from the "real" FileSystem.

This may not be ideal as deploying the program now requires both the binary and the files to be placed/uploaded on a server (NOTE: we are assuming we aren't deploying using containers which could solve this problem easily).

What is embed.FS anyways?

The embed package, a feature of Go since sometime in Go 1.16, enables us to pack files in the binary - this is useful as we can just ship the binary and not worry about forgetting to copy & upload the right files. For more information on //go:embed see this blog post on the JetBrains GoLand blog.

So we want to be able to serve files that are embedded in the Go program itself. How can we do that?

Using rebed to work with embedded file system alongside Fiber

Enter rebed; a library that enables us to read and write the contents of the embed.FS. We can use rebed to write the embedded files to a directory and let Fiber serve them straight from that directory. This gives us the benefit of embedding while also enabling us to have the files on the file system where they can be served via an another web-server like NGINX.

Here is how we could go about that:

package main

import (
    "embed"

    "github.com/gofiber/fiber/v2"
    "github.com/soypat/rebed"
)

// Let's assume you an app created by the likes of create-react-app
// which leaves it's built files in frontend/dist

//go:embed frontend/dist
var assets embed.FS

func main() {
    app := fiber.New()
    // I prefer this form with www parent directory
    // as it avoids overwriting the existing frontend/dist directory
    // if you happen to run the program in the same working directory
    rebed.Write(assets, "www")
    app.Static("/", "www/frontend/dist")
    err := app.Listen(":3000")
    if err != nil {
        panic(err)
    }
}

I feel the only icky part of this is that in your app.Static call you have to pass the full path of the embedded file system, e.g. note www/frontend/dist in the example, as well as the fact that you may have to clean up at some point if you don't want that directory to be on the server during re-deploy or something.

Can we have this another way?

~The other option I considered is to send a PR to the Fiber project to modify how the Static function handles files but I don't think it will get accepted as that may also require changing how fasthttp (the http library beneath Fiber) serves Static files.~

~So for now, you can either copy the files you want to serve manually or embed them and let rebed recreate them for you when the program is running or find another clever way maybe using Fiber's adaptor package somehow.~

UPDATE: Use the right way

Thanks for reading.