visit
In this article I would like to show you how you could use the @memoizeAsync decorator, from the , in your application (both node and web) in one of the most elegant and simple ways.
Let’s say we have the following class:export class SettingsProviderClient {
getSettings(context): Promise<SettingsDto> {
...
}
}
First step, install the utils-decorators library:
npm install --save utils-decorators
Second step, apply the memoizeAsync decorator:
import { memoizeAsync } from 'utils-decorators';
export class SettingsProviderClient {
@memoizeAsync(1000 * 60 * 10) // 10 minutes in ms
getSettings(context): Promise<SettingsDto> {
...
}
}
In the next few paragraphs we will discuss the memoizeAsync API and it’s more advanced configurations.
Custom key resolving:
By default the decorator will put as the key of a cache entry the string which is generated by running JSON.stringify on the provided arguments to the decorated method. If you would like to change this behaviour then you will need to provide your own key resolver:
@memoizeAsync({
keyResolver: (context) => context.id.toString(),
expirationTimeMs: 1000 * 60 * 10
})
getSettings(context): Promise<SettingsDto> {
...
}
The cache attribute is expecting to receive an object that implements the Cache interface (which the JavaScriptMap object implements by default):
interface Cache<D> {
set: (key: string, value: D) => void;
get: (key: string) => D | null;
delete: (key: string) => void;
has: (key: string) => boolean;
}
Distributed cache:
In many cases you have more than one instance of your application running, in this cases you might want to have a distributed cache which all the instances of the application can share.
To achieve this you need to provide your custom cache which should implement the AsynCache interface
interface AsyncCache<D> {
set: (key: string, value: D) => Promise<void>;
get: (key: string) => Promise<D> | Promise<null>;
delete: (key: string) => Promise<void>;
has: (key: string) => Promise<boolean>;
}
The AsyncCache interface resembles the Cache interface but notice that every operation is async (returning a Promise).
For example, let’s say that we are using Redis (you can see a full implementation in this ) as our distributed cache:
@memoizeAsync({
keyResolver: (context) => context.id.toString(),
expirationTimeMs: 1000 * 60 * 10,
cache: redisCache
})
getSettings(context): Promise<SettingsDto> {
...
}
Different cache types for different environments
You probably noticed that both the local and the async caches are provided via the same cache attribute, so if we would like to use different caches for different environments we could achieve this with a simple condition check:
@memoizeAsync({
keyResolver: (context) => context.id.toString(),
expirationTimeMs: 1000 * 60 * 10,
cache: isProd() ? redisCache : new Map()
})
getSettings(context): Promise<SettingsDto> {
...
}
Previously published