DEV Community

Cover image for Can you please refresh (Or how we version our Single-Page Application)

Can you please refresh (Or how we version our Single-Page Application)

Ante Sepic on December 03, 2019

In this article, I outline our approach to solving the problem of people not "getting" the latest version of our SPA. A single-page application ...
Collapse
 
willvincent profile image
Will Vincent • Edited

We have netlify hit a route on our backend that then in turn sends a special message via MQTT to all currently active users, which causes an "An update is ready, please click to load the new version" message to appear at the bottom of their screen.

Works great, super painless thanks to that netlify webhook

Collapse
 
originalexe profile image
Ante Sepic

Nice. Did you already have the backend -> frontend communication set up, or did you add it specifically for the purpose of the update notice?

As mentioned in the article, it seemed overkill to us to add a completely new communication system just for this purpose.

Collapse
 
gauravvermamaropost profile image
gaurav-verma-maropost

How did you solve the issue of excluding the index.html file from service-worker for having the new index.html all the time directly from the server and not form service-workers or disk cache. Also if possible could you share the function isNewerVersionAvailable()

Collapse
 
originalexe profile image
Ante Sepic • Edited

Hi,

This app did not make use of service workers, so we did not have to implement any workarounds. Since the headers on Cloudfront were set so that html files were never cached, there were never any cache-related problems.

Also if possible could you share the function isNewerVersionAvailable()

I have since changed jobs so I no longer have access to the source code, but the logic was something like:

let oldSource = null;

async function isNewerVersionAvailable() {
  const freshSource = await fetch(...);

  if (oldSource === null) {
    oldSource = freshSource;
    return false;
  }

  if (oldSource === freshSource) {
    return false;
  }

  oldSource = freshSource;
  return true;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dillon profile image
dillon

Was the edge-case of the first request being a new version considered? Seems like a user could potentially miss out on a notification for an entire version if their first page load is within the 10 minute interval of a new version being deployed.

I suppose a workaround could be just setting oldSource ASAP after first page load which cuts the edge-case window down drastically.

Collapse
 
gauravvermamaropost profile image
gaurav-verma-maropost

Thanks. Just one question why didn't you considered calling some other smaller file's response headers time stamp by fetching that one. That could have saved some data per request.

Thread Thread
 
originalexe profile image
Ante Sepic

The fact is that our HTML file was already rather small (2.57kb gzip), so while fetching for example just file headers would have been less expensive in terms of bandwidth, I think we were rather happy with that approach. If I were to do it again, I would test the headers approach and go with that if it was equally reliable.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen

I liked your considerations and approach. But isn't this is something that is already solved by a service worker? Events for update notifications and triggering of updates.

Collapse
 
originalexe profile image
Ante Sepic • Edited

Hey Lars, thanks for the feedback.

As far as I am aware, a service worker checks for the new version availability on page load, but we needed the checks for people who were idle, or who were just browsing the app without refreshing.

Additionally, even if it would technically do what we needed it to, we were not making use of the service worker in that particular spa, so adding an additional script just for the update detection mechanism did not seem worth it.

Lastly, we also support IE11 on our app, so that was taken into consideration as well.

Collapse
 
layzee profile image
Lars Gyrup Brink Nielsen • Edited

I see. What about this issue, how are you going to handle that?

Without service workers, users can load one tab to your site, then later open another. This can result in two versions of your site running at the same time. Sometimes this is ok, but if you're dealing with storage you can easily end up with two tabs having very different opinions on how their shared storage should be managed. This can result in errors, or worse, data loss.
From developers.google.com/web/fundamen...

Registered service workers have an update method to check for updates manually.

Thread Thread
 
originalexe profile image
Ante Sepic • Edited

Actually, in this case, we actually prefer to notify the user about the update in every tab.
It would be bad to not show the notification in every tab (we ideally don't want users to run the old version in some of them), but we also can't refresh all tabs at once (some of them might have data that the user does not want to lose).

I hope that makes sense, but let me know if I am missing your point.

Collapse
 
testarossaaaaa profile image
Testarossa

What do you do with problematic extensions that modifies content on the fly? Users will see a 'Reload to update' notification every tick of the timer. We are currently facing this problem.

Collapse
 
originalexe profile image
Ante Sepic

Hi,

We did not have any reports of this. How are you fetching the HTML? Are you relying on innerHTML initially? Most of the extensions will mess with that, but not with the response of the network request itself, so if you only rely on what fetch gives you, I believe you should be fine in most cases.

If that's still a problem, you could rely on the "last-modified" header instead. We serve this app via S3 + CloudFront, and it sends the last-modified header. You can then access it and compare that instead of the whole HTML.