Serving an SPA with Go's "Echo"

Replacing the (update: not any more unmaintained) Gorilla/mux framework with Echo in 👍 Get Feedback! was easy and straight forward, I did not hat to change very much. But one problem is handled differently: redirecting all paths (that are not requesting static resources and are not API endpoints) interally to index.html, so that the single page application works in the browser – in my case a client side React (or Preact) app.

My Go application serves the front end resources (index.html, images, styles and JavaScript) with a static file server from a directory in the same container. The resources are not embedded in the binary.

What I did with Gorilla/mux (more or less, this is inlined and shortened):

muxRouter.MatcherFunc(func(r *http.Request, match *mux.RouteMatch) bool {
    return ShouldReturnIndex(r.URL.Path) || fileIsServable(r.URL.Path)
}).HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
    if ShouldReturnIndex(request.URL.Path) {
        request.URL.Path = "/"
    }
    fileServer.ServeHTTP(writer, request)
}).Methods("GET", "HEAD")

What this does, is: If the path matches a static resource (fileIsServable) or matches a pattern that should be redirected to index.html, a handler function is called which is rewriting the request URL to /, if it matches a pattern to redirect and serves the file (if /, this would be index.html) with the static file server, which is an http handler: http.FileServer(http.Dir(httpRoot)).

With Echo, it is not possible to change the url path in the same way. There are several approaches solving this with additional libraries:

A single binary SPA using Go
A single binary SPA using Go - Using statik and echo with to serve up a single binary React app
One of the coolest features of Go. Embed ReactJS into a binary with Go
Today we’re going to attempt to embed a React Application into a Go binary. Please watch the youtube…

While I like the idea of embedding the front end resources into the binary, for now I just wanted to replace the http routing library, without adding any more libraries and without restructuring my application.

The easiest way for me was just redirecting internally with the Echo rewrite middleware for certain paths and using Echo's static server to serve the front end resources:

e := echo.New()
e.Pre(middleware.Rewrite(map[string]string{
    "^/pathprefix/*": "/",
}))
e.Static("/", fileServer.httpRoot)