Skip to content

Instantly share code, notes, and snippets.

@andrewchilds
Last active June 22, 2022 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewchilds/ad124fb6b90a29308c429097959ac0a3 to your computer and use it in GitHub Desktop.
Save andrewchilds/ad124fb6b90a29308c429097959ac0a3 to your computer and use it in GitHub Desktop.
Proposal: A client-side key-value store interface
const tableName = 'crayons';
const crayons = [
{ id: 1, color: 'blue', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 2, color: 'black', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 3, color: 'brown', updated_at: new Date('2022-02-10T11:31:25-0500') },
{ id: 4, color: 'green', updated_at: new Date('2022-02-10T11:31:25-0500') },
];
// Upsert multiple entries to a table, returning number of rows changed
await clientCache.upsertMany(tableName, crayons);
// Upsert single entry to a table, returning number of rows changed
for (let crayon of crayons) {
await clientCache.upsert(tableName, crayon);
});
// Select an entire table, returning an Array
await clientCache.select(tableName);
// Select where using an object, using first, returning an Array
await clientCache.select(tableName).where({ id: 2 }).first();
// Select where using a function, returning an Array
await clientCache.select(tableName).where(c => c.updated_at < TWO_WEEKS_AGO);
// Delete a table, returning number of rows changed
await clientCache.select(tableName).del();
// Delete a row, returning number of rows changed
await clientCache.select(tableName).where({ id: 3 }).del();

Proposal: A client-side key-value store interface

Building a modern, fast, offline-compatible web app invariably means dealing with caching.

Caching can happen at various layers - database, server, edge, client - however I would argue that the client is the ideal place to cache, and in practice, the hardest. We have two native options in practice - localStorage and IndexedDB - and a plethora of non-native libraries[1] written on top of them to make them more intuitive.

The problem is that the two native options are fundamentally poor, and prone to odd, buggy behavior[2]. LocalStorage is too simple and too slow, and IndexedDB is too complex and too buggy. I want something in between.

The above code is what I would build if I had a magic wand and could create a native client-side caching interface. It would combine the simplicity of localStorage with the capacity and performance of IndexedDB. It would be a simple but opinionated key-value store that requires no schema, versioning, or migrations.

[1] See for example https://pouchdb.com, https://github.com/dexie/Dexie.js, https://github.com/jakearchibald/idb and https://github.com/pamelafox/lscache.

[2] See https://gist.github.com/pesterhazy/4de96193af89a6dd5ce682ce2adff49a for one list of issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment