Implements Caching in Medusa V2
Caching is one of those things you don’t notice until it’s missing. Without it, your Medusa app ends up wasting time on repeated database calls and computations, slowing down responses that could otherwise be instant.

That’s fine if you’re testing locally, but in production every wasted millisecond means slower endpoints and unnecessary database load.
Medusa already ships with caching modules ("In-Memory" for dev, "Redis" for prod), so let’s look at how to actually use them in practice.
Custom API Route Caching
The simplest place to start: your own routes.
Here’s an example that checks if products are in cache before hitting the DB.
If cached, return immediately; if not, fetch and save for next time.
1import { Product } from ".medusa/types/remote-query-entry-points"
2import {
3 MedusaRequest,
4 MedusaResponse,
5} from "@medusajs/framework/http"
6import {
7 ContainerRegistrationKeys,
8 Modules,
9} from "@medusajs/framework/utils"
10
11export const GET = async (
12 req: MedusaRequest,
13 res: MedusaResponse
14) => {
15 const query = req.scope.resolve(ContainerRegistrationKeys.QUERY)
16 const cacheService = req.scope.resolve(Modules.CACHE)
17
18 // ℹ️ Define a cache key
19 // The cache key can be unique or not, depending on the query.
20 // For example, we could add the pagination parameters
21 // to the key to make sure we cache different pages of products separately
22 const CACHE_KEY = "medusa:products"
23
24 // ℹ️ First, we check if the data is cached
25 const cached = await cacheService.get<{ data: Product[] }>(CACHE_KEY)
26
27 // ℹ️ If the data is cached, we return it immediately
28 if (cached?.data) {
29 return res.json({ data: cached.data })
30 }
31
32 // ℹ️ If the data is not cached, we fetch it from the database
33 const { data } = await query.graph({
34 entity: "product",
35 fields: ["*"]
36 })
37
38 // ℹ️ We store the fetched data in the cache, for future requests
39 await cacheService.set(CACHE_KEY, { data })
40
41 // ℹ️ Finally, we return the fetched data
42 res.json({ data })
43}
That’s it, fewer queries, almost instant responses.
You can even build cache keys with pagination params (medusa:products:page=2
) if needed.
Core API Route Caching
But what about Medusa’s built-in routes like /store/products
?
We can’t modify them directly, but we can intercept them with middleware.
Here’s a quick example that:
- Checks cache first
- Returns instantly if found,
- Otherwise intercepts the response, stores it, then sends it to the client.
1import {
2 defineMiddlewares,
3 type MedusaNextFunction,
4 type MedusaRequest,
5 type MedusaResponse,
6} from "@medusajs/framework/http"
7import { Modules } from "@medusajs/framework/utils"
8import { type HttpTypes } from "@medusajs/framework/types"
9
10export default defineMiddlewares({
11 routes: [
12 {
13 matcher: "/store/products", // ℹ️ The core API route we want to cache
14 method: 'GET',
15 middlewares: [
16 async (
17 req: MedusaRequest,
18 res: MedusaResponse,
19 next: MedusaNextFunction
20 ) => {
21 const cacheModule = req.scope.resolve(Modules.CACHE)
22
23 // ℹ️ This is the part responsible for retrieving the products from the cache
24 const cacheKey = `medusa:products`
25 const cachedProducts = await cacheModule.get<HttpTypes.StoreProductListResponse>(cacheKey)
26
27 if (cachedProducts) {
28 res.json(cachedProducts)
29 return
30 }
31
32 // ℹ️ This is the part responsible for caching the products after they are retrieved from the database
33 const originalJsonFn = res.json
34 Object.assign(res, {
35 json: async function (body: HttpTypes.StoreProductListResponse) {
36 await cacheModule.set(cacheKey, body)
37 await originalJsonFn.call(res, body)
38 },
39 })
40
41 next()
42 },
43 ],
44 },
45 ],
46})
This works with any core route, requires no changes to Medusa’s internals, and gives you a lot of flexibility.
Takeaway
With those two approaches, you’ll cover both custom and core API routes and your store instantly feels faster.
On top of that, don't forget that you can also use this inside your "Workflow's steps", since you can resolve the Cache Module.