Enabling Persistent Storage in IndexedDB – closingtags </>
Categories
Javascript Programming Svelte SvelteKit

Enabling Persistent Storage in IndexedDB

Exploring how to mark #IndexedDB storage as persistent for an offline first #PWA built with #SvelteKit

It has now been 60 days since my book; SvelteKit Up and Running, was published 🎉. You would think that would be enough time to recover from technical writing but apparently, it isn’t. As such, I’ll be keeping this post short.

Lately, I’ve been working on an offline first PWA (built with SvelteKit) that makes use of the IndexedDB API to store data locally on the user’s device. To do this, I’ve relied heavily on the Dexie wrapper, which makes managing the schema a breeze. However, Dexie doesn’t provide a way to instantiate or mark a database as “persistent.” This means that at anytime the user’s browser could remove data stored within application’s database. This typically only happens when device storage is running low but I imagine users of the application would be fairly upset to open the app to discover their precious data has mysteriously disappeared.

As previously mentioned, Dexie itself does not provide a method for enabling persistence. However, the authors of Dexie have encountered this dilemma and provide a strategy in their documentation. It’s a simple and effective solution because it only needs to make two calls to the StorageManager API (which is far less complex than the IndexedDB API). The API methods persist() and persisted() are the relevant calls. In my project, I created two functions to wrap these methods and return the resulting promises. As I didn’t want these methods being run in server-side code, I’ve wrapped them in the browser environment check provided by SvelteKit.

db.js

// Persistent Storage methods as taken
// from https://dexie.org/docs/StorageManager
// adapted for use within SvelteKit

import { browser } from '$app/environment';
// ...

export const persist = async () => {
  if(browser) {
    console.log('setting persistence...');
    return await navigator.storage && navigator.storage.persist && navigator.storage.persist();
  }
  return {};
}

export const isStoragePersisted = async () => {
  if(browser) {
    console.log('checking persistence...');
    return await navigator.storage && navigator.storage.persisted && navigator.storage.persisted();
  }
  return {};
}

Within the project’s root layout, I can then check if the client-side database is persistent with a simple call to isStoragePersisted(). If not, I annoy prompt the user to enable persistent storage and in the prompt’s callback, call persist(). This then triggers the browser prompt which asks the user if it’s alright to enable persistent storage for the application.

+layout.svelte

import { persist, isStoragePersisted } from 'db.js';
import { onMount } from 'svelte';
import { confirmDialog } from 'toast.js';

onMount(async() => {
  const status = await isStoragePersisted();
  if(!status) {
    confirmDialog('Make storage persistent now?', persist, console.log('denied'), true);
  }
});

If you’re curious about what’s happening in toast.js, it’s making use of Svelte Toast to create notifications. If you’d like to see the rest of the project, the code can be viewed on GitHub.

By Dylan Hildenbrand

Dylan Hildenbrand smiling at the camera. I have tossled, brown hair, rounded glasses, a well-trimmed and short beard. I have light complexion and am wearing a dark sweater with a white t-shirt underneath.

Author and full stack web developer experienced with #PHP, #SvelteKit, #JS, #NodeJS, #Linux, #WordPress, and #Ansible. Check out my book at sveltekitbook.dev!

Do you like these posts? Consider sponsoring me on GitHub!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.