* standard docs for caching module + deprecated cache module * added guides for creating + using, and overall changes from cache to caching * fix details related to redis provider * fix build errors * fix build error * fixes * add guides to sidebar * add sidebar util * document query + index * moved cache tag conventions * fix build errors * added migration guide * added memcached guide * fixes * general fixes and updates * updated reference * document medusa cache * small fix * fixes * remove cloud cache * revert edit dates changes * revert edit dates * small update
334 lines
13 KiB
Plaintext
334 lines
13 KiB
Plaintext
---
|
||
slug: /references/caching-module-provider
|
||
tags:
|
||
- caching
|
||
- server
|
||
- how to
|
||
sidebar_label: Create Caching Provider
|
||
keywords:
|
||
- caching
|
||
- provider
|
||
- integration
|
||
---
|
||
|
||
import { TypeList } from "docs-ui"
|
||
|
||
# How to Create a Caching Module Provider
|
||
|
||
In this guide, you’ll learn how to create a Caching Module Provider and the methods you must implement in its main service.
|
||
|
||
:::note
|
||
|
||
The Caching Module and its providers are available starting [Medusa v2.11.0](https://github.com/medusajs/medusa/releases/tag/v2.11.0).
|
||
|
||
:::
|
||
|
||
---
|
||
|
||
## Implementation Example
|
||
|
||
As you implement your Caching Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||
|
||
If you need to refer to an existing implementation as an example, check the [Redis Caching Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/caching-redis).
|
||
|
||
---
|
||
|
||
## 1. Create Module Provider Directory
|
||
|
||
Start by creating a new directory for your module provider.
|
||
|
||
If you're creating the module provider in a Medusa application, create it under the `src/modules` directory. For example, `src/modules/my-caching`.
|
||
|
||
If you're creating the module provider in a plugin, create it under the `src/providers` directory. For example, `src/providers/my-caching`.
|
||
|
||
<Note>
|
||
|
||
The rest of this guide always uses the `src/modules/my-caching` directory as an example.
|
||
|
||
</Note>
|
||
|
||
---
|
||
|
||
## 2. Create the Caching Module Provider Service
|
||
|
||
Create the file `src/modules/my-caching/service.ts` that holds the module provider's main service. It must implement the `ICachingProviderService` interface imported from `@medusajs/framework/types`:
|
||
|
||
```ts title="src/modules/my-caching/service.ts"
|
||
import { ICachingProviderService } from "@medusajs/framework/types"
|
||
|
||
class MyCachingProviderService implements ICachingProviderService {
|
||
// TODO implement methods
|
||
}
|
||
|
||
export default MyCachingProviderService
|
||
```
|
||
|
||
### constructor
|
||
|
||
The constructor allows you to access resources from the module's container using the first parameter,
|
||
and the module's options using the second parameter.
|
||
|
||
If you're creating a client or establishing a connection with a third-party service, do it in a [Loader](https://docs.medusajs.com/learn/fundamentals/modules/loaders)
|
||
and store it in the Module's container. Then, you can access it in your service using the container.
|
||
|
||
:::note[Loader Example]
|
||
|
||
[Initialize MongoDB client in loader and access it in service](https://docs.medusajs.com/learn/fundamentals/modules/loaders#example-register-custom-mongodb-connection).
|
||
|
||
:::
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
import { ICachingProviderService } from "@medusajs/framework/types"
|
||
import { Logger } from "@medusajs/framework/types"
|
||
|
||
type InjectedDependencies = {
|
||
logger: Logger
|
||
// assuming you initialized a client
|
||
// in a Loader and stored it in the container
|
||
client: Client
|
||
}
|
||
|
||
type Options = {
|
||
url: string
|
||
}
|
||
|
||
class MyCachingModuleProvider implements ICachingProviderService {
|
||
static identifier = "my-cache"
|
||
protected logger_: Logger
|
||
protected options_: Options
|
||
protected client
|
||
|
||
constructor (
|
||
{ logger, client }: InjectedDependencies,
|
||
options: Options
|
||
) {
|
||
this.logger_ = logger
|
||
this.options_ = options
|
||
// set the service's client to
|
||
// the client from the container
|
||
this.client = client
|
||
}
|
||
|
||
// ...
|
||
}
|
||
|
||
export default MyCachingModuleProvider
|
||
```
|
||
|
||
### Identifier
|
||
|
||
Every caching module provider must have an `identifier` static property. The provider's ID
|
||
will be stored as `lp_{identifier}_{id}`, where `id` is the ID you set in your `medusa-config.ts` file.
|
||
|
||
For example:
|
||
|
||
```ts
|
||
class MyCachingModuleProvider implements ICachingProviderService {
|
||
static identifier = "my-cache"
|
||
// ...
|
||
}
|
||
```
|
||
|
||
### clear
|
||
|
||
This method clears data from the cache. If no options are specified, all items matching the key or tags should be cleared.
|
||
Otherwise, if `options.autoInvalidate` is `true`, only items that were set with `options.autoInvalidate: true` should be cleared.
|
||
|
||
Items with `options.autoInvalidate: false` should only be cleared when no options are provided.
|
||
|
||
If neither `key` nor `tags` are provided, nothing should be cleared.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
async clear({ key, tags, options, }: {
|
||
key?: string;
|
||
tags?: string[];
|
||
options?: { autoInvalidate?: boolean }
|
||
}): Promise<void> {
|
||
if (!options) {
|
||
// clear all items
|
||
await this.client.invalidate({ key, tags })
|
||
} else if (options.autoInvalidate) {
|
||
// clear only items with autoInvalidate option set to true
|
||
const keysToDelete: string[] = []
|
||
const storedOptions = await this.client.get({ key, tags, pipeline: "options" })
|
||
storedOptions.forEach((item) => {
|
||
if (item.autoInvalidate) {
|
||
keysToDelete.push(item.key as string)
|
||
}
|
||
})
|
||
await this.client.invalidate({ keys: keysToDelete })
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"param0","type":"`object`","description":"The parameters for clearing the item(s).","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"key","type":"`string`","description":"The key of the item to clear.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"tags","type":"`string`[]","description":"The tags of the items to clear. All items with any of the provided tags should be cleared.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"options","type":"`object`","description":"Options for clearing the item(s). The options should be matched against the stored options when the item was set.\nFor example, if the item was set with `autoInvalidate: true`, it will only be cleared if the `autoInvalidate` option is also set to `true`.\nIf not provided, all items matching the key or tags should be cleared regardless of their options.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"autoInvalidate","type":"`boolean`","description":"Whether to clear item(s) that were set to automatically invalidate.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="clear"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"A promise that resolves when the item(s) have been cleared.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="clear"/>
|
||
|
||
### get
|
||
|
||
This method retrieves data from the cache either by `key` or `tags`. If neither `key` nor `tags` are provided, `null` should be returned.
|
||
If both `key` and `tags` are provided, `key` should take precedence over `tags`.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyCachingModuleProvider implements ICachingProviderService {
|
||
// ...
|
||
async get({ key, tags }: { key?: string; tags?: string[] }): Promise<any> {
|
||
// Assuming you're using a client to get data
|
||
if (key) {
|
||
return await this.client.get({ key })
|
||
}
|
||
if (tags) {
|
||
return await this.client.getByTags({ tags })
|
||
}
|
||
return null
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"param0","type":"`object`","description":"The parameters for retrieving the item.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"key","type":"`string`","description":"The key of the item to retrieve. If both are provided, `key` should take precedence over `tags`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"tags","type":"`string`[]","description":"The tags of the items to retrieve. All items with any of the provided tags should be retrieved.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="get"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<any>","optional":false,"defaultValue":"","description":"The item(s) that was stored in the cache, or `null` if not found.","expandable":false,"children":[{"name":"any","type":"`any`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="get"/>
|
||
|
||
### set
|
||
|
||
This method stores data in the cache. It should also store the options with the item,
|
||
allowing you to later to check the `autoInvalidate` option when clearing the item.
|
||
|
||
#### Example
|
||
|
||
```ts
|
||
class MyCachingModuleProvider implements ICachingProviderService {
|
||
// ...
|
||
async set({ key, data, ttl, tags, options }: {
|
||
key: string;
|
||
data: any;
|
||
ttl?: number;
|
||
tags?: string[];
|
||
options?: { autoInvalidate?: boolean }
|
||
}): Promise<void> {
|
||
// Assuming you're using a client to set data
|
||
await this.client.set({ key, data, ttl, tags })
|
||
await this.client.set({ key, data: options, pipeline: "options" })
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Parameters
|
||
|
||
<TypeList types={[{"name":"param0","type":"`object`","description":"The parameters for storing the item.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"key","type":"`string`","description":"The key of the item to store.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"data","type":"`object`","description":"The data to store in the cache.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"ttl","type":"`number`","description":"The time-to-live (TTL in seconds) value in seconds.\nIf not provided, the default TTL value configured in the provider should be used.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"tags","type":"`string`[]","description":"The tags of the items to store. Items should be grouped together using tags for retrieval or invalidation.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"options","type":"`object`","description":"Options for storing the item. The options should be stored with the item, allowing you to later match against them when clearing the item.\nFor example, if you set `autoInvalidate: false`, the item will only be invalidated when calling the `clear` method directly with the same key or tags.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"autoInvalidate","type":"`boolean`","description":"Whether to automatically invalidate the item when related data changes.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="set"/>
|
||
|
||
#### Returns
|
||
|
||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"A promise that resolves when the item has been stored.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="set"/>
|
||
|
||
---
|
||
|
||
## 3. Create Module Definition File
|
||
|
||
Create the file `src/modules/my-caching/index.ts` with the following content:
|
||
|
||
```ts title="src/modules/my-caching/index.ts"
|
||
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
|
||
import MyCachingProviderService from "./service"
|
||
|
||
export default ModuleProvider(Modules.CACHING, {
|
||
services: [MyCachingProviderService],
|
||
})
|
||
```
|
||
|
||
This exports the module provider's definition, indicating that the `MyCachingProviderService` is the module provider's service.
|
||
|
||
---
|
||
|
||
## 4. Use Module Provider
|
||
|
||
To use your Caching Module Provider, add it to the `providers` array of the Caching Module in `medusa-config.ts`:
|
||
|
||
```ts title="medusa-config.ts"
|
||
module.exports = defineConfig({
|
||
// ...
|
||
modules: [
|
||
{
|
||
resolve: "@medusajs/medusa/caching",
|
||
options: {
|
||
providers: [
|
||
{
|
||
// if module provider is in a plugin, use `plugin-name/providers/my-caching`
|
||
resolve: "./src/modules/my-caching",
|
||
id: "my-caching",
|
||
// set this if you want this provider to be used by default
|
||
// and you have other Caching Module Providers registered.
|
||
is_default: true,
|
||
options: {
|
||
url: "http://example.com",
|
||
// provider options...
|
||
}
|
||
},
|
||
]
|
||
}
|
||
}
|
||
]
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 5. Test it Out
|
||
|
||
To test out your Caching Module Provider, create a simple API route that retrieves cached data with Query:
|
||
|
||
```ts title="src/api/test-caching/route.ts"
|
||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
||
|
||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||
const query = req.scope.resolve("query")
|
||
|
||
const { data } = await query.graph({
|
||
entity: "product",
|
||
fields: ["id", "title"],
|
||
}, {
|
||
cache: {
|
||
enable: true,
|
||
providers: ["my-caching"], // use your provider id here
|
||
}
|
||
})
|
||
|
||
res.status(200).json({ data })
|
||
}
|
||
```
|
||
|
||
Then, start your Medusa server with the following command:
|
||
|
||
```bash npm2yarn
|
||
npm run dev
|
||
```
|
||
|
||
Next, send a `GET` request to `http://localhost:9000/test-caching`:
|
||
|
||
```bash
|
||
curl http://localhost:9000/test-caching
|
||
```
|
||
|
||
You will receive a response with the list of products. The first time you make this request, the products will be fetched from the database and cached in memory. Subsequent requests will retrieve the products from the cache, which improves performance.
|
||
|
||
---
|
||
|
||
## Useful Guides
|
||
|
||
- [How to Use Caching Module](/references/caching-service)
|