Browser hot-reloading for Go

Software engineers coming from a Javascript/Typescript background are accustomed to having their web apps auto-reload when they save a change in their code.
Unfortunately in Go there is no such functionality and you need to build it yourself.
Fortunately this is not a lot of work.
I’m using Gofiber (https://github.com/gofiber/fiber) in the code below, but it doesn’t really matter what you use to serve content.
Step 0) Use air to recompile your go code when changed
I suppose you already do this but if you don’t, please do ;-)
Step 1) A hot reload route in your app
Add a /hot_reload route to your app that serves a unique ID that changes when the server is restarted:
import (
"log"
"github.com/dchest/uniuri"
"github.com/gofiber/fiber/v2"
)
func main() {
app := fiber.New()
webServerRunID := uniuri.New()
app.Get("/hot_reload", func(c *fiber.Ctx) error {
return c.Send([]byte(webServerRunID))
})
log.Fatal(app.Listen(":3000"))
}
Step 2) Some Javascript that monitors the hot reload route
<script>
var lastWebServerRunID = undefined;
function hotReloadCheck() {
fetch('/hot_reload')
.then(response => {
if (response.status == 200) {
return response.text();
}
})
.then(webServerRunID => {
if (lastWebServerRunID == undefined) {
lastWebServerRunID = webServerRunID;
} else {
if (webServerRunID != "" && webServerRunID != undefined) {
if (lastWebServerRunID != webServerRunID) {
console.log("Reloading website...");
window.location.reload();
}
}
}
})
.catch((reason) => {
// Do nothing
})
.finally(() => {
setTimeout(hotReloadCheck, 500);
});
}
hotReloadCheck();
</script>
Step 3) (optional) Avoid 502 while restarting
If you run your code behind a reverse proxy you might get some nasty 502 errors while air is reloading your app. You can get rid of those by using the below nginx trick:
upstream backend {
server localhost:3000 max_fails=1 fail_timeout=1s;
server localhost:3001 backup;
}
server {
server_name example.com;
listen 443 ssl;
listen [::]:443 ssl;
ssl_certificate /etc/ssl/example.crt;
ssl_certificate_key /etc/ssl/example.key;
location / {
proxy_pass http://backend;
proxy_redirect http://backend http://$host$request_uri;
proxy_set_header Host $host;
}
}
server {
# This is our backup service which always says: 204 No Content
listen 3001;
listen [::]:3001;
return 204;
}
That’s it. It will take 2–3 seconds for the page to reload as there’s a lot of things going on behind (recompiling the code, waiting 0.5s for the hot reload, waiting 1s in nginx for the backend timeout) but this setup is infinitely better than having to reload the page manually.
Have fun!
Adrian
Originally published on Medium, 5 November 2022. Original.