Caching Module Concepts

In this guide, you'll learn about the main concepts of the Caching Module, including cache keys, cache tags, and automatic cache invalidation.

Cache Keys#

Cache keys uniquely identify cached data in the caching service. The Caching Module automatically generates cache keys when you cache data with Query or the Index Module.

Custom Cache Keys#

When you cache custom data with the Caching Module's service, you can generate a cache key using the computeKey method. This method generates a unique key based on the data you want to cache.

For example:

Code
1const data = { id: "prod_123", title: "Product 123" }2const key = await cachingModuleService.computeKey(data)3await cachingModuleService.set({4  key,5  tags: ["Product:prod_123", "Product:list:*"],6  data,7})

The computeKey method takes an object as input and generates a unique key based on its properties.

The generated key is a hash string that uniquely identifies the data. The has doesn't change based on the order of properties in the object.

For example, the following two objects generate the same cache key:

Code
1const key1 = await cachingModuleService.computeKey({ id: "prod_123", title: "Product 123" })2const key2 = await cachingModuleService.computeKey({ title: "Product 123", id: "prod_123" })3
4console.log(key1 === key2) // true

When to Use Custom Cache Keys?#

Use custom cache keys when you're caching custom data with the Caching Module's service. This ensures that the cached data is uniquely identified and can be retrieved or invalidated correctly.

You can also pass a custom key to Query or the Index Module when caching data. This is useful when you want to use the key for custom invalidation or retrieval.

Learn more about passing custom keys in the Query guide.


Cache Tags#

Cache tags are useful for grouping cached data, making it easier to invalidate or retrieve related cached entries.

When you cache data with the Query or Index Module, the Caching Module automatically generates cache tags based on the entity being queried and its retrieved relations.

When you cache custom data with the Caching Module's service, you can pass custom tags to the set and get methods.

Caching Tags Convention#

The Caching Module generates cache tags in the following format:

  • Entity:id: Cache tag for a single record of an entity. For example, Product:prod_123 for caching a single product with the ID prod_123.
  • Entity:list:*: Cache tag for a list of records of an entity. For example, Product:list:* for caching a list of products.
Note: Entity is the pascal-cased name of the data model, which you pass as the first parameter to model.define when defining the model.

When you use custom tags, ensure they adhere to the above convention. Otherwise, the Caching Module cannot automatically invalidate your cached data. You'll have to invalidate the cached data manually.

For example:

Code
1const key = await cachingModuleService.computeKey(data)2await cachingModuleService.set({3  key,4  tags: ["Product:list:*", "Product:prod_123"],5  data,6})

When to Use Custom Tags?#

Use custom tags when you want to group cached data for custom invalidation or retrieval.

Note that if your custom tags do not follow the Caching Tags Convention, the Caching Module cannot automatically invalidate your cached data. You must manually invalidate the cached data when it changes.


Automatic Cache Invalidation#

When is Cache Automatically Invalidated?#

The Caching Module automatically invalidates cached data when the underlying data changes through database operations such as create, update, or delete.

For example, if you cache a list of products with the tag Product:list:* and a new product is created, the Caching Module automatically invalidates the cached list of products.

This ensures that your application always serves fresh data and avoids serving stale or outdated information.

The following table shows when the Caching Module invalidates cached data based on different database operations:

Database Operation

Invalidated Cache Tags

Create

Entity:list:* (Product:list:*)

Update

Entity:{id}, Entity:list:* if the list includes the updated record. (Product:prod_123, Product:list:*)

Delete

Entity:list:* (Product:list:*)

Which Data is Automatically Invalidated?#

The Caching Module automatically invalidates your cached data when:

  1. The data includes an id field. This is used internally to map the data to the corresponding cache tags.
    • When retrieving data with Query or the Index Module, ensure the id field is included in the fields option.
Code
1const { data: products } = useQueryGraphStep({2  entity: "product",3  fields: ["id", "title"], // Ensure 'id' is included, or pass '*'4  options: {5    cache: {6      enable: true,7    },8  },9})
  • When caching custom data with the Caching Module's service, ensure the data object includes an id property.
  1. Custom tags follow the Caching Tags Convention.
Code
1const data = { id: "prod_123", title: "Product 123" }2const key = await cachingModuleService.computeKey(data)3await cachingModuleService.set({4  key,5  tags: ["Product:prod_123", "Product:list:*"],6  data,7})
  1. The autoInvalidate option is not set or is set to true. This option is enabled by default.
Code
1const { data: products } = useQueryGraphStep({2  entity: "product",3  fields: ["id", "title"],4  options: {5    cache: {6      enable: true,7      // This is enabled by default8      // autoInvalidate: true,9    },10  },11})

If the cached data includes relations, and the relation is updated, the Caching Module also invalidates the cache tags for the related entity.

For example, consider the following Query usage:

Code
1const { data: products } = useQueryGraphStep({2  entity: "product",3  fields: ["id", "title", "variants.*"],4  options: {5    cache: {6      enable: true,7    },8  },9})

If the product's variant is updated, the Caching Module invalidates the cache tags for both the Product and ProductVariant entities.

When to Disable Automatic Invalidation?#

Disabling automatic invalidation means the data remains in the cache until it expires (based on the TTL) or is manually invalidated.

Consider disabling automatic invalidation in the following cases:

  1. You're caching data that rarely changes, such as a list of countries.
  2. You want to manage cache invalidation manually, such as when caching data as part of a custom workflow where you want to control when the cache is invalidated.
  3. You're caching data that does not belong to a Medusa data model, such as data from an external API or computed values. This data is not automatically invalidated by default.

Disable automatic invalidation by setting the autoInvalidate option to false when caching data with Query, the Index Module, or the Caching Module's service.

When you disable automatic invalidation, manually invalidate the cached data when it changes.


Caching Best Practices#

Cache Rarely-Changing Data#

Cache data that is read frequently but changes infrequently, such as product information, categories, or static content.

Caching such data can significantly improve performance and reduce database load.

Do Not Cache Dynamic Data#

Avoid caching data that changes frequently or is user-specific, such as shopping cart contents, user sessions, or product pricing.

Caching such data can lead to inconsistencies and stale information being served to users. It also increases bandwidth and memory usage, as the cache is updated frequently.

Do Not Cache Frequently Updated Data#

Avoid caching data that is updated frequently, such as inventory levels or order statuses.

Caching such data increases the overhead of cache invalidation and may lead to performance degradation.

Was this page helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break