Home
Blog
Product

Announcing Cache Modules

Apr 04, 2023 by

Frane Polić

Frane Polić

We are excited to announce that we introduced cache modules into Medusa. This article gives an under-the-hood look at this feature.
Medusa provides commerce building blocks for developers to build rich commerce applications. With the latest evolution of our toolkit towards a more modular architecture, we are taking steps to make Medusa more agnostic to infrastructure. As a big step in this direction, we are excited to announce Medusa’s new cache modules.
In this article, we will look under the hood to see how cache modules work, which cache module you can use in your project today, and, finally, we will demonstrate how easily a Memcached module can be built.

How it works

Cache modules in Medusa implement the
Copy to clipboard
ICacheService
interface and are used internally to cache data like product prices and tax rates.
Medusa’s default starter project comes with an in-memory cache module (
Copy to clipboard
@medusajs/cache-inmemory
) pre-installed, which uses a plain JavaScript Map object to store data. This is great for testing and development purposes, but not recommended for production environments. You can have one cache module installed at a time and for production environments, we have built a cache module that uses Redis (
Copy to clipboard
@medusajs/cache-redis
). To enable the Redis cache module, simply install the package:
yarn add @medusajs/cache-redis
And configure it in your
Copy to clipboard
medusa-config.js
file:
module.exports = {
//...
modules: {
// ...
"cacheService": {
resolve: "@medusajs/cache-redis",
options: { ttl: 30, redisUrl: REDIS_URL }
}
},
};
And that’s it. With just a couple of changed config lines, you use a different data store for our caching layer.

Building a cache module

As mentioned above, we are making in-memory and Redis cache modules available today. Building a new cache module is, however, very easy if you have a different caching tool in place already or prefer another caching technology. To demonstrate how to build a cache module, we will implement
Copy to clipboard
ICacheService
to work with Memcached. For communication with the Memcached instance, we will be using the Memcached client for Node.js.
The first thing to do is to create our project. We need a basic TypeScript project configured for Node. You can clone a preconfigured starter here, and after running
Copy to clipboard
yarn install
, start building.
To implement the service, create a new TypeScript file in
Copy to clipboard
src/services
:
// src/services/memcached-cache.ts
import Memcached from "memcached" // 1. Memcached client
import { ICacheService } from "@medusajs/medusa" // 2. Interface from medusa core that all caching modules need to implement
const DEFAULT_CACHE_TIME = 30 // 3. Keep data cached for 30 seconds
/**
* 4. Module config type.
* Defines which options can be passed to the module in the medusa-config file.
*/
type MemcachedCacheModuleOptions = {
/**
* Time to keep data in the cache (in seconds)
*/
ttl?: number
/**
* Allow passing the configuration for Memcached client
*/
location: Memcached.Location
options?: Memcached.options
}
type InjectedDependencies = {}
class MemcachedCacheService implements ICacheService {
protected readonly memcached: Memcached
protected readonly TTL: number
constructor(
{}: InjectedDependencies,
options: MemcachedCacheModuleOptions // 5. Options passed as module config will be injected here by the modules loader
) {
this.memcached = new Memcached(options.location, options.options) // 6. Instantiate Memcached client for communication with the Memcached instance
this.TTL = options.ttl || DEFAULT_CACHE_TIME
}
/**
* Set a key/value pair to the cache.
* It is also possible to manage the `ttl` through environment variables using CACHE_TTL. If the ttl is 0 it will
* act like the value should not be cached at all.
*/
async set(
key: string,
data: Record<string, unknown>,
ttl: number = this.TTL
): Promise<void> {
return new Promise((res, rej) =>
this.memcached.set(key, JSON.stringify(data), ttl, (err) => {
if (err) {
rej(err)
} else {
res()
}
})
)
}
/**
* Retrieve a cached value belonging to the given key.
*/
async get<T>(cacheKey: string): Promise<T | null> {
return new Promise((res, rej) => {
this.memcached.get(cacheKey, (err, data) => {
if (err) {
res(null)
} else {
if (data) {
res(JSON.parse(data))
} else {
res(null)
}
}
})
})
}
/**
* Invalidate cache for a specific key.
*/
async invalidate(key: string): Promise<void> {
return new Promise((res, rej) => {
this.memcached.del(key, (err) => {
if (err) {
rej(err)
} else {
res()
}
})
})
}
}
export default MemcachedCacheService
And that’s it. The final thing to add is export from the
Copy to clipboard
index.ts
file like so:
// src/index.ts
import { ModuleExports } from "@medusajs/modules-sdk"
import { MemcachedCacheService } from "./services"
const service = MemcachedCacheService
const moduleDefinition: ModuleExports = {
service,
}
export default moduleDefinition
You can find the entire implementation here.
To test the module with a Medusa instance, link your module project with a Medusa instance:
// first build the package from the root
yarn build
// register package
yarn link
Next, we need to add this package to our Medusa project and tweak our
Copy to clipboard
medusa-config.js
file:
// in the root of your Medusa project run:
yarn link medusa-cache-memcached
// medusa-config.js
module.exports = {
// ...
modules:{
// ...
"cacheService":
{
resolve: "medusa-cache-memcached",
options: { location: "localhost:55000" }}
},
};
With this example, we see how straightforward it is to build a (cache) module for Medusa. This, combined with the fact that Medusa is an open-source project, shows the advantages users of such projects have in terms of a growing ecosystem of existing modules.

What’s next

The introduction of cache modules is a step in adding infrastructure agnosticism to Medusa’s architecture. This will make it easier to integrate Medusa into existing stacks and improve the developer experience. Our introduction of EventBus modules is a similar evolution to Medusa’s architecture. We are excited to see what you build with these new tools and can’t wait to launch more modules in the near future.

Share this post

Try Medusa

Spin up your environment in a few minutes.