DEV Community

Tianya School
Tianya School

Posted on

PWA Offline Storage Strategies-IndexedDB and Cache API

Progressive Web Apps (PWAs) utilize offline storage strategies to provide an offline experience, enabling access to some content even when the network is unavailable. The two primary offline storage technologies are IndexedDB and the Cache API (also known as Service Worker Cache). Each has distinct features and is suited for different use cases.

IndexedDB

IndexedDB is a key-value store ideal for storing large amounts of structured data, such as database records, user settings, or large files. It supports complex queries and transactional operations. Below is an example of creating, storing, and retrieving data with IndexedDB:

// Open or create a database
let dbPromise = indexedDB.open("myDatabase", 1);

// When the database opens successfully
dbPromise.then(function(db) {
    // Create an object store
    let objectStore = db.createObjectStore("myStore", { keyPath: "id" });

    // Insert data
    objectStore.add({ id: 1, name: "Alice" });

    // Query data
    let transaction = db.transaction(["myStore"], "readonly");
    let store = transaction.objectStore("myStore");
    let request = store.get(1);

    request.onsuccess = function(event) {
        console.log("Retrieved data:", event.target.result);
    };
});
Enter fullscreen mode Exit fullscreen mode

Cache API

The Cache API, part of Service Workers, is used to cache network requests and responses, typically for static resources. It provides a straightforward way to store and retrieve resources based on their URLs. Below is an example of using the Cache API to cache web resources:

// Cache resources during the install phase in the service worker
self.addEventListener("install", async event => {
    event.waitUntil(
        caches.open("myCache").then(cache => {
            return cache.addAll([
                "/index.html",
                "/styles.css",
                "/scripts.js"
            ]);
        })
    );
});

// Handle fetch events by attempting to retrieve resources from the cache
self.addEventListener("fetch", event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            // Return cached response if available
            if (response) {
                return response;
            } else {
                // Otherwise, fetch from the network and cache the new resource
                return fetch(event.request).then(networkResponse => {
                    caches.open("myCache").then(cache => {
                        cache.put(event.request, networkResponse.clone());
                    });
                    return networkResponse;
                }).catch(() => {
                    // Return a cached fallback response if the network fails
                    return caches.match("/offline.html");
                });
            }
        })
    );
});
Enter fullscreen mode Exit fullscreen mode

Code Breakdown:

  • The install event initializes the cache, adding specified URLs to it.
  • The fetch event listens for network requests, prioritizing cached resources. If no cache exists, it fetches from the network, caches the new resource, and returns it. If the network request fails, a fallback offline page is returned.

By combining IndexedDB and the Cache API, PWAs can store both user data and static resources offline, ensuring a robust user experience even without a network connection.

In real-world PWA applications, IndexedDB and the Cache API are often used together to achieve optimal offline experiences. For example, IndexedDB stores user data, while the Cache API handles static resources.

self.addEventListener("install", async event => {
    event.waitUntil(
        Promise.all([
            caches.open("staticCache").then(cache => {
                return cache.addAll([
                    "/index.html",
                    "/styles.css",
                    "/scripts.js"
                ]);
            }),
            // Assume an API fetches user data
            fetch("/api/user").then(response => {
                return response.json().then(data => {
                    return indexedDB.open("userData", 1).then(db => {
                        let transaction = db.transaction(["userData"], "readwrite");
                        let store = transaction.objectStore("userData");
                        store.put(data);
                    });
                });
            })
        ])
    );
});

self.addEventListener("fetch", event => {
    event.respondWith(
        caches.match(event.request).then(response => {
            if (response) {
                return response;
            }

            // Try fetching from the network
            return fetch(event.request).then(networkResponse => {
                caches.open("staticCache").then(cache => {
                    cache.put(event.request, networkResponse.clone());
                });

                return networkResponse;
            }).catch(() => {
                // If network fails, try fetching user data from IndexedDB
                if (event.request.url.endsWith("/api/user")) {
                    return indexedDB.open("userData", 1).then(db => {
                        let transaction = db.transaction(["userData"], "readonly");
                        let store = transaction.objectStore("userData");
                        let request = store.get(1);
                        return request.onsuccess ? request.onsuccess.event.target.result : null;
                    });
                } else {
                    return caches.match("/offline.html");
                }
            });
        })
    );
});
Enter fullscreen mode Exit fullscreen mode

Code Breakdown:

  1. During the install event, static resources are cached, and user data is fetched from an API and stored in IndexedDB.
  2. During the fetch event, resources are first retrieved from the cache. If unavailable, the network is queried. For API requests, if the network fails, user data is retrieved from IndexedDB. For other resources, a fallback offline page is returned.

Top comments (0)