Files
medusa-store/www/apps/resources/references/locking/interfaces/locking.ILockingProvider/page.mdx
2025-05-08 16:40:17 +03:00

445 lines
29 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
slug: /references/locking-module-provider
tags:
- locking
- server
- how to
sidebar_label: Create Locking Provider
keywords:
- locking
- provider
- integration
---
import { TypeList } from "docs-ui"
# How to Create a Locking Module Provider
In this document, youll learn how to create a Locking Module Provider and the methods you must implement in its main service.
---
## Implementation Example
As you implement your Locking 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 Locking Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/locking-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-locking`.
If you're creating the module provider in a plugin, create it under the `src/providers` directory. For example, `src/providers/my-locking`.
<Note>
The rest of this guide always uses the `src/modules/my-locking` directory as an example.
</Note>
---
## 2. Create the Locking Module Provider Service
Create the file `src/modules/my-locking/service.ts` that holds the module provider's main service. It must implement the `ILockingProvider` interface imported from `@medusajs/framework/types`:
```ts title="src/modules/my-locking/service.ts"
import { ILockingProvider } from "@medusajs/framework/types"
type Options = {
url: string
}
class MyLockingProviderService implements ILockingProvider {
// TODO implement methods
}
export default MyLockingProviderService
```
### 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 the constructor.
#### Example
```ts
import { ILockingProvider } from "@medusajs/framework/types"
import { Logger } from "@medusajs/framework/types"
type InjectedDependencies = {
logger: Logger
}
type Options = {
url: string
}
class MyLockingProviderService implements ILockingProvider {
static identifier = "my-lock"
protected logger_: Logger
protected options_: Options
// assuming you're initializing a client
protected client
constructor (
{ logger }: InjectedDependencies,
options: Options
) {
this.logger_ = logger
this.options_ = options
// assuming you're initializing a client
this.client = new Client(options)
}
// ...
}
export default MyLockingProviderService
```
### Identifier
Every locking module provider must have an `identifier` static property. The provider's ID
will be stored as `lp_{identifier}`.
For example:
```ts
class MyLockingProviderService implements ILockingProvider {
static identifier = "my-lock"
// ...
}
```
### execute
This method executes a given asynchronous job with a lock on the given keys. The Locking Module uses this method
when you call its `execute` method and your provider is the default provider, or you pass your provider's identifier to its `execute` method.
In the method, you should first try to acquire the lock on the given keys before the specified timeout passes.
Then, once the lock is acquired, you execute the job. Otherwise, if the timeout passes before the lock is acquired, you cancel the job.
#### Example
An example of how to implement the `execute` method:
```ts
// other imports...
import { Context } from "@medusajs/framework/types"
import { setTimeout } from "node:timers/promises"
class MyLockingProviderService implements ILockingProvider {
// ...
async execute<T>(
keys: string | string[],
job: () => Promise<T>,
args?: { timeout?: number },
sharedContext?: Context
): Promise<T> {
// TODO you can add actions using the third-party client you initialized in the constructor
const timeout = Math.max(args?.timeout ?? 5, 1)
const timeoutSeconds = Number.isNaN(timeout) ? 1 : timeout
const cancellationToken = { cancelled: false }
const promises: Promise<any>[] = []
if (timeoutSeconds > 0) {
promises.push(this.getTimeout(timeoutSeconds, cancellationToken))
}
promises.push(
this.acquire_(
keys,
{
expire: args?.timeout ? timeoutSeconds : 0,
},
cancellationToken
)
)
await Promise.race(promises)
try {
return await job()
} finally {
await this.release(keys)
}
}
private async getTimeout(
seconds: number,
cancellationToken: { cancelled: boolean }
): Promise<void> {
return new Promise(async (_, reject) => {
await setTimeout(seconds * 1000)
cancellationToken.cancelled = true
reject(new Error("Timed-out acquiring lock."))
})
}
}
```
In this example, you first determine the timeout for acquiring the lock. You also create a `cancellationToken` object that you'll use to determine if the lock aquisition has timed out.
You then create an array of the following promises:
- A timeout promise that, if the lock acquisition takes longer than the timeout, sets the `cancelled` property of the `cancellationToken` object to `true`.
- A promise that acquires the lock. You use a private `acquire_` method which you can find its implementation in the `aquire` method's example. If the first promise
resolves and cancels the lock acquisition, the lock will not be acquired.
Finally, if the lock is acquired, you execute the job and release the lock after the job is done using the `release` method.
#### Type Parameters
<TypeList types={[{"name":"T","type":"`object`","description":"The type of the job's result.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
#### Parameters
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to lock during the job's execution.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"job","type":"() => Promise&#60;T&#62;","description":"The asynchronous job to execute while the keys are locked.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for the job execution.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"timeout","type":"`number`","description":"The timeout (in seconds) for acquiring the lock. If the time out is passed, the job is canceled and the lock is released.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"`boolean`","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;T&#62;","optional":false,"defaultValue":"","description":"The result of the job.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
### acquire
This method acquires a lock on the given keys. The Locking Module uses this method when you call its `acquire` method and your provider is the default provider,
or you pass your provider's identifier to its `acquire` method.
In this method, you should only aquire the lock if the timeout hasn't passed. As explained in the [execute](page.mdx#execute) method's example,
you can use a `cancellationToken` object to determine if the lock acquisition has timed out.
If the lock aquisition isn't canceled, you should aquire the lock, setting its expiry and owner. You should account for the following scenarios:
- The lock doesn't have an owner and you don't pass an owner, in which case the lock can be extended or released by anyone.
- The lock doesn't have an owner or has the same owner that you pass, in which case you can extend the lock's expiration time and set the owner.
- The lock has an owner, but you pass a different owner, in which case the method should throw an error.
#### Example
An example of how to implement the `acquire` method:
```ts
type ResolvablePromise = {
promise: Promise<any>
resolve: () => void
}
class MyLockingProviderService implements ILockingProvider {
// ...
async acquire(
keys: string | string[],
args?: {
ownerId?: string | null
expire?: number
awaitQueue?: boolean
}
): Promise<void> {
return this.acquire_(keys, args)
}
async acquire_(
keys: string | string[],
args?: {
ownerId?: string | null
expire?: number
awaitQueue?: boolean
},
cancellationToken?: { cancelled: boolean }
): Promise<void> {
keys = Array.isArray(keys) ? keys : [keys]
const { ownerId, expire } = args ?? {}
for (const key of keys) {
if (cancellationToken?.cancelled) {
throw new Error("Timed-out acquiring lock.")
}
// assuming your client has this method and it validates the owner and expiration
const result = await this.client.acquireLock(key, ownerId, expire)
if (result !== 1) {
throw new Error(`Failed to acquire lock for key "${key}"`)
}
}
}
}
```
In this example, you add a private `acquire_` method that you use to acquire the lock. This method accepts an additional `cancellationToken` argument that you can use to determine if the lock acquisition has timed out.
You can then use this method in other methods, such as the `execute` method.
In the `acquire_` method, you loop through the keys and try to acquire the lock on each key if the lock acquisition hasn't timed out. If the lock acquisition fails, you throw an error.
This method assumes that the client you're integrating has a method called `acquireLock` that validates the owner and expiration time, and returns `1` if the lock is successfully acquired.
#### Parameters
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to acquire the lock on.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for acquiring the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of the lock's owner. If specified, only the owner can release the lock or extend its expiration time.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"expire","type":"`number`","description":"The expiration time (in seconds) for the lock. If the lock is already acquired and the owner is the same, the expiration time is extended\nby the value passed. If not specified, the lock does not expire.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"`boolean`","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;void&#62;","optional":false,"defaultValue":"","description":"Resolves when the lock is acquired.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
### release
This method releases a lock on the given keys. The Locking Module uses this method when you call its `release` method and your provider is the default provider,
or you pass your provider's identifier to its `release` method.
In this method, you should release the lock on the given keys. If the lock has an owner, you should only release the lock if the owner is the same as the one passed.
#### Example
An example of how to implement the `release` method:
```ts
// other imports...
import { promiseAll } from "@medusajs/framework/utils"
class MyLockingProviderService implements ILockingProvider {
// ...
async release(
keys: string | string[],
args?: { ownerId?: string | null },
sharedContext?: Context
): Promise<boolean> {
const ownerId = args?.ownerId ?? "*"
keys = Array.isArray(keys) ? keys : [keys]
const releasePromises = keys.map(async (key) => {
// assuming your client has this method and it validates the owner
const result = await this.client.releaseLock(key, ownerId)
return result === 1
})
const results = await promiseAll(releasePromises)
return results.every((released) => released)
}
}
```
In this example, you loop through the keys and try to release the lock on each key using the client you're integrating. This implementation assumes that the client validates
ownership of the lock and returns a result of `1` if the lock is successfully released.
#### Parameters
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to release the lock from.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for releasing the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of the lock's owner. The lock can be released either if it doesn't have an owner, or\nif its owner ID matches the one passed in this property.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"`boolean`","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;boolean&#62;","optional":false,"defaultValue":"","description":"Whether the lock was successfully released. If the lock has a different owner than the one passed, the method returns `false`.","expandable":false,"children":[{"name":"boolean","type":"`boolean`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
### releaseAll
This method releases all locks. The Locking Module uses this method when you call its `releaseAll` method and your provider is the default provider,
or you pass your provider's identifier to its `releaseAll` method.
In this method, you should release all locks if no owner is passed. If an owner is passed, you should only release the locks that the owner has acquired.
#### Example
An example of how to implement the `releaseAll` method:
```ts
class MyLockingProviderService implements ILockingProvider {
// ...
async releaseAll(
args?: { ownerId?: string | null },
sharedContext?: Context
): Promise<void> {
const ownerId = args?.ownerId ?? "*"
await this.client.releaseAllLock(ownerId)
}
}
```
In this example, you release all locks either of all owners or the owner passed as an argument. This implementation assumes that the client you're integrating has a method called `releaseAllLock` that releases all locks
for all owners or a specific owner.
#### Parameters
<TypeList types={[{"name":"args","type":"`object`","description":"Additional arguments for releasing the locks.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of a lock owner. If specified, all locks that the owner has acquired are released.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"preventReleaseEvents","type":"`boolean`","description":"preventReleaseEvents","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
#### Returns
<TypeList types={[{"name":"Promise","type":"Promise&#60;void&#62;","optional":false,"defaultValue":"","description":"This method releases all locks. The Locking Module uses this method when you call its `releaseAll` method and your provider is the default provider,\nor you pass your provider's identifier to its `releaseAll` method.\n\nIn this method, you should release all locks if no owner is passed. If an owner is passed, you should only release the locks that the owner has acquired.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
---
## 3. Create Module Definition File
Create the file `src/modules/my-locking/index.ts` with the following content:
```ts title="src/modules/my-locking/index.ts"
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
import MyLockingProviderService from "./service"
export default ModuleProvider(Modules.LOCKING, {
services: [MyLockingProviderService],
})
```
This exports the module provider's definition, indicating that the `MyLockingProviderService` is the module provider's service.
---
## 4. Use Module Provider
To use your Locking Module Provider, add it to the `providers` array of the Locking Module in `medusa-config.ts`:
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/payment",
options: {
providers: [
{
// if module provider is in a plugin, use `plugin-name/providers/my-locking`
resolve: "./src/modules/my-locking",
id: "my-lock",
// set this if you want this provider to be used by default
// and you have other Locking Module Providers registered.
is_default: true,
options: {
url: "http://example.com",
// provider options...
}
},
]
}
}
]
})
```
---
## 5. Test it Out
When you start the Medusa application, if your Locking Module Provider is the only registered provider without enabling `is_default`, you'll see the following message:
```bash
info: Locking module: Using "my-lock" as default.
```
This indicates that your Locking Module Provider is being used as the default provider.
The Locking Module will now use your provider to handle all locking operations.
---
## Useful Guides
- [How to Use Locking Module](/references/locking-service)