The browser-cache-async
package provides a highly customizable, asynchronous caching system for client-side data management. Leveraging the power of the browser's IndexedDB
, it enables efficient storage and retrieval of API responses, significantly reducing network latency and improving application performance.
With flexible configuration options for cache lifecycles, expiration strategies, and data management policies, browser-cache-async
is the ideal solution for developers looking to optimize web applications, enhance offline capabilities, and deliver a smoother user experience.
Install the package:
npm install -S browser-cache-async
Cache the result of a fetch
request:
import { BrowserCache } from 'browser-cache-async';
import { IProduct } from './types.js';
const cache = new BrowserCache<IProduct>('products');
// retrieve and cache the product. If revalidate is not provided, the data becomes stale after 24 hours
const id = 1;
const product = await cache.run({
id,
query: async () => {
const res = await fetch(`https://fakestoreapi.com/products/${id}`);
return res.json();
}
});
// {
// id: 1,
// title: '...',
// price: '...',
// category: '...',
// description: '...',
// image: '...'
// }
Cache an article for 2 weeks if it has been published:
import { BrowserCache } from 'browser-cache-async';
import { BackendService } from './backend.js';
import { IArticle } from './types.js';
const cache = new BrowserCache<IArticle>('articles');
const id = 'db6d9d01-8d67-4765-8baa-2210cbc0470e';
const article = await cache.run({
id,
query: async () => BackendService.getArticle(id),
cacheIf: (id: IRecordID, data: IArticle) => data.isDraft === false,
revalidate: '2 weeks'
});
// {
// id: 'db6d9d01-8d67-4765-8baa-2210cbc0470e',
// heading: '...',
// subHeading: '...',
// content: '...',
// isDraft: false
// }
// if the article is updated or removed, trigger a revalidation manually
await cache.revalidate(id);
IRecordID
The identifier used to manage records. The store behaves differently based on the type:
undefined
: the data will be stored at the root of the storestring
|number
: the value will be coerced into a string and can be used to locate the data
type IRecordID = undefined | string | number;
Note: this type is exposed by the browser-keyval-stores
package.
IStringValue
The template literal types that prevents developers from passing invalid strings to the ms
function.
type IUnit =
| 'Years'
| 'Year'
| 'Yrs'
| 'Yr'
| 'Y'
| 'Weeks'
| 'Week'
| 'W'
| 'Days'
| 'Day'
| 'D'
| 'Hours'
| 'Hour'
| 'Hrs'
| 'Hr'
| 'H'
| 'Minutes'
| 'Minute'
| 'Mins'
| 'Min'
| 'M'
| 'Seconds'
| 'Second'
| 'Secs'
| 'Sec'
| 's'
| 'Milliseconds'
| 'Millisecond'
| 'Msecs'
| 'Msec'
| 'Ms';
type IUnitAnyCase = IUnit | Uppercase<IUnit> | Lowercase<IUnit>;
type IStringValue =
| `${number}`
| `${number}${IUnitAnyCase}`
| `${number} ${IUnitAnyCase}`;
Note: this type is exposed by the ms
package`.
ICacheIfFn
Utility type to indicate the function that will be invoked to evaluate if the data should be cached.
type ICacheIfFn<T> =
((id: IRecordID, data: T) => Promise<boolean>) | ((id: IRecordID, data: T) => boolean);
IQueryOptions
Object in charge of controlling how the query is executed and cached.
import { IRecordID } from 'browser-keyval-stores';
type IQueryOptions<T> = {
// the record's identifier
id?: IRecordID;
// the function that will be invoked to retrieve the data
query: () => Promise<T>;
// the function that will be invoked to evaluate if the data should be cached. If not provided,
// the data will always be cached.
cacheIf?: ICacheIfFn | boolean;
// the number of milliseconds the data will be fresh for before becoming stale. If not provided,
// the data will become stale after 1 day.
revalidate?: IStringValue | number;
};
// the result of processing the query options object passed by the developer
type IProcessedQueryOptions<T> = IQueryOptions<T> & { revalidate: number };
IBrowserCache
Object in charge of managing the caching of data in the browser.
interface IBrowserCache<T> {
// properties
// ...
// actions
run: (options: IQueryOptions<T>) => Promise<T>;
revalidate(id?: IRecordID): Promise<void>;
}
- TypeScript
# integration tests
npm run test:integration
# unit tests
npm run test:unit
Install dependencies:
npm install
Build the library:
npm start
Publish to npm
:
npm publish