The personal weblog of Martin Donath, professional web developer, technology ninja and design enthusiast based in Cologne, Germany.

Building Web Applications in Golang With Gulp and LiveReload

Gulp and LiveReload are two awesome tools that greatly ease web application development by streamlining the build process.

Gulp is a modern build system designed around the idea of streaming a set of input files down a pipeline of tasks for automated processing, e.g. concatenation, sourcemap generation, minification, revisioning and many more. LiveReload automatically reloads your application when detecting changes to the source and consists of two parts: a client, implemented as a browser extension (available for Chrome, Firefox, and more) and a server counterpart (we’ll use gulp-livereload, an elegant Gulp integration).

There are many tutorials on setting this up to work with Node.js projects (for starters see here, here and here), but nothing on getting it to work with compiled languages like Golang, so I thought I share my setup.

The Problem

Developing web applications in an interpreted language like Javascript or Python, we’re spoilt children - the source is re-interpreted on every request, so we change a line of code here and there, reload the page and voilá - there it is. Instantly. Not so with compiled languages. Add a missing quote in a template: rebuild, restart, reload. Insert a debug statement into a controller: rebuild, restart, reload. Remove the debug statement - guess what - rebuild, restart and reload. Every time.

At the time of writing, there are two tools that try to solve the problem of rebuilding and restarting Go web applications upon changes: Gin and Fresh. Gin acts as a proxy to your application and monitors *.go files for changes. Fresh monitors every file within your project. While the former misses changes in templates and configuration files, the latter may crash because of too many open file descriptors. Furthermore, none of them comes with LiveReload integration.

For the rest of this article we assume that you are familiar with the basic concepts of Gulp, so we can concentrate on smoothening the build process with Golang.

The Tools

Gulp

Gulp is a modern, stream-oriented build system. A set of input files is passed down a pipeline of tasks, e.g. stylesheets written in SASS are passed to the libsass compiler, an Autoprefixer, Media Query merger and Minifier, and finally written to disk. Gulp is very efficient and customizable at monitoring files for changes and will automatically start the associated task when it detects a change in a file.

LiveReload

LiveReload is a browser extensions for Chrome and Firefox that tells your browser to reload assets or whole pages upon notification of the server. Gulp is tightly integrated with LiveReload through gulp-livereload. Stylesheets are reloaded without a full page refresh, enabling instant preview.

The Setup

We need to define three tasks: the first will rebuild the server, the second will terminate the currently running server process and respawn it, the third will watch for changes in source files to trigger a restart, or a rebuild and a restart. In Golang, the build step can easily be accomplished by invoking go install:

1
2
3
4
5
6
/*
 * Build application server.
 */
gulp.task('server:build', function() {
  return child.spawnSync('go', ['install']);
});

The build task is performed synchronously via spawnSync (see documentation here), as we have to wait for the build to complete before restarting the application server. The spawn task checks if the server is currently running, terminates it by sending SIGTERM to the child process and restarts it. When the server sends the first log output (ideally something like Listening on 127.0.0.1:3000), a reload is triggered and the browser will refresh instantly. Go compiles your application server down to a single binary, so put the name of your binary in line 9:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * Restart application server.
 */
gulp.task('server:spawn', function() {
  if (server)
    server.kill();

  /* Spawn application server */
  server = child.spawn('your-server-binary-goes-here');

  /* Trigger reload upon server start */
  server.stdout.once('data', function() {
    reload.reload('/');
  });

  /* Pretty print server log output */
  server.stdout.on('data', function(data) {
    var lines = data.toString().split('\n')
    for (var l in lines)
      if (lines[l].length)
        util.log(lines[l]);
  });

  /* Print errors to stdout */
  server.stderr.on('data', function(data) {
    process.stdout.write(data.toString());
  });
});

Finally, the watch task watches for changes in source files and performs either of two actions upon the detection of a change:

  1. It triggers server:respawn if the changed files do not require a rebuild, but are loaded upon application start, e.g. changes in templates, configuration or locales.
  2. It triggers server:rebuild and after completion server:respawn if the source files demand a rebuild of the application. Obviously, this can not be parallelized.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * Watch source for changes and restart application server.
 */
gulp.task('server:watch', function() {

  /* Restart application server */
  gulp.watch([
    '.views/**/*.tmpl',
    'config/*.json',
    'locales/*.json'
  ], ['server:spawn']);

  /* Rebuild and restart application server */
  gulp.watch([
    '*/**/*.go',
  ], sync([
    'server:build',
    'server:spawn'
  ], 'server'));
});

You can add files, directories or glob patterns to the watchdogs, tailoring the watch task to fit your web application’s needs.

Conclusion

Gulp and LiveReload are great technologies that ease web application development by automating the rebuild-restart-reload cycle. While this article showed how to integrate those tools with Golang, it can easily be extended to other languages that require a build step.

For a complete Gulp setup including asset compilation, compression and revisioning, as well as view minification and the discussed automatic application server rebuild and restart, see this Gist.

Comments