Create a never offline web app!
Tags: pwa, javascript, firebase, webdev
Published: 2019-07-15 14:36:16

Welcome  to the fifth article about progressive web apps. :raising_hand:
If you read from [the first post](https://dev.to/paco_ita/a-gentle-introduction-to-progressive-web-apps-step-1-24da) of this series, you should have now a solid base, even if PWAs were completely unknown. 
Terms like `service workers`, `web app manifest`, `cache API` should be familiar to you. In the previous articles we discovered different tools we can use to easily build a PWA from scratch. Therefore probably you already started upgrading your existing web application with the learnt progressive features.

This is already a step forward for offering an improved experience to our users. However there is something else we can do to further leverage progressive web apps capabilities and bring them to a next level. I will show you that nowadays, with modern technologies, we can provide a never offline application to our users.

So grab a coffee, make yourself comfortable and let's start!!

![start](https://media.giphy.com/media/l0IyjiXOXTX6Yemsg/giphy.gif)
&nbsp;

## Limits of the Cache API
We previously learnt that the `Cache API` allows to cache only `GET Requests`, but no [`POST nor PUT`](https://w3c.github.io/ServiceWorker/#cache-put)  are currently possible.
If you try to cache a request other than a `GET` you will receive the following error: `TypeError: Invalid request method POST.` (here in case we submitted a POST).

So our web app still works offline, it allows to navigate and read content from it, but it offers just a *passive* experience. Even though this is perfectly fine in many scenarios, it would be great if we could offer full CRUD (Create, Read, Update, Delete) functions even offline.

To achieve this, it is necessary to develop custom solutions to overcome to this limitation. For example we can detect when the client is offline and, in that case, store in the **Indexed DB** the values the user inputted in forms and attempted to post to the server.
Then, when the user's network connectivity is restored, our app must push all the pending changes. We have also to plan how to handle exceptions if a POST fails and how this should affect the other cached  requests.

Another possibility is given by `Cloud Firestore`.
&nbsp;

##Cloud Firestore
![firestore-logo](https://thepracticaldev.s3.amazonaws.com/i/9uhu47s2cfwho07z1dbc.png)

> Cloud Firestore is a flexible, scalable NoSQL database for mobile, web, and server development from Firebase and Google Cloud Platform. It keeps your data in sync across client apps through realtime listeners and offers offline support for mobile and web so you can build responsive apps that work regardless of network latency or Internet connectivity.

![structure](https://thepracticaldev.s3.amazonaws.com/i/r26hmpckf4r3qgtkzxje.png)
Data in Firestore database is saved as json objects (key:value structure) called `Documents` and contained in `Collections`. This organisation makes it easier to design domain objects (persisted in the database) in a fashion similar to our web app DTOs.

Firebase platform also proposes a generous *free tier*:

![free-tier](https://thepracticaldev.s3.amazonaws.com/i/e0c1c5o73li16443h4bu.png)

So we can use it for our personal projects or demos without any worries about initial costs.

I will reserve a separate article to describe more in detail the Firebase platform and other Cloud Firestore features. Here we concentrate on its `offline persistence` functionality.
&nbsp;

#### Offline persistence
Offline storage is enabled by default for mobile development, but not for the web. We have to activate it explicitly by calling the `enablePersistence` method:


```javascript
// Register Firebase Keys
firebase.initializeApp({
  apiKey: '### FIREBASE API KEY ###',
  authDomain: '### FIREBASE AUTH DOMAIN ###',
  projectId: '### CLOUD FIRESTORE PROJECT ID ###',
} ,"myDemoApp");


// Enable offline support
firebase.firestore().enablePersistence()
  .catch(function(err) {
      if (err.code == 'unimplemented') {
          // The current browser does not support all of the
          // features required to enable persistence
      }
  });
});

```
From this moment any document received from the server is stored locally in an Indexed DB. Now we can read, edit or delete any cached document, even without a network connection. If the cache does not contain the requested document, an error is returned.
All pending local changes are then automatically synchronised with the DB on the server as soon as the user comes back online.

Let's image we are travelling by train with a discontinue network connection, continuously turning offline and online. Sound familiar, doesn't it?

![offline](https://thepracticaldev.s3.amazonaws.com/i/a53s0n41slya7y73z6r9.jpg)

Nevertheless our application would still be accessible and even allow to modify its content (as long as we have the requested documents in the cache). We designed a PWA behaving seamlessly while online or offline.

We can analyse the cached data within the "Application" Tab in DevTools (if using Chrome):

![cached-data](https://thepracticaldev.s3.amazonaws.com/i/jfrak95w4fr0d77baldc.png)


By default the cache limit is 40MB. After exceeding this quota, Firestore attempts to clean up old documents until the cache size returns under the limit set. It is possible to specify a different threshold (the minimum value must be 1MB) or disable the eviction process entirely:

```javascript
firebase.firestore().settings({
  cacheSizeBytes: firebase.firestore.CACHE_SIZE_UNLIMITED
});
```
&nbsp;

#### Firestore limitations
Before deciding to use Firestore in our application though, we have to be aware of some limitations:

- **Limit of pending changes is set to 500.**
Google engineers explicitly designed such a limit, as the offline persistence is meant to cover temporary connection discontinuity and not to be used for a long time.

- **Concurrent updates policy is "last write wins".**
If there are multiple updates against the same document on the database, the last write arriving to the server will be saved. This might lead to potentially save *older* data if this comes from a client that was offline and is now synchronising its pending changes.
&nbsp;

At this point the choice is yours. It is not always necessary to offer edit functionality while offline, it depends on your business requirements. If you decide to go for it, then either you develop a full custom solution or opt for Firestore. I found the latter choice giving a lot of benefits out of the box with a very little effort.

In this post we saw how to further improve our PWA and I hope to have been able to convince you that nowadays it is possible to implement web applications that are potentially never offline!
&nbsp;
![bye](https://media.giphy.com/media/OOSp3kMb3IL4lE27Ar/giphy.gif)

&nbsp;
You can follow me on:
&nbsp;
<a href="https://twitter.com/paco_ita">![follow-me-twitter](https://thepracticaldev.s3.amazonaws.com/i/fgw3ppch67r8ued10oef.png)</a>
&nbsp;