cache.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import { JsonCompatible } from '@vendure/common/lib/shared-types';
  2. import { SetCacheKeyOptions } from '../config/system/cache-strategy';
  3. import { CacheService } from './cache.service';
  4. /**
  5. * @description
  6. * Configuration for a new {@link Cache} instance.
  7. *
  8. * @docsCategory cache
  9. * @since 3.1.0
  10. */
  11. export interface CacheConfig {
  12. /**
  13. * @description
  14. * A function which generates a cache key from the given id.
  15. * This key will be used to store the value in the cache.
  16. *
  17. * By convention, the key should be namespaced to avoid conflicts.
  18. *
  19. * @example
  20. * ```ts
  21. * getKey: id => `MyStrategy:getProductVariantIds:${id}`,
  22. * ```
  23. */
  24. getKey: (id: string | number) => string;
  25. /**
  26. * @description
  27. * Options available when setting the value in the cache.
  28. */
  29. options?: SetCacheKeyOptions;
  30. }
  31. /**
  32. * @description
  33. * A convenience wrapper around the {@link CacheService} methods which provides a simple
  34. * API for caching and retrieving data.
  35. *
  36. * The advantage of using the `Cache` class rather than directly calling the `CacheService`
  37. * methods is that it allows you to define a consistent way of generating cache keys and
  38. * to set default cache options, and takes care of setting the value in cache if it does not
  39. * already exist.
  40. *
  41. * In most cases, using the `Cache` class will result in simpler and more readable code.
  42. *
  43. * This class is normally created via the {@link CacheService}.`createCache()` method.
  44. *
  45. * @example
  46. * ```ts
  47. * const cache = cacheService.createCache({
  48. * getKey: id => `ProductVariantIds:${id}`,
  49. * options: {
  50. * ttl: 1000 * 60 * 60,
  51. * tags: ['products'],
  52. * },
  53. * });
  54. *
  55. * // This will fetch the value from the cache if it exists, or
  56. * // fetch it from the ProductService if not, and then cache
  57. * // using the key 'ProductVariantIds.${id}'.
  58. * const variantIds = await cache.get(id, async () => {
  59. * const variants await ProductService.getVariantsByProductId(ctx, id) ;
  60. * // The cached value must be serializable, so we just return the ids
  61. * return variants.map(v => v.id);
  62. * });
  63. * ```
  64. *
  65. * @docsCategory cache
  66. * @since 3.1.0
  67. */
  68. export class Cache {
  69. constructor(
  70. private config: CacheConfig,
  71. private cacheService: CacheService,
  72. ) {}
  73. /**
  74. * @description
  75. * Retrieves the value from the cache if it exists, otherwise calls the `getValueFn` function
  76. * to get the value, sets it in the cache and returns it.
  77. */
  78. async get<T extends JsonCompatible<T>>(
  79. id: string | number,
  80. getValueFn: () => T | Promise<T>,
  81. ): Promise<T> {
  82. const key = this.config.getKey(id);
  83. const cachedValue = await this.cacheService.get<T>(key);
  84. if (cachedValue) {
  85. return cachedValue;
  86. }
  87. const value = await getValueFn();
  88. await this.cacheService.set(key, value, this.config.options);
  89. return value;
  90. }
  91. /**
  92. * @description
  93. * Deletes one or more items from the cache.
  94. */
  95. async delete(id: string | number | Array<string | number>): Promise<void> {
  96. const ids = Array.isArray(id) ? id : [id];
  97. const keyArgs = ids.map(_id => this.config.getKey(_id));
  98. await Promise.all(keyArgs.map(key => this.cacheService.delete(key)));
  99. }
  100. /**
  101. * @description
  102. * Invalidates one or more tags in the cache.
  103. */
  104. async invalidateTags(tags: string[]): Promise<void> {
  105. await this.cacheService.invalidateTags(tags);
  106. }
  107. }