Cache
Introduction
Cache
stores response data from requests. It uses an event system that successively transmits data and takes care
of validation in the system. Data is stored under cacheKey
in the storage (which by default is a Map object, but can
be replaced by any other compatible interface).
Purpose
- Stores request results
- Manages stored data
- Emits storage events
CacheKey
The cache stores data on a key-value
basis. The key is always specified inside the request as cacheKey
; it
determines where the data is stored and is used in propagation of data handling events.
By default, the cacheKey value is auto-determined based on the method, endpoint, and query params of the given request. However, there is nothing to prevent you manually adding the key when setting the request or using one of its methods.
Thanks to the automatic way of indexing data in the cache, we do not have to worry about paginated keys in the data – everything will happen automatically.
Events
Available cache events.
{
emitCacheData: (cacheKey: string, data: CacheValueType<Response, Error, Adapter>) => void;
emitInvalidation: (cacheKey: string) => void;
onData: (cacheKey: string, callback: (data: CacheValueType<Response, Error, Adapter>) => void) => VoidFunction;
onInvalidate: (cacheKey: string, callback: () => void) => VoidFunction;
}
Storage
By default, the cache uses Map as a data retention location. However, you can also choose where the data is stored in the system – i.e. local storage or IndexedDB – regardless if the source works synchronously or asynchronously. This allows you to set up persistent storage across sessions.
export const client = new Client<ServerErrorType>({
url: "localhost:3000",
cache: (instance) =>
new Cache(instance, {
storage,
}),
});
Read Cache
The cache can be read using the get
method. It returns the data stored under the given key.
const data = client.cache.get("key");
// OR
const getUsers = client.createRequest()({
method: "GET",
endpoint: "/users",
});
const data = client.cache.get(getUsers.cacheKey);
// OR
const getUsers = client.createRequest()({
method: "GET",
endpoint: "/users",
cacheKey: "CUSTOM_CACHE_KEY",
});
const data = client.cache.get("CUSTOM_CACHE_KEY");
const data = client.cache.get(getUsers.cacheKey);
// OR Dynamic
const getNote = client.createRequest()({
method: "GET",
endpoint: "/notes/:noteId",
});
const data = client.cache.get(getNote.setParams({ noteId: 1 }).cacheKey);
Write Cache
The cache can be written using the set
method. It stores the data under the given key. We MUST pass the whole request
to the set method as it stores configuration related to cache time, garbage colletion, etc.
You can set everything you want, error states, extra data, etc.
The cache will take care of the rest and will emit all
necessary events. This way our subsystems will receive it and will be able to react to it.
const getNote = client.createRequest()({
method: "GET",
endpoint: "/notes/:noteId",
});
client.cache.set(getNote.setParams({ noteId: 1 }), {
data: { text: "Hello World" },
error: null,
status: 200,
success: true,
extra: xhrExtra,
});
// You can also use callback to read previous data
client.cache.set(getNote.setParams({ noteId: 1 }), (prevData) => ({
...prevData,
data: { text: "Hello World" },
}));
Update Cache
We can update cache by using the update
method. It updates the data under the given key. We pass the whole request to
method in similar way as we did it with set
method.
Cache will be NOT updated if the initial data is not present in the cache.
const getNote = client.createRequest()({
method: "GET",
endpoint: "/notes/:noteId",
});
client.cache.update(getNote.setParams({ noteId: 1 }), {
data: { text: "Hello World" },
error: null,
status: 200,
success: true,
extra: xhrExtra,
});
// You can also use callback to read previous data
client.cache.update(getNote.setParams({ noteId: 1 }), (prevData) => ({
...prevData,
data: { text: "Hello World" },
}));
Delete Cache
To delete data from the cache, we can use the delete
method. It deletes the data under the given key.
const getNote = client.createRequest()({
method: "GET",
endpoint: "/notes/:noteId",
});
client.cache.delete(getNote.setParams({ noteId: 1 }).cacheKey);
// OR
client.cache.delete("CUSTOM_CACHE_KEY");
Invalidate Cache
You can also invalidate the cache by using the invalidate
method. It deletes the data under the given key and emits
event, so in case of using it with for example React, it will refetch the data.
const getNote = client.createRequest()({
method: "GET",
endpoint: "/notes/:noteId",
});
client.cache.invalidate(getNote.setParams({ noteId: 1 }).cacheKey);
// OR
client.cache.invalidate("CUSTOM_CACHE_KEY");
// OR via RegExp
client.cache.invalidate(new RegExp("CUSTOM_CACHE_KEY"));
Persistence
We can achieve the persistence of stored data by changing the cache storage to persistent. It must match the provided interface.
Read More
Currently there is no cross-tab synchronization. It's planned for a future release.
Lifecycle
Cache options can be provided with some lifecycle methods. These are events such as onInitialization
.
Parameters
Configuration options
{
clearKey: string;
lazyStorage: {
delete: (key: string) => Promise<void>;
get: (key: string) => Promise<CacheValueType<Response, Error, Adapter> | undefined>;
keys: () => Promise<string[] | IterableIterator<string> | string[]>;
set: (key: string, data: CacheValueType<Response, Error, Adapter>) => Promise<void>;
};
onChange: (key: string, data: CacheValueType<Response, Error, Adapter>) => void;
onDelete: (key: string) => void;
onInitialization: (cache: Cache<C>) => void;
storage: {
clear: () => void;
delete: (key: string) => void;
get: (key: string) => CacheValueType<Response, Error, Adapter> | undefined;
keys: () => string[] | IterableIterator<string> | string[];
set: (key: string, data: CacheValueType<Response, Error, Adapter>) => void;
};
}