How to force client reload after deployment?

Andrew picture Andrew · Mar 27, 2015 · Viewed 16.9k times · Source

I'm using the MEAN stack (mongo, express, angular and node). I'm deploying relatively frequently to production...every couple of days. My concern is that I'm changing the client side code and the API at times and I would rather not have to ensure backwards compatibility of the API with previous versions of the client code.

In such a scenario, what is the most effective way of ensuring that all clients reload when I push to production? I have seen that Evernote for example has a pop-up that says something along the lines of please reload your browser for the latest version of Evernote. I would like to do something similiar...do I need to go down the path of socket.io or sock.js or am I missing something simple and there is a simpler way to achieve this?

Answer

Marco Lüthy picture Marco Lüthy · Mar 27, 2015

Update: AppCache was deprecated summer 2015 so the below is no longer the best solution. The new recommendation is to use Service Workers instead. However, Service Workers are currently still experimental with sketchy (read: probably no) support in IE and Safari.

Alternatively, many build tools now seamlessly incorporate cache-busting and file "versioning" techniques to address OPs question. WebPack is arguably the current leader in this space.


This might be a good use case for using HTML5's AppCache

You'd probably want to automate some of these steps into your deployment scripts, but here is some code you might find useful to get you started.

First, create your appcache manifest file. This will also allow you to cache resources in the client's browser until you explicitly modify the appcache manifest file's date.

/app.appcache:

CACHE MANIFEST

#v20150327.114142

CACHE:
/appcache.js
/an/image.jpg
/a/javascript/file.js
http://some.resource.com/a/css/file.css

NETWORK:
*
/

In app.appcache, the comment on line #v20150327.114142 is how we indicate to the browser that the manifest has changed and resources should be reloaded. It can be anything, really, as long as the file will look different to the browser from the previous version. During deployment of new code in your application, this line should be modified. Could also use a build ID instead.

Second, on any pages you want to use the appcache, modify the header tag as such:

<html manifest="/app.appcache"></html>

Finally, you'll need to add some Javascript to check the appcache for any changes, and if there are, do something about it. Here's an Angular module. For this answer, here's a vanilla example:

appcache.js:

window.applicationCache.addEventListener('updateready', function(e) {
    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
        // Browser downloaded a new app cache.
        // Swap it in and reload the page to get the latest hotness.
        window.applicationCache.swapCache();
        if (confirm('A new version of the application is available. Would you like to load it?')) {
            window.location.reload();
        }
    }
    else {
        // Manifest didn't changed. Don't do anything.
    }
}, false);

Alternatively, if AppCache won't work for your situation, a more ghetto solution would be to create a simple API endpoint that returns the current build ID or last deployment date-time. Your Angular application occasionally hits this endpoint and compares the result to it's internal version, and if different, reloads itself.

Or, you may consider a live-reload script (example), but, while very helpful in development, I'm not sure how good of an idea it is to use live/in-place-reloading of assets in production.