### Start Medusa Development Server Source: https://docs.medusajs.com/resources/infrastructure-modules/notification/local This command starts the Medusa development server, which is necessary to run and test the application's features, including the notification system. ```bash ❯yarn dev ``` -------------------------------- ### Install Memcached Client using npm/yarn Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Installs the 'memjs' client, which is required to interact with Memcached. This command can be executed using either npm or yarn. ```bash npm install memjs ``` ```bash yarn add memjs ``` -------------------------------- ### Start Medusa Application Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html This command starts the Medusa application using npm, which will trigger the registration and connection of the Redis Cache Module. Successful connection is indicated by a log message in the terminal. ```bash npm run dev ``` -------------------------------- ### Install Dependencies for Medusa App Source: https://docs.medusajs.com/resources/infrastructure-modules/notification/local This snippet shows the commands to install project dependencies for a Medusa application using different package managers. It is a prerequisite for running the development server. ```bash * yarn * pnpm * npm ``` -------------------------------- ### Send GET Request to Test Cache Endpoint (curl) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Send a GET request to the '/test-cache' endpoint using curl to retrieve products. This tests the caching mechanism implemented in the API route. ```shell curl http://localhost:9000/test-cache ``` -------------------------------- ### Create Test API Route (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Create a GET API route at 'src/api/test-cache/route.ts' to test Memcached caching. This route retrieves products with caching enabled, using a specific key for easy verification. ```typescript 1import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" 2 3export const GET = async (req: MedusaRequest, res: MedusaResponse) => { 4 const query = req.scope.resolve("query") 5 6 // Test caching with a simple query 7 const { data } = await query.graph({ 8 entity: "product", 9 fields: ["id", "title", "handle"], 10 }, { 11 cache: { 12 enable: true, 13 // For testing purposes 14 key: "test-cache-products", 15 // If you didn't set is_default to true, uncomment the following line 16 // providers: ["caching-memcached"], 17 }, 18 }) 19 20 res.status(200).json({ 21 message: "Products retrieved with Memcached caching", 22 data, 23 }) 24} ``` -------------------------------- ### Retrieve File URL using Medusa File Module in a Workflow (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/file/index.html Demonstrates how to resolve the File Module service within a Medusa workflow step and use the `retrieveFile` method to get the URL of a specific file. This example requires the `@medusajs/framework` and `@medusajs/framework/workflows-sdk` packages. ```typescript import { Modules } from "@medusajs/framework/utils" import { createStep, createWorkflow, StepResponse, WorkflowResponse, } from "@medusajs/framework/workflows-sdk" const step1 = createStep( "step-1", async ({}, { container }) => { const fileModuleService = container.resolve( Modules.FILE ) const { url } = await fileModuleService.retrieveFile("image.png") return new StepResponse(url) } ) export const workflow = createWorkflow( "workflow-1", () => { const url = step1() return new WorkflowResponse(url) } ) ``` -------------------------------- ### Implement Cache Get Method with Memcached (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/create/index.html Provides an example implementation for the `get` method of the ICacheService interface using Memcached. It retrieves data from the cache by key, handling potential errors and parsing JSON data. ```typescript async get(cacheKey: string): Promise { 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) } } }) }) } ``` -------------------------------- ### Implement get method for MemcachedCachingProviderService Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html The `get` method retrieves cached data from Memcached. It supports fetching by a specific key, optionally validating associated tags, and decompressing data if necessary. If no key is provided, it can retrieve data associated with a list of tags. It returns the cached data or null if not found or invalid. ```typescript class MemcachedCachingProviderService implements ICachingProviderService { // ... async get({ key, tags, }: { key?: string tags?: string[] }): Promise { if (key) { const prefixedKey = this.CACHE_PREFIX + key // Get the stored tags for this key and validate them const keyTagsKey = `${this.KEY_TAGS_PREFIX}${key}` const keyTagsResult = await this.client.get(keyTagsKey) if (keyTagsResult.value) { const storedTags = JSON.parse(keyTagsResult.value.toString()) const tagNames = Object.keys(storedTags) const isValid = await this.validateKeyByTags(key, tagNames) if (!isValid) { return null } } const result = await this.client.get(prefixedKey) if (result.value) { const dataString = result.value.toString() // Check if data is compressed (look for compression flag in options) const optionsKey = this.OPTIONS_PREFIX + key const optionsResult = await this.client.get(optionsKey) let compressed = false if (optionsResult.value) { const options = JSON.parse(optionsResult.value.toString()) compressed = options.compressed || false } // Decompress if needed const decompressedData = await this.decompressData(dataString, compressed) return JSON.parse(decompressedData) } return null } if (tags && tags.length > 0) { // Retrieve data by tags - get all keys associated with the tags return await this.getByTags(tags) } return null } } ``` -------------------------------- ### Retrieve Cached Data from Memcached (Terminal) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Retrieve the cached data from Memcached using the 'get' command and the key specified in the API route ('medusa:test-cache-products'). The key is prefixed with 'medusa:' by default. ```shell ❯get medusa:test-cache-products ``` -------------------------------- ### Verify Redis Caching with cURL Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/providers/redis This command-line instruction demonstrates how to make a GET request to the `/cache-product` endpoint using cURL. It's used to verify that the Redis Caching Module is functioning correctly by retrieving cached product data. ```bash curl http://localhost:9000/cache-product ``` -------------------------------- ### Test Redis Cache Module Connection Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/redis This snippet shows the command to start the Medusa application using yarn. After starting, you should see a confirmation message in the terminal logs indicating a successful connection to Redis. ```bash yarn dev ``` -------------------------------- ### Configure Notification Module Providers in medusa-config.ts (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/notification/index.html This example shows how to configure Notification Module providers in the `medusa-config.ts` file. It demonstrates registering the local notification provider and specifying the 'email' channel it supports. This configuration is essential for defining which providers handle specific notification channels. ```typescript import { Modules } from "@medusajs/framework/utils" // ... module.exports = { // ... modules: [ // ... { resolve: "@medusajs/medusa/notification", options: { providers: [ // ... { resolve: "@medusajs/medusa/notification-local", id: "notification", options: { channels: ["email"], }, }, ], }, }, ], } ``` -------------------------------- ### Implement Memcached Caching Provider get Method in TypeScript Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached The 'get' method retrieves cached data by key or tags from Memcached. It handles key validation using associated tags, decompresses data if necessary, and supports retrieving data by multiple tags. Dependencies include the Memcached client and helper methods for tag validation and decompression. Returns cached data or null if not found or invalid. ```typescript class MemcachedCachingProviderService implements ICachingProviderService { // ... async get({ key, tags, }: { key?: string tags?: string[] }): Promise { if (key) { const prefixedKey = this.CACHE_PREFIX + key // Get the stored tags for this key and validate them const keyTagsKey = `${this.KEY_TAGS_PREFIX}${key}` const keyTagsResult = await this.client.get(keyTagsKey) if (keyTagsResult.value) { const storedTags = JSON.parse(keyTagsResult.value.toString()) const tagNames = Object.keys(storedTags) const isValid = await this.validateKeyByTags(key, tagNames) if (!isValid) { return null } } const result = await this.client.get(prefixedKey) if (result.value) { const dataString = result.value.toString() // Check if data is compressed (look for compression flag in options) const optionsKey = this.OPTIONS_PREFIX + key const optionsResult = await this.client.get(optionsKey) let compressed = false if (optionsResult.value) { const options = JSON.parse(optionsResult.value.toString()) compressed = options.compressed || false } // Decompress if needed const decompressedData = await this.decompressData(dataString, compressed) return JSON.parse(decompressedData) } return null } if (tags && tags.length > 0) { // Retrieve data by tags - get all keys associated with the tags return await this.getByTags(tags) } return null } } ``` -------------------------------- ### Implement Event Emission Logic in TypeScript Source: https://docs.medusajs.com/resources/infrastructure-modules/event/create Provides an example implementation for the `emit` method within a custom Event Module service. This logic logs received events and includes a placeholder for pushing the event to a messaging system. ```typescript class MyEventService extends AbstractEventBusModuleService { async emit(data: Message | Message[], options: Record): Promise { const events = Array.isArray(data) ? data : [data] for (const event of events) { console.log(`Received the event ${event.name} with data ${event.data}`) // TODO push the event somewhere } } // ... } ``` -------------------------------- ### Import zlib and promisify for Compression Utilities Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Imports necessary functions from the 'zlib' and 'util' modules to enable asynchronous compression and decompression. 'promisify' is used to convert callback-based zlib functions into promise-based ones for easier async/await usage. ```typescript import { deflate, inflate } from "zlib" import { promisify } from "util" const deflateAsync = promisify(deflate) const inflateAsync = promisify(inflate) ``` -------------------------------- ### API Route to Execute Product Caching Workflow Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/providers/redis/index.html This code snippet shows an example of an API route in MedusaJS that triggers the `cacheProductsWorkflow`. The `GET` handler executes the workflow and returns the cached product data. This demonstrates how to integrate workflows that utilize the Redis Caching Module Provider into your application's API. ```typescript import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http" import { cacheProductsWorkflow } from "../../workflows/cache-products" export const GET = async (req: MedusaRequest, res: MedusaResponse) => { const { result } = await cacheProductsWorkflow(req.scope) .run({}) res.status(200).json(result) } ``` -------------------------------- ### Connect to Memcached CLI (Terminal) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Connect to your Memcached server using the memcached-cli tool. Replace 'localhost:11211' with your Memcached server URL if it differs. ```shell ❯yarn memcached-cli localhost:11211 # Replace with your Memcached server URL ``` -------------------------------- ### Implement Data Compression and Decompression Methods (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Adds private methods 'compressData' and 'decompressData' to the 'MemcachedCachingProviderService' class. 'compressData' compresses string data using zlib if enabled and above a threshold, returning the data and a compression status. 'decompressData' decompresses data if it was previously compressed. ```typescript class MemcachedCachingProviderService implements ICachingProviderService { // ... private async compressData(data: string): Promise<{ data: string; compressed: boolean }> { if (!this.compressionEnabled || data.length < this.compressionThreshold) { return { data, compressed: false } } const buffer = Buffer.from(data, "utf8") const compressed = await deflateAsync(buffer) const compressedData = compressed.toString("base64") // Only use compression if it actually reduces size if (compressedData.length < data.length) { return { data: compressedData, compressed: true } } return { data, compressed: false } } private async decompressData( data: string, compressed: boolean ): Promise { if (!compressed) { return data } const buffer = Buffer.from(data, "base64") const decompressed = await inflateAsync(buffer) return decompressed.toString("utf8") } } ``` -------------------------------- ### Create Memcached Module Provider Service (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached This TypeScript code defines the MemcachedCachingProviderService class, which implements the ICachingProviderService interface. It initializes protected properties for dependencies, Memcached client, and configuration options. The constructor injects the memcachedClient and module options, setting up cache prefixes and compression settings. ```typescript import { ICachingProviderService } from "@medusajs/framework/types" import * as memjs from "memjs" import { ModuleOptions } from "./loaders/connection" type InjectedDependencies = { memcachedClient: memjs.Client } class MemcachedCachingProviderService implements ICachingProviderService { static identifier = "memcached-cache" protected client: memjs.Client protected options_: ModuleOptions protected readonly CACHE_PREFIX: string protected readonly TAG_PREFIX: string protected readonly OPTIONS_PREFIX: string protected readonly KEY_TAGS_PREFIX: string protected readonly TAG_KEYS_PREFIX: string protected readonly compressionEnabled: boolean protected readonly compressionThreshold: number protected readonly compressionLevel: number protected readonly defaultTtl: number constructor( { memcachedClient }: InjectedDependencies, options: ModuleOptions ) { this.client = memcachedClient this.options_ = options // Set all prefixes with the main prefix const mainPrefix = options.cachePrefix || "medusa" this.CACHE_PREFIX = `${mainPrefix}:` this.TAG_PREFIX = `${mainPrefix}:tag:` this.OPTIONS_PREFIX = `${mainPrefix}:opt:` this.KEY_TAGS_PREFIX = `${mainPrefix}:key_tags:` this.TAG_KEYS_PREFIX = `${mainPrefix}:tag_keys:` // Set compression options this.compressionEnabled = options.compression?.enabled ?? true this.compressionThreshold = options.compression?.threshold ?? 2048 // 2KB default this.compressionLevel = options.compression?.level ?? 6 // Balanced compression // Set default TTL this.defaultTtl = options.defaultTtl ?? 3600 // 1 hour default } } ``` -------------------------------- ### Implement Data Compression Method using zlib Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Implements the 'compressData' method within the 'MemcachedCachingProviderService' class. This method compresses string data using zlib.deflate if compression is enabled and the data exceeds a specified threshold. It returns the compressed data (base64 encoded) and a flag indicating if compression was applied. Compression is only used if it results in a smaller data size. ```typescript private async compressData(data: string): Promise<{ data: string; compressed: boolean }> { if (!this.compressionEnabled || data.length < this.compressionThreshold) { return { data, compressed: false } } const buffer = Buffer.from(data, "utf8") const compressed = await deflateAsync(buffer) const compressedData = compressed.toString("base64") // Only use compression if it actually reduces size if (compressedData.length < data.length) { return { data: compressedData, compressed: true } } return { data, compressed: false } } ``` -------------------------------- ### Memcached Connection Log (Terminal) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Verify the Memcached connection by observing the application logs. Successful connection is indicated by messages like 'Connecting to Memcached...' and 'Successfully connected to Memcached'. ```shell ❯info: Connecting to Memcached... ❯info: Successfully connected to Memcached ``` -------------------------------- ### Implement Cache Get Method using Memcached in TypeScript Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/create This TypeScript code demonstrates how to implement the `get` method of the ICacheService interface using Memcached. It retrieves data from the cache by key, handling potential errors and parsing the JSON stringified data. The method returns the cached item or null if not found. ```typescript class MyCacheService implements ICacheService { // ... async get(cacheKey: string): Promise { 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) } } }) }) } } ``` -------------------------------- ### Set Memcached Environment Variables (Terminal) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached Configure the Memcached server URLs by setting the MEMCACHED_SERVERS environment variable in your .env file. You can also set optional variables like username and password. ```shell ❯MEMCACHED_SERVERS=127.0.0.1:11211 # Comma-separated list of Memcached server URLs ❯# Add other optional variables as needed ``` -------------------------------- ### Retrieve Cached Data using memcached-cli (Bash) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Demonstrates how to retrieve cached data from Memcached using the `memcached-cli` tool. It shows how to connect to the Memcached server and fetch data using the specified cache key, including the default `medusa:` prefix. ```bash npx memcached-cli localhost:11211 # Replace with your Memcached server URL get medusa:test-cache-products ``` -------------------------------- ### Implement setKeyTags Helper Method for Memcached Tag Management Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached The `setKeyTags` method manages tags for Memcached by storing mappings between keys and tags. It takes a cache key, an array of tags, and Memcached set options. It iterates through tags, retrieves/sets namespace versions, updates tag-key lists, and finally stores the tag-namespace mapping for the given key. ```typescript class MemcachedCachingProviderService implements ICachingProviderService { // ... private async setKeyTags( key: string, tags: string[], setOptions: memjs.InsertOptions ): Promise { const timestamp = Math.floor(Date.now() / 1000) const tagNamespaces: Record = {} const operations: Promise[] = [] // Batch all namespace operations for (const tag of tags) { const tagKey = this.TAG_PREFIX + tag const tagKeysKey = `${this.TAG_KEYS_PREFIX}${tag}` // Get namespace version operations.push( (async () => { const result = await this.client.get(tagKey) if (!result.value) { tagNamespaces[tag] = timestamp.toString() await this.client.add(tagKey, timestamp.toString()) } else { tagNamespaces[tag] = result.value.toString() } })() ) // Add key to tag's key list operations.push( (async () => { const result = await this.client.get(tagKeysKey) let keys: string[] = [] if (result.value) { keys = JSON.parse(result.value.toString()) as string[] } if (!keys.includes(key)) { keys.push(key) await this.client.set(tagKeysKey, JSON.stringify(keys), setOptions) } })() ) } await Promise.all(operations) // Store the tag namespaces for this key const keyTagsKey = `${this.KEY_TAGS_PREFIX}${key}` const serializedTags = JSON.stringify(tagNamespaces) await this.client.set(keyTagsKey, serializedTags, setOptions) } } ``` -------------------------------- ### Implement Data Decompression Method using zlib Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Implements the 'decompressData' method within the 'MemcachedCachingProviderService' class. This method decompresses data using zlib.inflate if the 'compressed' flag is true. It takes base64 encoded data, decodes it, and then decompresses it back into a UTF-8 string. If 'compressed' is false, the original data is returned directly. ```typescript private async decompressData( data: string, compressed: boolean ): Promise { if (!compressed) { return data } const buffer = Buffer.from(data, "base64") const decompressed = await inflateAsync(buffer) return decompressed.toString("utf8") } ``` -------------------------------- ### Implement Medusa Cache Service Interface (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/create/index.html Defines the basic structure of a custom cache service by implementing the ICacheService interface from '@medusajs/framework/types'. This includes placeholder implementations for get, set, and invalidate methods. ```typescript import { ICacheService } from "@medusajs/framework/types" class MyCacheService implements ICacheService { get(key: string): Promise { throw new Error("Method not implemented.") } set(key: string, data: unknown, ttl?: number): Promise { throw new Error("Method not implemented.") } invalidate(key: string): Promise { throw new Error("Method not implemented.") } } export default MyCacheService ``` -------------------------------- ### Implement Memcached Set Method for Caching Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached The 'set' method adds data to the Memcached cache. It handles data serialization, compression, setting the Time-To-Live (TTL), and associating tags. Options like autoInvalidation are also stored. Operations are batched using Promise.all for efficiency. ```typescript class MemcachedCachingProviderService implements ICachingProviderService { // ... async set({ key, data, ttl, tags, options, }: { key: string data: any ttl?: number tags?: string[] options?: { autoInvalidate?: boolean } }): Promise { const prefixedKey = this.CACHE_PREFIX + key const serializedData = JSON.stringify(data) const setOptions: memjs.InsertOptions = {} // Use provided TTL or default TTL setOptions.expires = ttl ?? this.defaultTtl // Compress data if enabled const { data: finalData, compressed, } = await this.compressData(serializedData) // Batch operations for better performance const operations: Promise[] = [ this.client.set(prefixedKey, finalData, setOptions), ] // Always store options (including compression flag) to allow checking them later const optionsKey = this.OPTIONS_PREFIX + key const optionsData = { ...options, compressed } operations.push( this.client.set(optionsKey, JSON.stringify(optionsData), setOptions) ) // Handle tags using namespace simulation with batching if (tags && tags.length > 0) { operations.push(this.setKeyTags(key, tags, setOptions)) } await Promise.all(operations) } } ``` -------------------------------- ### Enable Automatic Invalidation with QueryGraphStep (Default Behavior) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/concepts/index.html The 'autoInvalidate' option for caching is enabled by default. This example demonstrates how to explicitly set it to 'true' when using QueryGraphStep, although it is not necessary as it reflects the default behavior. ```typescript const { data: products } = useQueryGraphStep({ entity: "product", fields: ["id", "title"], options: { cache: { enable: true, // This is enabled by default // autoInvalidate: true, }, }, }) ``` -------------------------------- ### Configure Event Module in medusa-config.ts (TypeScript) Source: https://docs.medusajs.com/resources/infrastructure-modules/event/create/index.html This example shows how to integrate a custom event module into the Medusa.js configuration. The module is added to the `modules` array under the `eventBus` key, specifying its resolution path and any necessary options. ```typescript import { Modules } from "@medusajs/framework/utils" // ... module.exports = defineConfig({ // ... modules: [ { resolve: "./src/modules/my-event", options: { // any options }, }, ], }) ``` -------------------------------- ### Use Redis Provider Explicitly in Workflow Step Source: https://docs.medusajs.com/resources/infrastructure-modules/locking/redis/index.html Example of explicitly using the Redis Locking Module Provider within a Medusa workflow step. It shows how to acquire a lock using the provider's identifier. ```typescript import { Modules } from "@medusajs/framework/utils" import { createStep } from "@medusajs/framework/workflows-sdk" const step1 = createStep( "step-1", async ({}, { container }) => { const lockingModuleService = container.resolve( Modules.LOCKING ) await lockingModuleService.acquire("prod_123", { provider: "lp_locking-redis", }) } ) ``` -------------------------------- ### Create Memcached Connection Loader in TypeScript Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached This TypeScript code defines the options for the Memcached module and an asynchronous loader function. The loader establishes a connection to Memcached using the 'memjs' library, tests the connection by retrieving server stats, and registers the Memcached client in the container. It handles potential connection errors and logs connection status. ```typescript import { LoaderOptions } from "@medusajs/framework/types" import * as memjs from "memjs" export type ModuleOptions = { serverUrls?: string[] username?: string password?: string options?: memjs.ClientOptions cachePrefix?: string defaultTtl?: number // Default TTL in seconds compression?: { enabled?: boolean threshold?: number // Minimum size in bytes to compress level?: number // Compression level (1-9) } } export default async function connection({ container, options, }: LoaderOptions) { const logger = container.resolve("logger") const { serverUrls = ["127.0.0.1:11211"], username, password, options: clientOptions, } = options || {} try { logger.info("Connecting to Memcached...") // Create Memcached client const client = memjs.Client.create(serverUrls.join(","), { username, password, ...clientOptions, }) // Test the connection await new Promise((resolve, reject) => { client.stats((err, stats) => { if (err) { logger.error("Failed to connect to Memcached:", err) reject(err) } else { logger.info("Successfully connected to Memcached") resolve() } }) }) // Register the client in the container container.register({ memcachedClient: { resolve: () => client, }, }) } catch (error) { logger.error("Failed to initialize Memcached connection:", error) throw error } } ``` -------------------------------- ### Register Redis Cache Module in medusa-config.ts Source: https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html This code snippet demonstrates how to register the Redis Cache Module in your Medusa application's configuration file (`medusa-config.ts`). It requires Redis to be installed and running. The `resolve` property points to the module, and `options` configure the Redis connection URL. ```typescript module.exports = defineConfig({ // ... modules: [ { resolve: "@medusajs/medusa/cache-redis", options: { redisUrl: process.env.CACHE_REDIS_URL, }, }, ], }) ``` -------------------------------- ### Explicitly Use PostgreSQL Provider in Workflow Step Source: https://docs.medusajs.com/resources/infrastructure-modules/locking/postgres This code example demonstrates how to explicitly use the PostgreSQL Locking Module Provider within a Medusa workflow step. It shows how to resolve the locking module service and acquire a lock using the provider's identifier (`lp_locking-postgres`). ```typescript import { Modules } from "@medusajs/framework/utils" import { createStep } from "@medusajs/framework/workflows-sdk" const step1 = createStep( "step-1", async ({}, { container }) => { const lockingModuleService = container.resolve( Modules.LOCKING ) await lockingModuleService.acquire("prod_123", { provider: "lp_locking-postgres", }) } ) ``` -------------------------------- ### Add Environment Variables (Bash) Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/guides/memcached/index.html Sets the necessary environment variables for Memcached configuration. The `MEMCACHED_SERVERS` variable specifies the connection URLs for the Memcached instances. Optional variables like username and password can also be configured. ```bash MEMCACHED_SERVERS=127.0.0.1:11211 # Comma-separated list of Memcached server URLs # Add other optional variables as needed ``` -------------------------------- ### Specify Caching Provider for Caching Module Service Source: https://docs.medusajs.com/resources/infrastructure-modules/caching/providers/index.html This TypeScript example shows how to use the Caching Module's service to cache data and explicitly choose a provider, like Memcached, for that operation. The `providers: ["caching-memcached"]` array in the `set` method ensures the specified provider is used, bypassing the default. ```typescript const cachingModuleService = container.resolve(Modules.CACHING) await cachingModuleService.set({ key: "product-list", data: products, providers: ["caching-memcached"], // Specify Memcached provider }) ``` -------------------------------- ### Run Database Migrations for PostgreSQL Locking Module Source: https://docs.medusajs.com/resources/infrastructure-modules/locking/postgres After registering the PostgreSQL Locking Module Provider, this command is used to run the necessary database migrations. This creates the required `locking` table in your PostgreSQL database to store lock information. ```bash yarn medusa db:migrate ```