Skip to content

GO Live Reload with Automatic Page Refresh

Posted on:January 27, 2024 at 09:48 AM

GO Live Reload with Automatic Page Refresh

https://github.com/dmh2000/go-live-reload

Introduction

If you are coming from the React world of web app development, you probably use ‘create-react-app’,‘vite’ or some other tool to scaffold out your application. One feature that these provide is live reload of the app when you change something in the source code. It really speeds up development.

In the Go world, you can get live reload of a web app using cosmtrek/air or one of a few other tools : https://techinscribed.com/5-ways-to-live-reloading-go-applications/

THERE IS ONE CATCH WITH GO: In the React environment, when you change code, the server will restart, AND the changes show up immediately on the browser. This is possible because a React app has a bunch of JavaScript running in the app that handle it. Its baked into the React library that is part of the app.

On the other hand, the go tools such as Air will restart your server when source code changes, but they don’t force the browser to refresh the current page. So as far as I know with any of the go approaches, you have to manually refresh the page or possibly have it refresh itself with a periodic timer (horrible?).

There is a solution that seems to work for me. By adding a bit of code, you can have REAL live reload with page refresh on code change.

Approach

  1. Setup and use cosmtrek/air to live reload the go web server in the conventional manner. https://github.com/cosmtrek/air
  2. Add a tiny piece of JavaScript into your web pages in the Head section. This script creates a simple WebSocket server that listens for a connection from a WebSocket client.

This script coould be minified in real usage. It would need to be added to any page that wanted this to work during development.

    <script>
      let active = false;
      sock = new WebSocket("ws://localhost:8080/");
      sock.onopen = function (event) {
        console.log("connected");
        active = true;
      };

      sock.onclose = function (event) {
        console.log("disconnected");
        if (active) {
          setTimeout(function () {
            location.reload();
            active = false;
          }, 2000);
        }
      };
    </script>
  1. In a separate terminal run a small WebSocket client that connects to the server running in the web page. Run this client using Nodemon which is the Node.js tool like Air that restarts a the app when source code change.
import { WebSocketServer } from "ws";

const wss = new WebSocketServer({ port: 8080 });

wss.on("open", function connection(ws) {
  console.log("open");
});

wss.on("close", function close() {
  console.log("disconnected");
});

wss.on("error", function error() {
  console.log("error");
});

What Does This Do?

Startup

Source Code Change

Once the web page reloads and the client restarts, they will reconnect and wait for the next code change.

Note: The setTimeout delay in the scripts makes the web page refresh a little smoother. Without a delay, the browser might momentarily flash a ‘site not found’ message and then load the web page. It will all work without a delay, but with it you will just see the changes appear. You can tune the delay to match your system response. For me, 1 second wasn’t enough, 2 seconds worked.

A Simple Example

Fork this repo to a working directory.

The example code

https://github.com/dmh2000/go-live-reload

Dependencies

Install the following dependencies

Setup

From root of forked repo:

go mod tidy
cd ws
npm install
cd ..

Startup

Note : cmd/main.go must be run at the top level of the project, not in cmd. It needs to access the views directory to fetch the HTML template.

Run each of these commands in separate terminals

Start the Golang web app using cosmtrek/air

# live reload of golang web app
$ air

Open the browser to see the web page.

Start the websocket app using Nodemon

Nodemon options:

# restart the local websocket server so web app will detect disconnect and reload
$ cd ws
$ nodemon --ext go,mjs,HTML --watch ../cmd --watch ../views  index.mjs

# OR
# the package.json has a start script that does the above
npm start

Test