Persisting data in modern Web-Apps
LocalStorage, SessionStorage, IndexedDB, Service Worker, Filesystem API and of course Cookies 🍪!
It wasn't long ago when cookies were the only viable option to store data persistently in the browser. Speaking of cookies, we talk about a limited string of characters representing the storage capabilities for quite a long time. But technology has evolved, and so have the browsers. As a result, today's options to temporarily store data or persist in the browser are far better than some years ago and will eventually become useful for application development.
Modern Web-Apps or PWAs aim to compete with native apps and websites; to accomplish that, the Web-App needs to be reliable, performant, and persist even higher amounts of data like text blobs or images. Therefore, the new available Filesystem API1, adopted by Apple WebKit, will also be a game-changer.
The following article will discuss the different approaches to storing data as temporary and persistent for websites and web apps.
Cookies - The secure grandfather of storage
Cookies are key-value pairs stored in a maximum of 4KB size string. The 4KB limit generally refers to the maximum size of each cookie, including the cookie's name, value, and attributes (such as expiration date, domain, path, etc.). This limit is a per-cookie restriction.
Cookies are mainly used for three applications:
Token Storage for Authentication or Session-Identification: Cookies are frequently stored to store session or authentication tokens. When a user logs into a website, the server sends a session token back to the user's browser, which is stored as a cookie. This cookie is then sent back to the server with each subsequent request, allowing the server to recognize the user and maintain their session. This is fundamental to maintaining stateful interactions in the otherwise stateless HTTP protocol.
Tracking Cookies for Monitoring or Marketing Purposes: Tracking cookies monitor user behavior across websites. They are commonly used by advertisers and analytics services to gather data on user activities, preferences, and browsing histories. This data helps in creating targeted advertising and understanding user behavior patterns. For example, a tracking cookie set by an advertisement on one site can track user actions on other sites that display ads from the same network.
Storing Simple App States in a Primitive Way: Cookies can store simple, non-sensitive data locally in the user's browser. This could include user preferences, such as layout choices, color themes, or other information that contribute to the user experience but are not critical. Since cookies are sent with every HTTP request to the server, this use should be limited to small amounts of data to avoid unnecessary network traffic.
🍪 Cookies are still viable because of their secure nature and because I use them as my emoji.
Another security flag is the SameSite directive, which shall help prevent cross-site request forgery attacks (CSRF3) by restricting the sending of the cookie only to its originated site.
Caching with Service Worker
Caching in the browser is an old technique to reduce requests to the server and, therefore, reduce payload while increasing the overall user experience. Unfortunately, this basic caching has limitations as soon as the device connectivity is gone, and the developer doesn't have enough control over what to cache.
With the introduction of the service-worker4 came the capability to implement runtime caching strategies5 6 for specific routes in your app. It's possible to cache and persist all network traffic of your web app and reuse it with a Stale-While-Revalidate (SWR) approach.
This approach stores all requests in the cache storage, and with the next call of the Web App, the service worker, which acts as a proxy, will provide stale data and decide when to re-fetch by the current caching strategy.
This basic technique provides offline experience and reliability, some fundamentals of progressive web apps.
In Google Workbox, available strategies
Stale-While-Revalidate: This strategy serves content from the cache (stale) while simultaneously fetching an updated version from the network. The updated content is then cached for the next use. This approach is useful for assets where immediate speed is critical, but fresh content is also important. For example, a news website might use this for articles, ensuring quick loading while updating the latest content.
👉 Balanced Approach
Network-First: This strategy attempts to fetch the latest content from the network first. If the network request fails (perhaps due to a lack of internet connectivity), it returns to a cached response. It's ideal for dynamic content that changes frequently and needs to be as up-to-date as possible, such as live sports scores or stock market data.
👉 Fresh-First approach
👉 Static approach: when things don’t change
Network-Only: This strategy bypasses the cache entirely and always fetches the resource from the network. It’s the equivalent of not implementing a caching strategy in the service worker. It's suitable for dynamic or sensitive information where updates are frequent, and caching could present outdated or incorrect data.
👉 No-Caching approach
Cache-Only: This strategy serves responses exclusively from the cache without using the network. If the requested resource isn’t in the cache, it results in an error. This approach is used for scenarios where the application has to work offline, like a PWA, after it has precached its necessary resources during installation or activation.
👉 No online approach
The Service-Worker is not meant to store and retrieve specific data on demand; it is intended to be a passive persistor of network traffic and already received data. A good example is a map app that downloads tiles once. It makes them available in the cache for reliable offline usage or news app caching already downloaded for offline use later.
Caching is becoming more critical since mobile applications are limited to their current network, which might be of low bandwidth, poor quality, or unavailable when requesting new data. Still, the apps shall show no error code or failed state; the user journey must continue smoothly. This behavior is widely standard for native apps and should be adopted by the web using caching API.
Indexed DB - Asynchronous go-to storage
To store on-demand text-based data in the browser, it's recommended to use indexedDB, as long as there's no sensitive data. With IndexedDB, the browser provides key-value pair-based databases with a basic interface setItem, getItem, removeItem.