docs: add documentation for Locking Module (#11824)

* add locking docs

* fix main navbar

* added implementation example links

* generate refs

* update architecture

* fix vale error
This commit is contained in:
Shahed Nasser
2025-03-13 12:20:24 +02:00
committed by GitHub
parent 5cf0bf4d93
commit 28b0d08591
94 changed files with 15932 additions and 10857 deletions
@@ -71,11 +71,12 @@ You can replace any of the third-party services mentioned above to build your pr
[Architectural modules](!resources!/architectural-modules) integrate third-party services and systems for architectural features. Medusa has the following Architectural modules:
- [Workflow Engine Module](!resources!/architectural-modules/workflow-engine): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](!resources!/architectural-modules/workflow-engine/redis) to orchestrate workflows.
- [Cache Module](!resources!/architectural-modules/cache): Caches data that require heavy computation. You can integrate a custom module to handle the caching with services like Memcached, or use the existing [Redis Cache Module](!resources!/architectural-modules/cache/redis).
- [Event Module](!resources!/architectural-modules/event): A pub/sub system that allows you to subscribe to events and trigger them. You can integrate [Redis](!resources!/architectural-modules/event/redis) as the pub/sub system.
- [File Module](!resources!/architectural-modules/file): Manages file uploads and storage, such as upload of product images. You can integrate [AWS S3](!resources!/architectural-modules/file/s3) for file storage.
- [Locking Module](!resources!/architectural-modules/locking): Manages access to shared resources by multiple processes or threads, preventing conflict between processes and ensuring data consistency. You can integrate [Redis](!resources!/architectural-modules/locking/redis) for locking.
- [Notification Module](!resources!/architectural-modules/notification): Sends notifications to customers and users, such as for order updates or newsletters. You can integrate [SendGrid](!resources!/architectural-modules/notification/sendgrid) for sending emails.
- [Workflow Engine Module](!resources!/architectural-modules/workflow-engine): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](!resources!/architectural-modules/workflow-engine/redis) to orchestrate workflows.
All of the third-party services mentioned above can be replaced to help you build your preferred architecture and ecosystem.
+1 -1
View File
@@ -97,7 +97,7 @@ export const generatedEditDates = {
"app/learn/customization/integrate-systems/service/page.mdx": "2024-12-09T11:02:39.594Z",
"app/learn/customization/next-steps/page.mdx": "2024-12-06T14:34:53.356Z",
"app/learn/fundamentals/modules/architectural-modules/page.mdx": "2024-10-21T13:30:21.367Z",
"app/learn/introduction/architecture/page.mdx": "2025-03-11T15:28:01.187Z",
"app/learn/introduction/architecture/page.mdx": "2025-03-12T14:39:09.724Z",
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2024-12-09T15:54:08.713Z",
"app/learn/fundamentals/custom-cli-scripts/seed-data/page.mdx": "2024-12-09T14:38:06.385Z",
"app/learn/fundamentals/environment-variables/page.mdx": "2025-03-11T08:55:03.343Z",
File diff suppressed because it is too large Load Diff
+50 -10
View File
@@ -1,22 +1,68 @@
import { CardList } from "docs-ui"
export const metadata = {
title: `Cache Modules`,
title: `Cache Module`,
}
# {metadata.title}
In this document, you'll learn what a Cache Module is and how to use it in your Medusa application.
## What is a Cache Module?
A Cache Module is used to cache the results of computations such as price selection or various tax calculations.
The underlying database, third-party service, or caching logic is flexible since it's implemented in a module. You can choose from Medusas cache modules or create your own to support something more suitable for your architecture.
### Default Cache Module
By default, Medusa uses the [In-Memory Cache Module](./in-memory/page.mdx). This module uses a plain JavaScript Map object to store the cache data. While this is suitable for development, it's recommended to use other Cache Modules, such as the [Redis Cache Module](./redis/page.mdx), for production. You can also [Create a Cache Module](./create/page.mdx).
---
## How to Use the Cache Module?
You can use the registered Cache Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the Cache Module's service and use its methods to cache data, retrieve cached data, or clear the cache.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const cacheModuleService = container.resolve(
Modules.CACHE
)
await cacheModuleService.set("key", "value")
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
step1()
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Cache Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `set` method of the Cache Module to cache the value `"value"` with the key `"key"`.
---
## List of Cache Modules
By default, Medusa uses the In-Memory Cache Module. This module uses a plain JavaScript Map object to store the cache data.
This is useful for development. However, for production, it's highly recommended to use other Cache Modules, such as the Redis Cache Module.
Medusa provides the following Cache Modules. You can use one of them, or [Create a Cache Module](./create/page.mdx).
<CardList
items={[
@@ -38,9 +84,3 @@ This is useful for development. However, for production, it's highly recommended
}
]}
/>
---
## Create a Cache Module
To create a cache module, refer to [this guide](./create/page.mdx).
@@ -1,10 +1,10 @@
export const metadata = {
title: `Local Event Bus Module`,
title: `Local Event Module`,
}
# {metadata.title}
The Local Event Bus Module uses Node EventEmitter to implement Medusa's pub/sub events system. The Node EventEmitter is limited to a single process environment.
The Local Event Module uses Node EventEmitter to implement Medusa's pub/sub events system. The Node EventEmitter is limited to a single process environment.
This module is useful for development and testing, but its not recommended to be used in production.
@@ -12,11 +12,11 @@ For production, its recommended to use modules like [Redis Event Bus Module](
---
## Register the Local Event Bus Module
## Register the Local Event Module
<Note>
The Local Event Bus Module is registered by default in your application.
The Local Event Module is registered by default in your application.
</Note>
@@ -1,22 +1,80 @@
import { CardList } from "docs-ui"
export const metadata = {
title: `Event Modules`,
title: `Event Module`,
}
# {metadata.title}
In this document, you'll learn what an Event Module is and how to use it in your Medusa application.
## What is an Event Module?
An Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers.
This makes the event architecture customizable, as you can either choose one of Medusas event modules or create your own.
<Note>
Learn more about Medusa's event systems in the [Events and Subscribers documentation](!docs!/learn/fundamentals/events-and-subscribers).
</Note>
### Default Event Module
By default, Medusa uses the [Local Event Module](./local/page.mdx). This module uses Nodes EventEmitter to implement the publish/subscribe system. While this is suitable for development, it's recommended to use other Event Modules, such as the [Redis Event Module](./redis/page.mdx), for production. You can also [Create an Event Module](./create/page.mdx).
---
## How to Use the Event Module?
You can use the registered Event Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
Medusa provides the helper step [emitEventStep](/references/helper-steps/emitEventStep) that you can use in your workflow. You can also resolve the Event Module's service in a step of your workflow and use its methods to emit events.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const eventModuleService = container.resolve(
Modules.EVENT
)
await eventModuleService.emit({
name: "custom.event",
data: {
id: "123",
// other data payload
},
})
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
step1()
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Event Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `emit` method of the Event Module to emit an event with the name `"custom.event"` and the data payload `{ id: "123" }`.
---
## List of Event Modules
By default, Medusa uses the Local Event Module. This module uses Nodes EventEmitter to implement the publish/subscribe system.
This is useful for development. However, for production, its highly recommended to use other Event Modules, Redis Event Module.
Medusa provides the following Event Modules. You can use one of them, or [Create an Event Module](./create/page.mdx).
<CardList
items={[
@@ -38,9 +96,3 @@ This is useful for development. However, for production, its highly recommend
}
]}
/>
---
## Create a Event Module
To create an event module, refer to [this guide](./create/page.mdx).
@@ -1,12 +1,12 @@
import { Table, Prerequisites } from "docs-ui"
export const metadata = {
title: `Redis Event Bus Module`,
title: `Redis Event Module`,
}
# {metadata.title}
The Redis Event Bus Module uses Redis to implement Medusa's pub/sub events system.
The Redis Event Module uses Redis to implement Medusa's pub/sub events system.
It's powered by BullMQ and `io-redis`. BullMQ is responsible for the message queue and worker, and `io-redis` is the underlying Redis client that BullMQ connects to for events storage.
@@ -14,7 +14,7 @@ In production, it's recommended to use this module.
---
## Register the Redis Events Bus Module
## Register the Redis Event Module
<Prerequisites items={[
{
@@ -55,7 +55,7 @@ Make sure to add the following environment variables:
EVENTS_REDIS_URL=<YOUR_REDIS_URL>
```
### Redis Event Bus Module Options
### Redis Event Module Options
<Table>
<Table.Header>
@@ -1,18 +1,78 @@
import { CardList } from "docs-ui"
export const metadata = {
title: `File Module Providers`,
title: `File Module`,
}
# {metadata.title}
## What is a File Module Provider?
In this document, you'll learn about the File Module and its providers.
The File Module exposes the functionalities to upload assets, such as product images, to the Medusa application. A file module provider implements the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The File Module must have one file module provider configured.
## What is the File Module?
By default, Medusa uses the Local File Module. This module uploads files to the `uploads` directory of your Medusa application.
The File Module exposes the functionalities to upload assets, such as product images, to the Medusa application. Medusa uses the File Module in its core commerce features for all file operations, and you can use it in your custom features as well.
This is useful for development. However, for production, its highly recommended to use other File Modules, such as the S3 Module.
---
## How to Use the File Module?
You can use the File Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the File Module's service and use its methods to upload files, retrieve files, or delete files.
For example:
```ts
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)
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the service of the File Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `retrieveFile` method of the File Module to retrieve the URL of the file with the name `"image.png"`. The URL is then returned as a response from the step and the workflow.
---
### What is a File Module Provider?
A File Module Provider implements the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The File Module then uses the registered File Module Provider to handle file operations.
<Note>
Only one File Module Provider can be registered at a time. If you register multiple providers, the File Module will throw an error.
</Note>
By default, Medusa uses the [Local File Module](./local/page.mdx). This module uploads files to the `uploads` directory of your Medusa application.
This is useful for development. However, for production, its highly recommended to use other File Module Providers, such as the S3 File Module Provider. You can also [Create a File Provider](/references/file-provider-module).
<CardList
items={[
@@ -34,9 +94,3 @@ This is useful for development. However, for production, its highly recommend
}
]}
/>
---
## Create a File Module Provider
To create a file module provider, refer to [this guide](/references/file-provider-module).
@@ -0,0 +1,160 @@
import { Table, CardList } from "docs-ui"
export const metadata = {
title: `Locking Module`,
}
# {metadata.title}
In this document, you'll learn about the Locking Module and its providers.
## What is the Locking Module?
The Locking Module manages access to shared resources by multiple processes or threads. It prevents conflicts between processes that are trying to access the same resource at the same time, and ensures data consistency.
Medusa uses the Locking Module to control concurrency, avoid race conditions, and protect parts of code that should not be executed by more than one process at a time. This is especially essential in distributed or multi-threaded environments.
For example, Medusa uses the Locking Module in inventory management to ensure that only one transaction can update the stock levels at a time. By using the Locking Module in this scenario, Medusa prevents overselling an inventory item and keeps its quantity amounts accurate, even during high traffic periods or when receiving concurrent requests.
---
## How to Use the Locking Module?
You can use the Locking Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the Locking Module's service and use its methods to execute an asynchronous job, acquire a lock, or release locks.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const lockingModuleService = container.resolve(
Modules.LOCKING
)
const productModuleService = container.resolve(
Modules.PRODUCT
)
await lockingModuleService.execute("prod_123", async () => {
await productModuleService.deleteProduct("prod_123")
})
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
step1()
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the services of the Locking and Product modules from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `execute` method of the Locking Module to acquire a lock for the product with the ID `prod_123` and execute an asynchronous function, which deletes the product.
---
## When to Use the Locking Module?
You should use the Locking Module when you need to ensure that only one process can access a shared resource at a time. As mentioned in the inventory example previously, you don't want customers to order quantities of inventory that are not available, or to update the stock levels of an item concurrently.
In those scenarios, you can use the Locking Module to acquire a lock for a resource and execute a critical section of code that should not be accessed by multiple processes simultaneously.
---
## What is a Locking Module Provider?
A Locking Module Provider implements the underlying logic of the Locking Module. It manages the locking mechanisms and ensures that only one process can access a shared resource at a time.
Medusa provides [multiple Locking Module Providers](#list-of-locking-module-providers) that are suitable for development and production. You can also create a [custom Locking Module Provider](/references/locking-module-provider) to implement custom locking mechanisms or integrate with third-party services.
### Default Locking Module Provider
By default, Medusa uses the In-Memory Locking Module Provider. This provider uses a plain JavaScript map to store the locks. While this is useful for development, it is not recommended for production environments as it is only intended for use in a single-instance environment.
To add more providers, you can register them in the `medusa-config.ts` file. For example:
```ts
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/locking",
options: {
providers: [
// add providers here...
]
}
}
]
})
```
When you register other providers in `medusa-config.ts`, Medusa will set the default provider based on the following scenarios:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Scenario</Table.HeaderCell>
<Table.HeaderCell>Default Provider</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
One provider is registered.
</Table.Cell>
<Table.Cell>
The registered provider.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Multiple providers are registered and none of them has an `is_default` flag.
</Table.Cell>
<Table.Cell>
In-Memory Locking Module Provider.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Multiple providers and one of them has an `is_default` flag.
</Table.Cell>
<Table.Cell>
The provider with the `is_default` flag.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
---
## List of Locking Module Providers
Medusa provides the following Locking Module Providers. You can use one of them, or [Create a Locking Module Provider](./references/locking-module-provider).
<CardList
items={[
{
title: "Redis",
href: "/architectural-modules/locking/redis",
badge: {
variant: "green",
children: "Recommended"
}
},
{
title: "PostgreSQL",
href: "/architectural-modules/locking/postgres",
}
]}
/>
@@ -0,0 +1,105 @@
export const metadata = {
title: `PostgreSQL Locking Module Provider`,
}
# {metadata.title}
The PostgreSQL Locking Module Provider uses PostgreSQL's advisory locks to control and manage locks across multiple instances of Medusa. Advisory locks are lightweight locks that do not interfere with other database transactions. By using PostgreSQL's advisory locks, Medusa can create distributed locks directly through the database.
The provider uses the existing PostgreSQL database in your application to manage locks, so you don't need to set up a separate database or service to manage locks.
<Note>
While this provider is suitable for production environments, it's recommended to use the [Redis Locking Module Provider](../redis/page.mdx) if possible.
</Note>
---
## Register the PostgreSQL Locking Module Provider
To register the PostgreSQL Locking Module Provider, add it to the list of providers of the Locking Module in `medusa-config.ts`:
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/locking",
options: {
providers: [
{
resolve: "@medusajs/medusa/locking-postgres",
id: "postgres-lock",
// set this if you want this provider to be used by default
// and you have other Locking Module Providers registered.
is_default: true,
},
]
}
}
]
})
```
### Run Migrations
The PostgreSQL Locking Module Provider requires a new `locking` table in the database to store the locks. So, you must run the migrations after registering the provider:
```bash
npx medusa db:migrate
```
This will run the migration in the PostgreSQL Locking Module Provider and create the necessary table in the database.
---
## Use Provider with Locking Module
The PostgreSQL Locking Module Provider will be the default provider if you don't register any other providers, or if you set the `is_default` flag to `true`:
export const defaultHighlights = [
["11", "is_default"]
]
```ts title="medusa-config.ts" highlights={defaultHighlights}
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/locking",
options: {
providers: [
{
resolve: "@medusajs/medusa/locking-postgres",
id: "postgres-lock",
is_default: true,
},
]
}
}
]
})
```
If you use the Locking Module in your customizations, the PostgreSQL Locking Module Provider will be used by default in this case. You can also explicitly use this provider by passing its identifier `lp_locking-postgres` to the Locking Module's service methods.
For example, when using the `acquire` method in a [workflow step](!docs!/learn/fundamentals/workflows):
```ts
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",
})
}
)
```
@@ -0,0 +1,289 @@
import { Table, Prerequisites } from "docs-ui"
export const metadata = {
title: `Redis Locking Module Provider`,
}
# {metadata.title}
The Redis Locking Module Provider uses Redis to manage locks across multiple instances of Medusa. Redis ensures that locks are globally available, which is ideal for distributed environments.
This provider is recommended for production environments where Medusa is running in a multi-instance setup.
---
## Register the Redis Locking Module Provider
<Prerequisites
items={[
{
text: "A redis server set up locally or a database in your deployed application.",
link: "https://redis.io/download",
}
]}
/>
To register the Redis Locking Module Provider, add it to the list of providers of the Locking Module in `medusa-config.ts`:
```ts title="medusa-config.ts"
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/locking",
options: {
providers: [
{
resolve: "@medusajs/medusa/locking-redis",
id: "redis-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: {
redisUrl: process.env.LOCKING_REDIS_URL,
}
},
]
}
}
]
})
```
### Environment Variables
Make sure to add the following environment variable:
```bash
LOCKING_REDIS_URL=<YOUR_LOCKING_REDIS_URL>
```
Where `<YOUR_LOCKING_REDIS_URL>` is the URL of your Redis server, either locally or in the deployed environment.
<Note title="Tip">
The default Redis URL in a local environment is `redis://localhost:6379`.
</Note>
### Redis Locking Module Provider Options
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Option</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
<Table.HeaderCell>Required</Table.HeaderCell>
<Table.HeaderCell>Default</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`redisUrl`
</Table.Cell>
<Table.Cell>
A string indicating the Redis connection URL.
</Table.Cell>
<Table.Cell>
Yes
</Table.Cell>
<Table.Cell>
\-
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`redisOptions`
</Table.Cell>
<Table.Cell>
An object of Redis options. Refer to the [Redis API Reference](https://redis.github.io/ioredis/index.html#RedisOptions) for details on accepted properties.
</Table.Cell>
<Table.Cell>
No
</Table.Cell>
<Table.Cell>
\-
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`namespace`
</Table.Cell>
<Table.Cell>
A string used to prefix all locked keys with `{namespace}`.
</Table.Cell>
<Table.Cell>
No
</Table.Cell>
<Table.Cell>
`medusa_lock:`. So, all locked keys are prefixed with `medusa_lock:`.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`waitLockingTimeout`
</Table.Cell>
<Table.Cell>
A number indicating the default timeout (in seconds) to wait while acquiring a lock. This timeout is used when no timeout is specified when executing an asynchronous job or acquiring a lock.
</Table.Cell>
<Table.Cell>
No
</Table.Cell>
<Table.Cell>
`5`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`defaultRetryInterval`
</Table.Cell>
<Table.Cell>
A number indicating the time (in milliseconds) to wait before retrying to acquire a lock.
</Table.Cell>
<Table.Cell>
No
</Table.Cell>
<Table.Cell>
`5`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`maximumRetryInterval`
</Table.Cell>
<Table.Cell>
A number indicating the maximum time (in milliseconds) to wait before retrying to acquire a lock.
</Table.Cell>
<Table.Cell>
No
</Table.Cell>
<Table.Cell>
`200`
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
---
## Test out the Module
To test out the Redis Locking Module Provider, start the Medusa application:
```bash npm2yarn
npm run dev
```
You'll see the following message logged in the terminal:
```bash
info: Connection to Redis in "locking-redis" provider established
```
This message indicates that the Redis Locking Module Provider has successfully connected to the Redis server.
If you set the `is_default` flag to `true` in the provider options or you only registered the Redis Locking Module Provider, the Locking Module will use it by default for all locking operations.
---
## Use Provider with Locking Module
The Redis Locking Module Provider will be the default provider if you don't register any other providers, or if you set the `is_default` flag to `true`:
export const defaultHighlights = [
["11", "is_default"]
]
```ts title="medusa-config.ts" highlights={defaultHighlights}
module.exports = defineConfig({
// ...
modules: [
{
resolve: "@medusajs/medusa/locking",
options: {
providers: [
{
resolve: "@medusajs/medusa/locking-redis",
id: "redis-lock",
is_default: true,
options: {
// ...
}
},
]
}
}
]
})
```
If you use the Locking Module in your customizations, the Redis Locking Module Provider will be used by default in this case. You can also explicitly use this provider by passing its identifier `lp_locking-redis` to the Locking Module's service methods.
For example, when using the `acquire` method in a [workflow step](!docs!/learn/fundamentals/workflows):
```ts
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",
})
}
)
```
@@ -1,22 +1,73 @@
import { CardList } from "docs-ui"
export const metadata = {
title: `Notification Module Providers`,
title: `Notification Module`,
}
# {metadata.title}
In this document, you'll learn about the Notification Module and its providers.
## What is the Notification Module?
The Notification Module exposes the functionalities to send a notification to a customer or user. For example, sending an order confirmation email. Medusa uses the Notification Module in its core commerce features for notification operations, and you an use it in your custom features as well.
---
## How to Use the Notification Module?
You can use the Notification Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the Notification Module's service and use its methods to send notifications.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const notificationModuleService = container.resolve(
Modules.NOTIFICATION
)
await notificationModuleService.createNotifications({
to: "customer@gmail.com",
channel: "email",
template: "product-created",
data,
})
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
step1()
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Notification Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `createNotifications` method of the Notification Module to send an email notification.
Find a full example of sending a notification in the [Send Notification guide](./send-notification/page.mdx).
---
## What is a Notification Module Provider?
The Notification Module exposes the functionalities to send a notification to a customer or user. For example, sending an order confirmation email.
A Notification Module Provider implements the underlying logic of sending notification. It either integrates a third-party service or uses custom logic to send the notification.
You can resolve the Notification Module and send notifications in API routes, subscribers, or other resources.
By default, Medusa uses the [Local Notification Module](./local/page.mdx) which only simulates sending the notification by logging a message in the terminal.
A notification module provider implements the underlying logic of sending notification. It either integrates a third-party service or uses custom logic to send the notification.
By default, Medusa uses the Local Notification Module which only simulates sending the notification by logging a message in the terminal.
Medusa provides other Notification Modules that actually send notifications, such as the SendGrid Notification Module Provider.
Medusa provides other Notification Modules that actually send notifications, such as the [SendGrid Notification Module Provider](./send-notification/page.mdx). You can also [Create a Notification Module Provider](/references/notification-provider-module).
<CardList
items={[
@@ -43,7 +94,9 @@ Medusa provides other Notification Modules that actually send notifications, suc
## Notification Module Provider Channels
When you send a notification, you specify the channel to send it through, such as `email` or `sms`. Each provider defined in the Notification Module's `providers` option has a `channels` option specifying which channels it can be used in. Only one provider can be setup for each channel.
When you send a notification, you specify the channel to send it through, such as `email` or `sms`.
You register providers of the Notification Module in `medusa-config.ts`. For each provider, you pass a `channels` option specifying which channels the provider can be used in. Only one provider can be setup for each channel.
For example:
@@ -76,9 +129,3 @@ module.exports = {
```
The `channels` option is an array of strings indicating the channels this provider is used for.
---
## Create a Notification Module Provider
To create a notification module provider, refer to [this guide](/references/notification-provider-module).
@@ -6,17 +6,17 @@ export const metadata = {
# {metadata.title}
Medusa's architectural functionalities, such as emitting and subscribing to events or caching data, are all implemented in architectural modules. An architectural module is a package that can be installed and used in any Medusa application. These modules allow you to choose and integrate custom services for architectural purposes.
Medusa's architectural functionalities, such as emitting and subscribing to events or caching data, are all implemented in Architectural Modules. An Architectural Module is a package that can be installed and used in any Medusa application. These modules allow you to choose and integrate custom services for architectural purposes.
For example, you can use our [Redis Event Module](./event/redis/page.mdx) to handle event functionalities, or create a custom module that implements these functionalities with Memcached. Learn more about Medusa's architecture in [this documentation](!docs!/learn/introduction/architecture).
For example, you can use our [Redis Event Module](./event/redis/page.mdx) to handle event functionalities, or create a custom module that implements these functionalities with Memcached. Learn more in [the Architecture documentation](!docs!/learn/introduction/architecture).
This section of the documentation showcases official architectural modules and how to configure them in your application.
This section of the documentation showcases Medusa's Architectural Modules, how they work, and how to use them in your Medusa application.
## Cache Modules
## Cache Module
A Cache Module is used to cache the results of computations such as price selection or various tax calculations. Learn more in [this documentation](./cache/page.mdx).
The following modules are provided by Medusa. You can also create your own cache module as explained in [this guide](./cache/create/page.mdx).
The following Cache modules are provided by Medusa. You can also create your own cache module as explained in [this guide](./cache/create/page.mdx).
<CardList
items={[
@@ -41,11 +41,11 @@ The following modules are provided by Medusa. You can also create your own cache
---
## Event Modules
## Event Module
An Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers. Learn more in [this documentation](./event/page.mdx).
The following modules are provided by Medusa. You can also create your own event module as explained in [this guide](./event/create/page.mdx).
The following Event modules are provided by Medusa. You can also create your own event module as explained in [this guide](./event/create/page.mdx).
<CardList
items={[
@@ -70,11 +70,11 @@ The following modules are provided by Medusa. You can also create your own event
---
## File Module Providers
## File Module
A file module provider implements the logic of handling uploads and downloads of assets, such as integrating third-party services. Learn more in [this documentation](./file/page.mdx).
The File Module handles file upload and storage of assets, such as product images. Refer to the [File Module documentation](./file/page.mdx) to learn more about it.
The following modules are provided by Medusa. You can also create your own file module as explained in [this guide](/references/file-provider-module).
The File Module has module providers that implement the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The following File Module Providers are provided by Medusa. You can also create a custom provider as explained in the [Create File Module Provider guide](/references/file-provider-module).
<CardList
items={[
@@ -99,11 +99,36 @@ The following modules are provided by Medusa. You can also create your own file
---
## Notification Module Providers
## Locking Module
A notification module provider implements the logic of sending notifications, typically through integrating a third-party service. Learn more in [this documentation](./notification/page.mdx).
The Locking Module manages access to shared resources by multiple processes or threads. It prevents conflicts between processes and ensures data consistency. Refer to the [Locking Module documentation](./locking/page.mdx) to learn more about it.
The following modules are provided by Medusa. You can also create your own file module as explained in [this guide](/references/notification-provider-module).
The Locking Module uses module providers that implement the underlying logic of the locking mechanism. The following Locking Module Providers are provided by Medusa. You can also create a custom provider as explained in the [Create Locking Module Provider guide](/references/locking-provider-module).
<CardList
items={[
{
title: "Redis",
href: "/architectural-modules/locking/redis",
badge: {
variant: "green",
children: "Recommended"
}
},
{
title: "PostgreSQL",
href: "/architectural-modules/locking/postgres",
}
]}
/>
---
## Notification Module
The Notification Module handles sending notifications to users or customers, such as reset password instructions or newsletters. Refer to the [Notifcation Module documentation](./notification/page.mdx) to learn more about it.
The Notification Module has module providers that implement the underlying logic of sending notifications, typically through integrating a third-party service. The following modules are provided by Medusa. You can also create a custom provider as explained in the [Create Notification Module Provider guide](/references/notification-provider-module).
<CardList
items={[
@@ -151,11 +176,11 @@ The following modules are provided by Medusa. You can also create your own file
---
## Workflow Engine Modules
## Workflow Engine Module
Workflow engine modules handle tracking and recording the transactions and statuses of workflows and their steps. Learn more in [this documentation](./workflow-engine/page.mdx).
A Workflow Engine Module handles tracking and recording the transactions and statuses of workflows and their steps. Learn more about it in the [Worklow Engine Module documentation](./workflow-engine/page.mdx).
The following modules are provided by Medusa.
The following Workflow Engine modules are provided by Medusa.
<CardList
items={[
@@ -1,14 +1,74 @@
import { CardList } from "docs-ui"
export const metadata = {
title: `Workflow Engine Modules`,
title: `Workflow Engine Module`,
}
# {metadata.title}
Workflow engine modules handle tracking and recording the transactions and statuses of workflows and their steps.
In this document, you'll learn what a Workflow Engine Module is and how to use it in your Medusa application.
Medusa uses the In-Memory Workflow Engine Module by default. For production purposes, it's recommended to use the Redis Engine Module instead.
## What is a Workflow Engine Module?
A Workflow Engine Module handles tracking and recording the transactions and statuses of workflows and their steps. It can use custom mechanism or integrate a third-party service.
### Default Workflow Engine Module
Medusa uses the [In-Memory Workflow Engine Module](./in-memory/page.mdx) by default. For production purposes, it's recommended to use the [Redis Workflow Engine Module](./redis/page.mdx) instead.
---
## How to Use the Workflow Engine Module?
You can use the registered Workflow Engine Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the Workflow Engine Module's service and use its methods to track and record the transactions and statuses of workflows and their steps.
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
StepResponse,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const workflowEngineService = container.resolve(
Modules.WORKFLOW_ENGINE
)
const [workflowExecution] = await workflowEngineService.listWorkflowExecutions({
transaction_id: transaction_id,
})
return new StepResponse(workflowExecution)
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
const workflowExecution = step1()
return new WorkflowResponse(workflowExecution)
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Workflow Engine Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
Then, you use the `listWorkflowExecutions` method of the Workflow Engine Module to list the workflow executions with the transaction ID `transaction_id`. The workflow execution is then returned as a response from the step and the workflow.
---
## List of Workflow Engine Modules
Medusa provides the following Workflow Engine Modules.
<CardList
items={[
@@ -1,4 +1,4 @@
import { ChildDocs } from "docs-ui"
import { CardList } from "docs-ui"
export const metadata = {
title: `Commerce Modules`,
@@ -14,7 +14,82 @@ A commerce module also defines data models, representing tables in the database.
## Commerce Modules List
<ChildDocs onlyTopLevel={true} defaultItemsPerRow={2} hideItems={["Overview"]} />
<CardList
items={[
{
title: "API Key Module",
href: "/commerce-modules/api-key"
},
{
title: "Auth Module",
href: "/commerce-modules/auth"
},
{
title: "Cart Module",
href: "/commerce-modules/cart"
},
{
title: "Currency Module",
href: "/commerce-modules/currency"
},
{
title: "Customer Module",
href: "/commerce-modules/customer"
},
{
title: "Fulfillment Module",
href: "/commerce-modules/fulfillment"
},
{
title: "Inventory Module",
href: "/commerce-modules/inventory"
},
{
title: "Order Module",
href: "/commerce-modules/order"
},
{
title: "Payment Module",
href: "/commerce-modules/payment"
},
{
title: "Pricing Module",
href: "/commerce-modules/pricing"
},
{
title: "Product Module",
href: "/commerce-modules/product"
},
{
title: "Promotion Module",
href: "/commerce-modules/promotion"
},
{
title: "Region Module",
href: "/commerce-modules/region"
},
{
title: "Sales Channel Module",
href: "/commerce-modules/sales-channel"
},
{
title: "Stock Location Module",
href: "/commerce-modules/stock-location"
},
{
title: "Store Module",
href: "/commerce-modules/store"
},
{
title: "Tax Module",
href: "/commerce-modules/tax"
},
{
title: "User Module",
href: "/commerce-modules/user"
}
]}
/>
---
+21 -15
View File
@@ -194,29 +194,29 @@ export const generatedEditDates = {
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2025-01-07T09:02:27.235Z",
"app/commerce-modules/api-key/page.mdx": "2025-02-26T11:18:00.337Z",
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-25T13:26:27.176Z",
"app/architectural-modules/page.mdx": "2025-01-08T12:13:50.333Z",
"app/architectural-modules/page.mdx": "2025-03-12T10:28:44.544Z",
"app/architectural-modules/workflow-engine/redis/page.mdx": "2024-10-15T12:50:59.507Z",
"app/architectural-modules/notification/sendgrid/page.mdx": "2025-02-26T11:43:13.143Z",
"app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z",
"app/architectural-modules/workflow-engine/page.mdx": "2025-01-08T12:16:04.157Z",
"app/architectural-modules/workflow-engine/page.mdx": "2025-03-12T11:55:18.789Z",
"app/_events-reference/page.mdx": "2024-07-03T19:27:13+03:00",
"app/architectural-modules/cache/page.mdx": "2025-01-08T12:14:37.925Z",
"app/architectural-modules/cache/page.mdx": "2025-03-12T11:52:56.014Z",
"app/architectural-modules/file/s3/page.mdx": "2025-01-24T13:49:01.104Z",
"app/commerce-modules/api-key/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/api-key/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/architectural-modules/cache/redis/page.mdx": "2024-10-15T12:50:01.730Z",
"app/architectural-modules/event/redis/page.mdx": "2024-10-15T12:50:40.506Z",
"app/architectural-modules/event/local/page.mdx": "2024-10-15T12:50:37.512Z",
"app/architectural-modules/event/redis/page.mdx": "2025-03-12T11:56:15.922Z",
"app/architectural-modules/event/local/page.mdx": "2025-03-12T11:56:02.160Z",
"app/architectural-modules/workflow-engine/in-memory/page.mdx": "2024-10-15T12:50:57.249Z",
"app/architectural-modules/cache/in-memory/page.mdx": "2024-10-15T12:49:57.608Z",
"app/architectural-modules/notification/local/page.mdx": "2024-10-15T12:51:21.284Z",
"app/architectural-modules/file/local/page.mdx": "2024-10-16T15:48:42.839Z",
"app/architectural-modules/notification/send-notification/page.mdx": "2024-09-30T08:43:53.151Z",
"app/architectural-modules/file/page.mdx": "2025-01-08T12:15:19.981Z",
"app/architectural-modules/event/page.mdx": "2025-01-08T12:15:08.454Z",
"app/architectural-modules/file/page.mdx": "2025-03-12T10:46:14.005Z",
"app/architectural-modules/event/page.mdx": "2025-03-12T11:52:51.218Z",
"app/architectural-modules/cache/create/page.mdx": "2024-10-16T08:51:35.074Z",
"app/admin-widget-injection-zones/page.mdx": "2024-12-24T08:48:36.154Z",
"app/architectural-modules/notification/page.mdx": "2025-01-08T12:15:40.159Z",
"app/architectural-modules/notification/page.mdx": "2025-03-12T11:49:47.552Z",
"app/architectural-modules/event/create/page.mdx": "2024-12-09T14:46:40.248Z",
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:58.913Z",
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2024-08-20T00:10:58.949Z",
@@ -553,7 +553,7 @@ export const generatedEditDates = {
"references/types/NotificationTypes/interfaces/types.NotificationTypes.Attachment/page.mdx": "2024-12-17T16:57:21.128Z",
"references/modules/fulfillment_models/page.mdx": "2024-12-23T12:30:31.974Z",
"references/modules/order_models/page.mdx": "2025-01-27T11:43:58.772Z",
"references/notification/classes/notification.AbstractNotificationProviderService/page.mdx": "2025-02-11T11:36:55.004Z",
"references/notification/classes/notification.AbstractNotificationProviderService/page.mdx": "2025-03-12T12:28:32.206Z",
"references/order/interfaces/order.IOrderModuleService/page.mdx": "2025-03-04T13:33:53.711Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCampaignResponse/page.mdx": "2024-12-09T13:21:33.353Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminProductCategory/page.mdx": "2025-02-11T11:36:49.726Z",
@@ -845,7 +845,7 @@ export const generatedEditDates = {
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.authenticate/page.mdx": "2025-01-07T12:54:18.941Z",
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.validateCallback/page.mdx": "2025-01-07T12:54:18.948Z",
"references/auth/interfaces/auth.AuthenticationResponse/page.mdx": "2024-12-09T13:21:36.233Z",
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-02-07T10:35:24.400Z",
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-03-12T12:28:32.072Z",
"references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2025-03-04T13:33:41.373Z",
"references/types/CommonTypes/types/types.CommonTypes.BatchMethodResponse/page.mdx": "2024-12-09T13:21:32.849Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminAddReturnShipping/page.mdx": "2024-12-09T13:21:34.545Z",
@@ -1161,7 +1161,7 @@ export const generatedEditDates = {
"references/dml/entity/classes/dml.entity.DmlEntity/page.mdx": "2025-01-13T18:05:57.749Z",
"references/dml/entity_builder/EntityBuilder/methods/dml.entity_builder.EntityBuilder.define/page.mdx": "2025-01-13T18:05:57.746Z",
"references/dml/entity_builder/types/dml.entity_builder.ManyToManyOptions/page.mdx": "2025-01-13T18:05:57.744Z",
"references/file/classes/file.AbstractFileProviderService/page.mdx": "2025-02-24T10:48:42.996Z",
"references/file/classes/file.AbstractFileProviderService/page.mdx": "2025-03-12T12:28:32.076Z",
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createFulfillmentSets/page.mdx": "2024-12-09T13:21:55.940Z",
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createGeoZones/page.mdx": "2025-01-13T18:05:53.686Z",
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createServiceZones/page.mdx": "2024-12-09T13:21:55.976Z",
@@ -1223,7 +1223,7 @@ export const generatedEditDates = {
"references/fulfillment/interfaces/fulfillment.OrderLineItemDTO/page.mdx": "2024-12-23T13:57:04.802Z",
"references/fulfillment/interfaces/fulfillment.OrderLineItemTaxLineDTO/page.mdx": "2024-12-23T13:57:04.793Z",
"references/fulfillment/types/fulfillment.JoinerRelationship/page.mdx": "2024-12-09T13:21:56.268Z",
"references/fulfillment_provider/classes/fulfillment_provider.AbstractFulfillmentProviderService/page.mdx": "2025-02-24T10:48:43.021Z",
"references/fulfillment_provider/classes/fulfillment_provider.AbstractFulfillmentProviderService/page.mdx": "2025-03-12T12:28:32.087Z",
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.adjustInventory/page.mdx": "2024-12-09T13:21:56.800Z",
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.confirmInventory/page.mdx": "2024-12-09T13:21:56.804Z",
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.createInventoryItems/page.mdx": "2024-12-09T13:21:56.744Z",
@@ -1363,7 +1363,7 @@ export const generatedEditDates = {
"references/payment/interfaces/payment.JoinerServiceConfig/page.mdx": "2025-01-13T18:05:56.394Z",
"references/payment/interfaces/payment.JoinerServiceConfigAlias/page.mdx": "2024-12-09T13:22:02.308Z",
"references/payment/types/payment.JoinerRelationship/page.mdx": "2024-12-09T13:22:02.304Z",
"references/payment_provider/classes/payment_provider.AbstractPaymentProvider/page.mdx": "2025-03-04T13:33:56.703Z",
"references/payment_provider/classes/payment_provider.AbstractPaymentProvider/page.mdx": "2025-03-12T12:28:32.214Z",
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.addPriceListPrices/page.mdx": "2024-12-09T13:22:02.928Z",
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.addPrices/page.mdx": "2024-12-09T13:22:02.868Z",
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.calculatePrices/page.mdx": "2024-12-09T13:22:02.832Z",
@@ -1590,7 +1590,7 @@ export const generatedEditDates = {
"references/tax/interfaces/tax.TaxRateRuleDTO/page.mdx": "2024-12-10T14:55:13.509Z",
"references/tax/interfaces/tax.UpdateTaxRateDTO/page.mdx": "2024-12-10T14:55:13.523Z",
"references/tax/types/tax.JoinerRelationship/page.mdx": "2024-12-09T13:22:04.496Z",
"references/tax_provider/interfaces/tax_provider.ITaxProvider/page.mdx": "2024-12-09T13:22:04.468Z",
"references/tax_provider/interfaces/tax_provider.ITaxProvider/page.mdx": "2025-03-12T12:28:31.902Z",
"references/tax_provider/types/tax_provider.ItemTaxCalculationLine/page.mdx": "2024-12-09T13:22:04.464Z",
"references/tax_provider/types/tax_provider.ShippingTaxCalculationLine/page.mdx": "2024-12-09T13:22:04.460Z",
"references/types/DAL/interfaces/types.DAL.RepositoryService/page.mdx": "2025-03-04T13:33:50.003Z",
@@ -6043,5 +6043,11 @@ export const generatedEditDates = {
"references/types/interfaces/types.ITaxProvider/page.mdx": "2025-03-04T13:33:52.645Z",
"app/how-to-tutorials/page.mdx": "2025-03-10T09:33:49.208Z",
"app/tools/page.mdx": "2025-03-10T12:38:56.520Z",
"app/references-overview/page.mdx": "2025-03-10T12:55:49.803Z"
"app/references-overview/page.mdx": "2025-03-10T12:55:49.803Z",
"app/architectural-modules/locking/page.mdx": "2025-03-12T11:40:25.695Z",
"app/architectural-modules/locking/postgres/page.mdx": "2025-03-12T10:07:48.820Z",
"app/architectural-modules/locking/redis/page.mdx": "2025-03-12T10:07:43.867Z",
"references/locking/interfaces/locking.ILockingModule/page.mdx": "2025-03-12T12:28:30.425Z",
"references/locking/interfaces/locking.ILockingProvider/page.mdx": "2025-03-12T12:28:30.422Z",
"references/modules/locking/page.mdx": "2025-03-12T12:28:30.419Z"
}
@@ -91,6 +91,18 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/architectural-modules/file/s3/page.mdx",
"pathname": "/architectural-modules/file/s3"
},
{
"filePath": "/www/apps/resources/app/architectural-modules/locking/page.mdx",
"pathname": "/architectural-modules/locking"
},
{
"filePath": "/www/apps/resources/app/architectural-modules/locking/postgres/page.mdx",
"pathname": "/architectural-modules/locking/postgres"
},
{
"filePath": "/www/apps/resources/app/architectural-modules/locking/redis/page.mdx",
"pathname": "/architectural-modules/locking/redis"
},
{
"filePath": "/www/apps/resources/app/architectural-modules/notification/local/page.mdx",
"pathname": "/architectural-modules/notification/local"
@@ -12799,6 +12811,14 @@ export const filesMap = [
"filePath": "/www/apps/resources/references/js_sdk/store/classes/js_sdk.store.Store/page.mdx",
"pathname": "/references/js_sdk/store/classes/js_sdk.store.Store"
},
{
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingModule/page.mdx",
"pathname": "/references/locking/interfaces/locking.ILockingModule"
},
{
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingProvider/page.mdx",
"pathname": "/references/locking/interfaces/locking.ILockingProvider"
},
{
"filePath": "/www/apps/resources/references/medusa/classes/medusa.RestrictedFields/page.mdx",
"pathname": "/references/medusa/classes/medusa.RestrictedFields"
@@ -13011,6 +13031,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/references/modules/js_sdk/page.mdx",
"pathname": "/references/modules/js_sdk"
},
{
"filePath": "/www/apps/resources/references/modules/locking/page.mdx",
"pathname": "/references/modules/locking"
},
{
"filePath": "/www/apps/resources/references/modules/medusa/page.mdx",
"pathname": "/references/modules/medusa"
@@ -17,7 +17,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Cache Modules",
"title": "Cache Module",
"initialOpen": true,
"children": [
{
@@ -31,18 +31,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/cache/in-memory",
"title": "In-Memory",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/cache/redis",
"title": "Redis",
"children": []
"type": "sub-category",
"title": "Modules",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/cache/in-memory",
"title": "In-Memory",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/cache/redis",
"title": "Redis",
"children": []
}
]
},
{
"loaded": true,
@@ -66,7 +74,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Event Modules",
"title": "Event Module",
"initialOpen": true,
"children": [
{
@@ -80,18 +88,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/event/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/event/redis",
"title": "Redis",
"children": []
"type": "sub-category",
"title": "Modules",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/event/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/event/redis",
"title": "Redis",
"children": []
}
]
},
{
"loaded": true,
@@ -115,7 +131,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "File Module Providers",
"title": "File Module",
"initialOpen": true,
"children": [
{
@@ -129,18 +145,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/file/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/file/s3",
"title": "AWS S3 (and Compatible APIs)",
"children": []
"type": "sub-category",
"title": "Providers",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/file/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/file/s3",
"title": "AWS S3 (and Compatible APIs)",
"children": []
}
]
},
{
"loaded": true,
@@ -164,7 +188,72 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Notification Module Providers",
"title": "Locking Module",
"initialOpen": true,
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/locking",
"title": "Overview",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "sub-category",
"title": "Providers",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/locking/redis",
"title": "Redis",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/locking/postgres",
"title": "PostgreSQL",
"children": []
}
]
},
{
"loaded": true,
"isPathHref": true,
"type": "sub-category",
"title": "Guides",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/references/locking-module-provider",
"title": "Create Locking Provider",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/references/locking-service",
"title": "Use Locking Module",
"children": []
}
]
}
]
},
{
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Notification Module",
"initialOpen": true,
"children": [
{
@@ -178,18 +267,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/notification/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/notification/sendgrid",
"title": "SendGrid",
"children": []
"type": "sub-category",
"title": "Providers",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/notification/local",
"title": "Local",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/notification/sendgrid",
"title": "SendGrid",
"children": []
}
]
},
{
"loaded": true,
@@ -208,7 +305,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"type": "ref",
"path": "/integrations/guides/resend",
"title": "Integrate Resend",
"children": []
@@ -229,7 +326,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Workflow Engine Modules",
"title": "Workflow Engine Module",
"initialOpen": true,
"children": [
{
@@ -243,18 +340,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/workflow-engine/in-memory",
"title": "In-Memory",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/workflow-engine/redis",
"title": "Redis",
"children": []
"type": "sub-category",
"title": "Modules",
"children": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/workflow-engine/in-memory",
"title": "In-Memory",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/architectural-modules/workflow-engine/redis",
"title": "Redis",
"children": []
}
]
}
]
}
@@ -78,6 +78,14 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "Create Locking Provider",
"path": "https://docs.medusajs.com/resources/references/locking-module-provider",
"children": []
},
{
"loaded": true,
"isPathHref": true,
@@ -133,6 +141,14 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"title": "Send Notification",
"path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "Use Locking Module",
"path": "https://docs.medusajs.com/resources/references/locking-service",
"children": []
}
]
},
@@ -4704,6 +4704,16 @@ export const slugChanges = [
"newSlug": "/references/js-sdk/store",
"filePath": "/www/apps/resources/references/js_sdk/store/classes/js_sdk.store.Store/page.mdx"
},
{
"origSlug": "/references/locking/interfaces/locking.ILockingModule",
"newSlug": "/references/locking-service",
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingModule/page.mdx"
},
{
"origSlug": "/references/locking/interfaces/locking.ILockingProvider",
"newSlug": "/references/locking-module-provider",
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingProvider/page.mdx"
},
{
"origSlug": "/references/medusa_config/interfaces/medusa_config.ConfigModule",
"newSlug": "/references/medusa-config",
+1
View File
@@ -25,6 +25,7 @@ import { TypeList } from "docs-ui"
- [inventory-next](modules/inventory_next/page.mdx)
- [inventory-next-models](modules/inventory_next_models/page.mdx)
- [js-sdk](modules/js_sdk/page.mdx)
- [locking](modules/locking/page.mdx)
- [medusa](modules/medusa/page.mdx)
- [medusa-config](modules/medusa_config/page.mdx)
- [modules-sdk](modules/modules_sdk/page.mdx)
@@ -9,9 +9,17 @@ sidebar_label: Create Auth Provider
import { TypeList } from "docs-ui"
# How to Create an Auth Provider Module
# How to Create an Auth Module Provider
In this document, youll learn how to create an auth provider module and the methods you must implement in its main service.
In this document, youll learn how to create an Auth Module Provider and the methods you must implement in its main service.
---
## Implementation Example
As you implement your Auth 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 [Google Auth Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/auth-google).
---
@@ -31,9 +39,9 @@ The rest of this guide always uses the `src/modules/my-auth` directory as an exa
---
## 2. Create the Auth Provider Service
## 2. Create the Auth Module Provider's Service
Create the file `src/modules/my-auth/service.ts` that holds the module's main service. It must extend the `AbstractAuthModuleProvider` class imported from `@medusajs/framework/utils`:
Create the file `src/modules/my-auth/service.ts` that holds the module provider's main service. It must extend the `AbstractAuthModuleProvider` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-auth/service.ts"
import { AbstractAuthModuleProvider } from "@medusajs/framework/utils"
@@ -453,7 +461,7 @@ class MyAuthProviderService extends AbstractAuthModuleProvider {
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-auth/index.ts` with the following content:
@@ -469,11 +477,11 @@ export default ModuleProvider(Modules.AUTH, {
})
```
This exports the module's definition, indicating that the `MyAuthProviderService` is the module's service.
This exports the module provider's definition, indicating that the `MyAuthProviderService` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your Auth Module Provider, add it to the `providers` array of the Auth Module in `medusa-config.ts`:
@@ -513,9 +521,9 @@ module.exports = defineConfig({
## 5. Test it Out
To test out your authentication provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
To test out your Authentication Module Provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
For example, to get a registration token for an admin user, send a `POST` request to `/auth/user/my-auth/register` replacing `my-auth` with your authentication provider's ID:
For example, to get a registration token for an admin user, send a `POST` request to `/auth/user/my-auth/register` replacing `my-auth` with your Authentication Module Provider's ID:
```bash
curl -X POST http://localhost:9000/auth/user/my-auth/register
@@ -525,6 +533,6 @@ curl -X POST http://localhost:9000/auth/user/my-auth/register
}'
```
Change the request body to pass the data required for your authentication provider to register the user.
Change the request body to pass the data required for your Authentication Module Provider to register the user.
If registration is successful, the response will have a `token` property.
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createCartsStep({
"currency_code": "jpy"
"currency_code": "php"
})
}
)
@@ -19,7 +19,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = retrieveCartStep({
"id": "id_NC6OB61K97Y"
"id": "id_8UxqXFwvGKp1K"
})
}
)
@@ -24,7 +24,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateCartPromotionsStep({
"id": "id_DffiOIkGMKk1ORZHgv"
"id": "id_QjunYRvNPLqC"
})
}
)
@@ -34,7 +34,7 @@ export async function POST(
.run({
input: {
"id": [
"id_dCWncxGdqNJe1Xa8"
"id_bemxq4oJiSTZn"
]
}
})
@@ -61,7 +61,7 @@ export default async function handleOrderPlaced({
.run({
input: {
"id": [
"id_dCWncxGdqNJe1Xa8"
"id_bemxq4oJiSTZn"
]
}
})
@@ -88,7 +88,7 @@ export default async function myCustomJob(
.run({
input: {
"id": [
"id_dCWncxGdqNJe1Xa8"
"id_bemxq4oJiSTZn"
]
}
})
@@ -116,7 +116,7 @@ const myWorkflow = createWorkflow(
.runAsStep({
input: {
"id": [
"id_dCWncxGdqNJe1Xa8"
"id_bemxq4oJiSTZn"
]
}
})
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createCustomerGroupsStep({
"name": "Oswald"
"name": "Reuben"
})
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createFulfillmentSets({
"name": "Otha",
"name": "Princess",
"type": "{value}"
})
}
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createServiceZonesStep({
"name": "Braulio",
"name": "Chesley",
"fulfillment_set_id": "{value}"
})
}
@@ -24,7 +24,7 @@ const myWorkflow = createWorkflow(
const data = createShippingOptionRulesStep({
"data": [{
"attribute": "{value}",
"operator": "eq",
"operator": "lte",
"value": "{value}",
"shipping_option_id": "{value}"
}]
@@ -22,10 +22,10 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createShippingOptionsPriceSetsStep({
"id": "id_W67Buk6sFY",
"id": "id_HIsRdDyns3y1mST4",
"prices": [{
"currency_code": "bsd",
"amount": 50
"currency_code": "mur",
"amount": 49
}]
})
}
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createShippingProfilesStep({
"name": "Terry",
"name": "Roslyn",
"type": "{value}"
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteShippingOptionRulesStep({
"ids": [
"id_nM64LyjlsAZ"
"id_w0STpU4jGRJcMAB"
]
})
}
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateInventoryItemsStep({
"id": "id_mQEVzVbUaYJLgjQJY"
"id": "id_vM0T4AzOH45pTg"
})
}
)
@@ -20,8 +20,8 @@ const myWorkflow = createWorkflow(
() => {
const data = validateInventoryDeleteStep({
"inventory_items": [{
"id": "id_rGGlYBgTmX",
"reserved_quantity": 32
"id": "id_rwtTvcCy9pEA1AzENO8",
"reserved_quantity": 13
}]
})
}
@@ -23,8 +23,8 @@ const myWorkflow = createWorkflow(
() => {
const data = addOrderTransactionStep({
"order_id": "order_123",
"amount": 48,
"currency_code": "idr"
"amount": 11,
"currency_code": "szl"
})
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = cancelOrderChangeStep({
"id": "id_DaKY1rN96N4Dye1i4UZ"
"id": "id_ZFBssHt8s8B1wyV"
})
}
)
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = createOrderClaimsStep([{
"order_id": "order_123",
"type": "refund"
"type": "replace"
}])
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = declineOrderChangeStep({
"id": "id_Ou7TQ3XETnVPfiyeBxn"
"id": "id_2wLDxjDAU1BmB7Z"
})
}
)
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteClaimsStep({
"ids": [
"id_5kuTB79Xd8e14Y9BW6MH"
"id_8ubWMJmV8tFQACwSBRwB"
]
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteExchangesStep({
"ids": [
"id_ocY7t3llf5aL1"
"id_IS3vD87dhABzz"
]
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteOrderChangeActionsStep({
"ids": [
"id_DoNSMAC7x7yoiK67f3m"
"id_VTfP6NhpWdAnGb9b"
]
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteOrderChangesStep({
"ids": [
"id_s5kEPzEoBHwZamIZf"
"id_aL7s50hQv4X"
]
})
}
@@ -20,7 +20,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteOrderLineItems({
"ids": [
"id_qCoJvWGyVfQq5TC4LX"
"id_WQTEJb19KEWjtGsjr8MQ"
]
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteOrderShippingMethods({
"ids": [
"id_R9Ui7kOsEp"
"id_kB1A5qCI99"
]
})
}
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deleteReturnsStep({
"ids": [
"id_BTC6cHX3wCg6Ff"
"id_G4woLBjcA55As"
]
})
}
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateOrderChangeActionsStep({
"id": "id_YOUTShFPTOVb9hr"
"id": "id_vYJUMkfGqAUG"
})
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateOrderChangesStep({
"id": "id_2FI3REzv0cccw9"
"id": "id_39fPc4du4SYpubPg2"
})
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateOrderShippingMethodsStep({
"id": "id_xwOv7C5wTuE"
"id": "id_YxuvxvhYAEfz"
})
}
)
@@ -21,7 +21,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateReturnsStep({
"id": "id_V3qdW375AIzZCxbK8FRK"
"id": "id_y6VNwEVxTmUDauPsD2L"
})
}
)
@@ -39,7 +39,7 @@ export async function POST(
const { result } = await cancelOrderChangeWorkflow(req.scope)
.run({
input: {
"id": "id_TifTyReowGmg5yEQQLT"
"id": "id_7cSs3D0glvQGX5o89r"
}
})
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
const { result } = await cancelOrderChangeWorkflow(container)
.run({
input: {
"id": "id_TifTyReowGmg5yEQQLT"
"id": "id_7cSs3D0glvQGX5o89r"
}
})
@@ -89,7 +89,7 @@ export default async function myCustomJob(
const { result } = await cancelOrderChangeWorkflow(container)
.run({
input: {
"id": "id_TifTyReowGmg5yEQQLT"
"id": "id_7cSs3D0glvQGX5o89r"
}
})
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
const result = cancelOrderChangeWorkflow
.runAsStep({
input: {
"id": "id_TifTyReowGmg5yEQQLT"
"id": "id_7cSs3D0glvQGX5o89r"
}
})
}
@@ -39,7 +39,7 @@ export async function POST(
const { result } = await createOrderChangeActionsWorkflow(req.scope)
.run({
input: [{
"action": "UPDATE_ORDER_PROPERTIES"
"action": "DELIVER_ITEM"
}]
})
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
const { result } = await createOrderChangeActionsWorkflow(container)
.run({
input: [{
"action": "UPDATE_ORDER_PROPERTIES"
"action": "DELIVER_ITEM"
}]
})
@@ -89,7 +89,7 @@ export default async function myCustomJob(
const { result } = await createOrderChangeActionsWorkflow(container)
.run({
input: [{
"action": "UPDATE_ORDER_PROPERTIES"
"action": "DELIVER_ITEM"
}]
})
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
const result = createOrderChangeActionsWorkflow
.runAsStep({
input: [{
"action": "UPDATE_ORDER_PROPERTIES"
"action": "DELIVER_ITEM"
}]
})
}
@@ -39,7 +39,7 @@ export async function POST(
const { result } = await declineOrderChangeWorkflow(req.scope)
.run({
input: {
"id": "id_feqdUcXMDHpl5BhrF1"
"id": "id_wjqnrdNeurl7p"
}
})
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
const { result } = await declineOrderChangeWorkflow(container)
.run({
input: {
"id": "id_feqdUcXMDHpl5BhrF1"
"id": "id_wjqnrdNeurl7p"
}
})
@@ -89,7 +89,7 @@ export default async function myCustomJob(
const { result } = await declineOrderChangeWorkflow(container)
.run({
input: {
"id": "id_feqdUcXMDHpl5BhrF1"
"id": "id_wjqnrdNeurl7p"
}
})
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
const result = declineOrderChangeWorkflow
.runAsStep({
input: {
"id": "id_feqdUcXMDHpl5BhrF1"
"id": "id_wjqnrdNeurl7p"
}
})
}
@@ -40,7 +40,7 @@ export async function POST(
.run({
input: {
"ids": [
"id_ejESZlHpfen57ANk8MzQ"
"id_LZttQo8M2Bg"
]
}
})
@@ -67,7 +67,7 @@ export default async function handleOrderPlaced({
.run({
input: {
"ids": [
"id_ejESZlHpfen57ANk8MzQ"
"id_LZttQo8M2Bg"
]
}
})
@@ -94,7 +94,7 @@ export default async function myCustomJob(
.run({
input: {
"ids": [
"id_ejESZlHpfen57ANk8MzQ"
"id_LZttQo8M2Bg"
]
}
})
@@ -122,7 +122,7 @@ const myWorkflow = createWorkflow(
.runAsStep({
input: {
"ids": [
"id_ejESZlHpfen57ANk8MzQ"
"id_LZttQo8M2Bg"
]
}
})
@@ -40,7 +40,7 @@ export async function POST(
.run({
input: {
"ids": [
"id_cVndBSTOaDeZrGfM6"
"id_VmvGq4qPHDULT"
]
}
})
@@ -67,7 +67,7 @@ export default async function handleOrderPlaced({
.run({
input: {
"ids": [
"id_cVndBSTOaDeZrGfM6"
"id_VmvGq4qPHDULT"
]
}
})
@@ -94,7 +94,7 @@ export default async function myCustomJob(
.run({
input: {
"ids": [
"id_cVndBSTOaDeZrGfM6"
"id_VmvGq4qPHDULT"
]
}
})
@@ -122,7 +122,7 @@ const myWorkflow = createWorkflow(
.runAsStep({
input: {
"ids": [
"id_cVndBSTOaDeZrGfM6"
"id_VmvGq4qPHDULT"
]
}
})
@@ -23,15 +23,15 @@ const myWorkflow = createWorkflow(
() => {
const data = throwUnlessStatusIsNotPaid({
"paymentCollection": {
"id": "id_eAJuwhOu8bg3",
"currency_code": "bbd",
"id": "id_vGqsmproyL2X3fOeWS",
"currency_code": "bhd",
"amount": {
"numeric": 20
"numeric": 43
},
"status": "completed",
"status": "not_paid",
"payment_providers": [{
"id": "id_z5AQyPaFFn0",
"is_enabled": true
"id": "id_LMlRiwKBdHvYo",
"is_enabled": false
}]
}
})
@@ -39,7 +39,7 @@ export async function POST(
const { result } = await updateOrderChangeActionsWorkflow(req.scope)
.run({
input: [{
"id": "id_Iy3nRPjiHKo9sWnOrk"
"id": "id_NhKl53pnyDSMMKE2I"
}]
})
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
const { result } = await updateOrderChangeActionsWorkflow(container)
.run({
input: [{
"id": "id_Iy3nRPjiHKo9sWnOrk"
"id": "id_NhKl53pnyDSMMKE2I"
}]
})
@@ -89,7 +89,7 @@ export default async function myCustomJob(
const { result } = await updateOrderChangeActionsWorkflow(container)
.run({
input: [{
"id": "id_Iy3nRPjiHKo9sWnOrk"
"id": "id_NhKl53pnyDSMMKE2I"
}]
})
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
const result = updateOrderChangeActionsWorkflow
.runAsStep({
input: [{
"id": "id_Iy3nRPjiHKo9sWnOrk"
"id": "id_NhKl53pnyDSMMKE2I"
}]
})
}
@@ -39,7 +39,7 @@ export async function POST(
const { result } = await updateOrderChangesWorkflow(req.scope)
.run({
input: [{
"id": "id_HCKR2lBPbpaAtaU"
"id": "id_5n831DKhYtSIYgCvx"
}]
})
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
const { result } = await updateOrderChangesWorkflow(container)
.run({
input: [{
"id": "id_HCKR2lBPbpaAtaU"
"id": "id_5n831DKhYtSIYgCvx"
}]
})
@@ -89,7 +89,7 @@ export default async function myCustomJob(
const { result } = await updateOrderChangesWorkflow(container)
.run({
input: [{
"id": "id_HCKR2lBPbpaAtaU"
"id": "id_5n831DKhYtSIYgCvx"
}]
})
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
const result = updateOrderChangesWorkflow
.runAsStep({
input: [{
"id": "id_HCKR2lBPbpaAtaU"
"id": "id_5n831DKhYtSIYgCvx"
}]
})
}
@@ -24,8 +24,8 @@ const myWorkflow = createWorkflow(
const data = createPaymentSessionStep({
"payment_collection_id": "{value}",
"provider_id": "{value}",
"amount": 33,
"currency_code": "bnd"
"amount": 27,
"currency_code": "rwf"
})
}
)
@@ -27,7 +27,7 @@ const myWorkflow = createWorkflow(
() => {
const data = deletePaymentSessionsStep({
"ids": [
"id_CCBxiUKjH8BnhzJDbGgD"
"id_q21Y5traw5UZlU0"
]
})
}
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = updateRefundReasonsStep({
"id": "id_88bbRFUmjXhbI8wsvZn7"
"id": "id_ydhQzcRGB4f"
})
}
)
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = validatePriceListsStep({
"id": "id_eT8yS6k97cLV0Xq42"
"id": "id_r2rvymnVIzUW"
})
}
)
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createCollectionsStep([{
"title": "amplexus"
"title": "vestrum"
}])
}
)
@@ -21,7 +21,7 @@ const myWorkflow = createWorkflow(
() => {
const data = updatePromotionsValidationStep({
"promotionsData": [{
"id": "id_8RHhRkuHKZdQcFyvzazV",
"id": "id_e4yTROodl3VoMJIda0",
"status": "draft"
}]
})
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
"my-workflow",
() => {
const data = createStockLocations({
"name": "Eula"
"name": "Maybell"
})
}
)
@@ -4,9 +4,17 @@ slug: /references/file-provider-module
import { TypeList } from "docs-ui"
# How to Create a File Provider Module
# How to Create a File Module Provider
In this document, youll learn how to create a file provider module and the methods you must implement in its main service.
In this document, youll learn how to create a File Module Provider and the methods you must implement in its main service.
---
## Implementation Example
As you implement your File 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 [S3 File Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/file-s3).
---
@@ -26,9 +34,9 @@ The rest of this guide always uses the `src/modules/my-file` directory as an exa
---
## 2. Create the File Provider Service
## 2. Create the File Module Provider's Service
Create the file `src/modules/my-file/service.ts` that holds the implementation of the module's main service. It must extend the `AbstractFileProviderService` class imported from `@medusajs/framework/utils`:
Create the file `src/modules/my-file/service.ts` that holds the implementation of the module provider's main service. It must extend the `AbstractFileProviderService` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-file/service.ts"
import { AbstractFileProviderService } from "@medusajs/framework/utils"
@@ -224,7 +232,7 @@ class MyFileProviderService extends AbstractFileProviderService {
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-file/index.ts` with the following content:
@@ -240,11 +248,11 @@ export default ModuleProvider(Modules.FILE, {
})
```
This exports the module's definition, indicating that the `MyFileProviderService` is the module's service.
This exports the module provider's definition, indicating that the `MyFileProviderService` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your File Module Provider, add it to the `providers` array of the File Module in `medusa-config.ts`:
@@ -286,4 +294,4 @@ module.exports = defineConfig({
## 5. Test it Out
To test out your file provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
To test out your File Module Provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
@@ -9,17 +9,17 @@ sidebar_label: Create Fulfillment Provider
import { TypeList } from "docs-ui"
# How to Create a Fulfillment Provider Module
# How to Create a Fulfillment Module Provider
In this document, youll learn how to create a fulfillment provider module and the methods you must implement in its main service.
In this document, youll learn how to create a Fulfillment Module Provider and the methods you must implement in its main service.
---
## Understanding Fulfillment Provider Implementation
## Understanding Fulfillment Module Provider Implementation
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your fulfillment provider within core operations.
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your Fulfillment Module Provider within core operations.
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your fulfillment provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your fulfillment provider.
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your Fulfillment Module Provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your Fulfillment Module Provider.
---
@@ -39,9 +39,9 @@ The rest of this guide always uses the `src/modules/my-fulfillment` directory as
---
## 2. Create the Fulfillment Provider Service
## 2. Create the Fulfillment Module Provider Service
Create the file `src/modules/my-fulfillment/service.ts` that holds the module's main service. It must extend the `AbstractFulfillmentProviderService` class imported from `@medusajs/framework/utils`:
Create the file `src/modules/my-fulfillment/service.ts` that holds the module provider's main service. It must extend the `AbstractFulfillmentProviderService` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-fulfillment/service.ts"
import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
@@ -519,7 +519,7 @@ class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-fulfillment/index.ts` with the following content:
@@ -535,11 +535,11 @@ export default ModuleProvider(Modules.FULFILLMENT, {
})
```
This exports the module's definition, indicating that the `MyFulfillmentProviderService` is the module's service.
This exports the module provider's definition, indicating that the `MyFulfillmentProviderService` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your Fulfillment Module Provider, add it to the `providers` array of the Fulfillment Module in `medusa-config.ts`:
@@ -575,9 +575,9 @@ module.exports = defineConfig({
## 5. Test it Out
Before you use your fulfillment provider, in the Medusa Admin:
Before you use your Fulfillment Module Provider, in the Medusa Admin:
1. Add the fulfillment provider to a location.
1. Add the Fulfillment Module Provider to a location.
2. Add in the location a delivery shipping option that uses the provider.
Then, place an order, choosing the shipping option you created during checkout, and create a fulfillment in the Medusa Admin. The fulfillment is created using your provider.
@@ -0,0 +1,200 @@
---
slug: /references/locking-service
tags:
- locking
- server
- how to
sidebar_label: Use Locking Module
---
import { TypeList } from "docs-ui"
# How to Use Locking Module
In this document, youll learn about the different methods in the Locking Module's service and how to use them.
---
## Resolve Locking Module's Service
In your workflow's step, you can resolve the Locking Module's service from the Medusa container:
```ts
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
)
// TODO use lockingModuleService
}
)
```
You can then use the Locking Module's service's methods in the step. The rest of this guide details these methods.
---
## execute
This method executes a giuven asynchronous job with a lock on the given keys. You can optionally pass a
provider name to be used for locking. If no provider is passed, the default provider (in-memory or the
provider configuerd in `medusa-config.ts`) will be used.
### Example
For example, to use the lock module when deleting a product:
```ts
await lockingModuleService.execute("prod_123", async () => {
// assuming you've resolved the product service from the container
await productModuleService.delete("prod_123")
})
```
In the above example, the product of ID `prod_123` is locked while it's being deleted.
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
```ts
await lockingModuleService.execute("prod_123", async () => {
// assuming you've resolved the product service from the container
await productModuleService.delete("prod_123")
}, {
provider: "lp_my-lock"
})
```
### 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 durng 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.\nIts value defaults to `5` seconds if no value is passed or if you pass a value less than `1`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","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":[]}]}]} 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 execution.","expandable":false,"children":[{"name":"T","type":"T","optional":false,"defaultValue":"","description":"","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. You can optionally pass a provider name to be used for locking.
If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
You can pass an owner for the lock, which limits who can extend or release the acquired lock. Then, if you use this method again
passing the same owner, the lock's expiration time is extended with the value passed in the `expire` argument. Otherwise, if you pass a
different owner, the method throws an error.
### Example
For example, to acquire a lock on a product with ID `prod_123` for a user with ID `user_123`:
```ts
await lockingModuleService.acquire("prod_123", {
ownerId: "user_123",
expire: 60
})
```
In this example, you acquire a lock on the product with ID `prod_123` for the user with ID `user_123`. You extend the
lock's expiration time by `60` seconds.
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
```ts
await lockingModuleService.acquire("prod_123", {
ownerId: "user_123",
expire: 60,
provider: "lp_my-lock"
})
```
### 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 owner ID for the lock. 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":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","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":[]}]}]} 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. You can optionally pass a provider name to be used for locking.
If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
If the lock has an owner, you must pass the same owner to release the lock.
### Example
For example, to release a lock on a product with ID `prod_123` for a user with ID `user_123`:
```ts
await lockingModuleService.release("prod_123", {
ownerId: "user_123"
})
```
In this example, you release the lock on the product with ID `prod_123` for the user with ID `user_123`.
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
```ts
await lockingModuleService.release("prod_123", {
ownerId: "user_123",
provider: "lp_my-lock"
})
```
### 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":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","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":[]}]}]} 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. If you specify an owner ID, then all locks that the owner has acquired are released.
You can also pass a provider name to be used for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
### Example
For example, to release all locks for a user with ID `user_123`:
```ts
await lockingModuleService.releaseAll({
ownerId: "user_123"
})
```
In this example, you release all locks for the user with ID `user_123`.
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
```ts
await lockingModuleService.releaseAll({
ownerId: "user_123",
provider: "lp_my-lock"
})
```
### 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":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","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":[]}]}]} 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. If you specify an owner ID, then all locks that the owner has acquired are released.\n\nYou can also pass a provider name to be used for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
@@ -0,0 +1,440 @@
---
slug: /references/locking-module-provider
tags:
- locking
- server
- how to
sidebar_label: Create Locking Provider
---
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":[]}]}]} 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":[{"name":"T","type":"T","optional":false,"defaultValue":"","description":"","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":[]}]}]} 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":[]}]}]} 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":[]}]}]} 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)
@@ -0,0 +1,8 @@
import { TypeList } from "docs-ui"
# locking
## Interfaces
- [ILockingModule](../../locking/interfaces/locking.ILockingModule/page.mdx)
- [ILockingProvider](../../locking/interfaces/locking.ILockingProvider/page.mdx)
@@ -9,9 +9,17 @@ sidebar_label: Create Notification Provider
import { TypeList } from "docs-ui"
# How to Create a Notification Provider Module
# How to Create a Notification Module Provider
In this document, youll learn how to create a notification provider module and the methods you must implement in it.
In this document, youll learn how to create a Notification Module Provider and the methods you must implement in it.
---
## Implementation Example
As you implement your Notification 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 [SendGrid Notification Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/notification-sendgrid).
---
@@ -31,11 +39,11 @@ The rest of this guide always uses the `src/modules/my-notification` directory a
---
## 2. Create the Notification Provider Service
## 2. Create the Notification Module Provider's Service
Create the file `src/modules/my-notification/service.ts` that holds the implementation of the notification service.
The Notification Provider Module's main service must extend the `AbstractNotificationProviderService` class imported from `@medusajs/framework/utils`:
The Notification Module Provider's main service must extend the `AbstractNotificationProviderService` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-notification/service.ts"
import {
@@ -172,7 +180,7 @@ class MyNotificationProviderService extends AbstractNotificationProviderService
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-notification/index.ts` with the following content:
@@ -188,11 +196,11 @@ export default ModuleProvider(Modules.NOTIFICATION, {
})
```
This exports the module's definition, indicating that the `MyNotificationProviderService` is the module's service.
This exports the module provider's definition, indicating that the `MyNotificationProviderService` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your Notification Module Provider, add it to the `providers` array of the Notification Module in `medusa-config.ts`:
@@ -9,17 +9,25 @@ sidebar_label: Create Payment Provider
import { TypeList } from "docs-ui"
# How to Create a Payment Provider
# How to Create a Payment Module Provider
In this document, youll learn how to create a Payment Provider to be used with the Payment Module.
In this document, youll learn how to create a Payment Module Provider to be used with the Payment Module.
---
## Understanding Payment Provider Implementation
## Implementation Example
As you implement your Payment Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your payment provider within core operations.
If you need to refer to an existing implementation as an example, check the [Stripe Payment Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/payment-stripe).
For example, when the merchant captures an order's payment, the Payment Module uses the payment provider to capture the payment, the makes updates to the `Payment` record associated with the order. So, you only have to implement the third-party payment processing logic in your payment provider.
---
## Understanding Payment Module Provider Implementation
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your Payment Module Provider within core operations.
For example, when the merchant captures an order's payment, the Payment Module uses the Payment Module Provider to capture the payment, the makes updates to the `Payment` record associated with the order. So, you only have to implement the third-party payment processing logic in your Payment Module Provider.
---
@@ -39,9 +47,9 @@ The rest of this guide always uses the `src/modules/my-payment` directory as an
---
## 2. Create the Payment Provider Service
## 2. Create the Payment Module Provider's Service
Create the file `src/modules/my-payment/service.ts` that holds the module's main service. It must extend the `AbstractPaymentProvider` class imported from `@medusajs/framework/utils`:
Create the file `src/modules/my-payment/service.ts` that holds the module provider's main service. It must extend the `AbstractPaymentProvider` class imported from `@medusajs/framework/utils`:
```ts title="src/modules/my-payment/service.ts"
import { AbstractPaymentProvider } from "@medusajs/framework/utils"
@@ -630,7 +638,7 @@ class MyPaymentProviderService extends AbstractPaymentProvider<
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-payment/index.ts` with the following content:
@@ -646,11 +654,11 @@ export default ModuleProvider(Modules.PAYMENT, {
})
```
This exports the module's definition, indicating that the `MyPaymentProviderService` is the module's service.
This exports the module provider's definition, indicating that the `MyPaymentProviderService` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your Payment Module Provider, add it to the `providers` array of the Payment Module in `medusa-config.ts`:
@@ -682,12 +690,12 @@ module.exports = defineConfig({
## 5. Test it Out
Before you use your payment provider, enable it in a region using the Medusa Admin.
Before you use your Payment Module Provider, enable it in a region using the Medusa Admin.
Then, go through checkout to place an order. Your payment provider is used to authorize the payment.
Then, go through checkout to place an order. Your Payment Module Provider is used to authorize the payment.
---
## Useful Guides
- [Storefront Guide: how to implement UI for your payment provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
- [Storefront Guide: how to implement UI for your Payment Module Provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
@@ -9,23 +9,23 @@ sidebar_label: Create Tax Provider
import { TypeList } from "docs-ui"
# How to Create a Tax Provider
# How to Create a Tax Module Provider
In this document, youll learn how to create a tax provider to use with the Tax Module, and the methods to implement.
In this document, youll learn how to create a Tax Module Provider to use with the Tax Module, and the methods to implement.
---
## Overview
A tax provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default `system` provider. You can create your own tax provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.
A Tax Module Provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default `system` provider. You can create your own Tax Module Provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.
---
## Understanding Tax Provider Implementation
## Understanding Tax Module Provider Implementation
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your tax provider within core operations.
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your Tax Module Provider within core operations.
For example, during checkout, the tax provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your tax provider.
For example, during checkout, the Tax Module Provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your Tax Module Provider.
---
@@ -45,9 +45,9 @@ The rest of this guide always uses the `src/modules/my-tax` directory as an exam
---
## 2. Create the Tax Provider Service
## 2. Create the Tax Module Provider's Service
Create the file `src/modules/my-tax/service.ts` that holds the module's main service. It must extend the `ITaxProvider` class imported from `@medusajs/framework/types`:
Create the file `src/modules/my-tax/service.ts` that holds the module provider's main service. It must extend the `ITaxProvider` class imported from `@medusajs/framework/types`:
```ts title="src/modules/my-tax/service.ts"
import { ITaxProvider } from "@medusajs/framework/types"
@@ -156,7 +156,7 @@ export default class SystemTaxService implements ITaxProvider {
---
## 3. Create Module Definition File
## 3. Create Module Provider Definition File
Create the file `src/modules/my-tax/index.ts` with the following content:
@@ -172,11 +172,11 @@ export default ModuleProvider(Modules.TAX, {
})
```
This exports the module's definition, indicating that the `MyTaxProvider` is the module's service.
This exports the module provider's definition, indicating that the `MyTaxProvider` is the module provider's service.
---
## 4. Use Module
## 4. Use Module Provider
To use your Tax Module Provider, add it to the `providers` array of the Tax Module in `medusa-config.ts`:
@@ -10,7 +10,7 @@ export const architecturalModulesSidebar = [
},
{
type: "category",
title: "Cache Modules",
title: "Cache Module",
initialOpen: true,
children: [
{
@@ -19,14 +19,20 @@ export const architecturalModulesSidebar = [
title: "Overview",
},
{
type: "link",
path: "/architectural-modules/cache/in-memory",
title: "In-Memory",
},
{
type: "link",
path: "/architectural-modules/cache/redis",
title: "Redis",
type: "sub-category",
title: "Modules",
children: [
{
type: "link",
path: "/architectural-modules/cache/in-memory",
title: "In-Memory",
},
{
type: "link",
path: "/architectural-modules/cache/redis",
title: "Redis",
},
],
},
{
type: "sub-category",
@@ -43,7 +49,7 @@ export const architecturalModulesSidebar = [
},
{
type: "category",
title: "Event Modules",
title: "Event Module",
initialOpen: true,
children: [
{
@@ -52,14 +58,20 @@ export const architecturalModulesSidebar = [
title: "Overview",
},
{
type: "link",
path: "/architectural-modules/event/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/event/redis",
title: "Redis",
type: "sub-category",
title: "Modules",
children: [
{
type: "link",
path: "/architectural-modules/event/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/event/redis",
title: "Redis",
},
],
},
{
type: "sub-category",
@@ -76,7 +88,7 @@ export const architecturalModulesSidebar = [
},
{
type: "category",
title: "File Module Providers",
title: "File Module",
initialOpen: true,
children: [
{
@@ -85,14 +97,20 @@ export const architecturalModulesSidebar = [
title: "Overview",
},
{
type: "link",
path: "/architectural-modules/file/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/file/s3",
title: "AWS S3 (and Compatible APIs)",
type: "sub-category",
title: "Providers",
children: [
{
type: "link",
path: "/architectural-modules/file/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/file/s3",
title: "AWS S3 (and Compatible APIs)",
},
],
},
{
type: "sub-category",
@@ -109,7 +127,51 @@ export const architecturalModulesSidebar = [
},
{
type: "category",
title: "Notification Module Providers",
title: "Locking Module",
initialOpen: true,
children: [
{
type: "link",
path: "/architectural-modules/locking",
title: "Overview",
},
{
type: "sub-category",
title: "Providers",
children: [
{
type: "link",
path: "/architectural-modules/locking/redis",
title: "Redis",
},
{
type: "link",
path: "/architectural-modules/locking/postgres",
title: "PostgreSQL",
},
],
},
{
type: "sub-category",
title: "Guides",
children: [
{
type: "link",
path: "/references/locking-module-provider",
title: "Create Locking Provider",
},
{
type: "link",
path: "/references/locking-service",
title: "Use Locking Module",
},
],
},
],
},
{
type: "category",
title: "Notification Module",
initialOpen: true,
children: [
{
@@ -118,14 +180,20 @@ export const architecturalModulesSidebar = [
title: "Overview",
},
{
type: "link",
path: "/architectural-modules/notification/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/notification/sendgrid",
title: "SendGrid",
type: "sub-category",
title: "Providers",
children: [
{
type: "link",
path: "/architectural-modules/notification/local",
title: "Local",
},
{
type: "link",
path: "/architectural-modules/notification/sendgrid",
title: "SendGrid",
},
],
},
{
type: "sub-category",
@@ -137,7 +205,7 @@ export const architecturalModulesSidebar = [
title: "Create Notification Provider",
},
{
type: "link",
type: "ref",
path: "/integrations/guides/resend",
title: "Integrate Resend",
},
@@ -152,7 +220,7 @@ export const architecturalModulesSidebar = [
},
{
type: "category",
title: "Workflow Engine Modules",
title: "Workflow Engine Module",
initialOpen: true,
children: [
{
@@ -161,14 +229,20 @@ export const architecturalModulesSidebar = [
title: "Overview",
},
{
type: "link",
path: "/architectural-modules/workflow-engine/in-memory",
title: "In-Memory",
},
{
type: "link",
path: "/architectural-modules/workflow-engine/redis",
title: "Redis",
type: "sub-category",
title: "Modules",
children: [
{
type: "link",
path: "/architectural-modules/workflow-engine/in-memory",
title: "In-Memory",
},
{
type: "link",
path: "/architectural-modules/workflow-engine/redis",
title: "Redis",
},
],
},
],
},
@@ -59,6 +59,47 @@ const sidebarMappings: {
"/nextjs-starter",
],
},
{
module: async () =>
import(
"@/generated/generated-architectural-modules-sidebar.mjs"
) as Promise<{
default: Sidebar.Sidebar
}>,
paths: [
"/architectural-modules",
"/references/file-provider-module",
"/references/locking",
"/references/notification-provider-module",
],
},
{
module: async () =>
import("@/generated/generated-commerce-modules-sidebar.mjs") as Promise<{
default: Sidebar.Sidebar
}>,
paths: [
"/commerce-modules",
"/references/api-key",
"/references/auth",
"/references/cart",
"/references/currency",
"/references/customer",
"/references/fulfillment",
"/references/inventory",
"/references/order",
"/references/payment",
"/references/pricing",
"/references/product",
"/references/promotion",
"/references/region",
"/references/sales-channel",
"/references/stock-location",
"/references/store",
"/references/tax",
"/references/user",
],
},
{
module: async () =>
import("@/generated/generated-references-sidebar.mjs") as Promise<{
@@ -78,22 +119,6 @@ const sidebarMappings: {
"/references-overview",
],
},
{
module: async () =>
import(
"@/generated/generated-architectural-modules-sidebar.mjs"
) as Promise<{
default: Sidebar.Sidebar
}>,
paths: ["/architectural-modules"],
},
{
module: async () =>
import("@/generated/generated-commerce-modules-sidebar.mjs") as Promise<{
default: Sidebar.Sidebar
}>,
paths: ["/commerce-modules"],
},
{
module: async () =>
import("@/generated/generated-troubleshooting-sidebar.mjs") as Promise<{
@@ -38,6 +38,7 @@ export const MainNavItemDropdown = ({
className={clsx("transition-transform", isOpen && "rotate-180")}
/>
}
className="!flex"
/>
)
}
@@ -9,19 +9,22 @@ type MainNavItemLinkProps = {
item: NavigationItemLink
isActive: boolean
icon?: React.ReactNode
className?: string
}
export const MainNavItemLink = ({
item,
isActive,
icon,
className,
}: MainNavItemLinkProps) => {
return (
<LinkButton
href={item.link}
className={clsx(
isActive && "text-medusa-fg-base",
!isActive && "text-medusa-fg-muted hover:text-medusa-fg-subtle"
!isActive && "text-medusa-fg-muted hover:text-medusa-fg-subtle",
className
)}
>
{item.title}
+4
View File
@@ -30,6 +30,7 @@ export const navDropdownItems: NavigationItem[] = [
{
type: "dropdown",
title: "Build",
project: "resources",
children: [
{
type: "link",
@@ -57,6 +58,7 @@ export const navDropdownItems: NavigationItem[] = [
type: "dropdown",
title: "Tools",
link: "/resources/tools",
project: "resources",
children: [
{
type: "sub-menu",
@@ -94,6 +96,8 @@ export const navDropdownItems: NavigationItem[] = [
{
type: "dropdown",
title: "Reference",
project: "resources",
link: "/resources/references-overview",
children: [
{
type: "link",
@@ -2,7 +2,7 @@
import { usePathname } from "next/navigation"
import React, { createContext, useContext, useMemo } from "react"
import { NavigationItem } from "types"
import { MenuItem, NavigationItem, NavigationItemDropdown } from "types"
import { useSiteConfig } from "../SiteConifg"
export type MainNavContext = {
@@ -28,6 +28,46 @@ export const MainNavProvider = ({
const baseUrl = `${config.baseUrl}${config.basePath}`
const findActiveItem = (
items: NavigationItemDropdown["children"],
currentUrl: string
) => {
let item: MenuItem | undefined
let fallbackIndex: number | undefined
items.some((childItem, index) => {
if (childItem.type !== "link" && childItem.type !== "sub-menu") {
return false
}
if (childItem.type === "sub-menu") {
const activeChildRes = findActiveItem(childItem.items, currentUrl)
item = activeChildRes.item
fallbackIndex = activeChildRes.fallbackIndex
return !!item
}
const isItemActive = currentUrl.startsWith(childItem.link)
if (!isItemActive) {
return false
}
if (childItem.useAsFallback && fallbackIndex === undefined) {
fallbackIndex = index
return false
}
item = childItem
return true
})
return {
item,
fallbackIndex,
}
}
const activeItemIndex = useMemo(() => {
const currentUrl = `${baseUrl}${pathname}`.replace(/\/$/, "")
@@ -35,24 +75,15 @@ export const MainNavProvider = ({
const index = navItems.findIndex((item, index) => {
if (item.type === "dropdown") {
return item.children.some((childItem) => {
if (childItem.type !== "link") {
return
}
const { item: activeChild, fallbackIndex: childFallbackIndex } =
findActiveItem(item.children, currentUrl)
const isItemActive = currentUrl.startsWith(childItem.link)
if (activeChild) {
fallbackIndex = childFallbackIndex
return true
}
if (
isItemActive &&
childItem.useAsFallback &&
fallbackIndex === undefined
) {
fallbackIndex = index
return false
}
return isItemActive
})
return item.link && currentUrl.startsWith(item.link)
}
if (item.project && item.project !== config.project.key) {
+8
View File
@@ -35,6 +35,14 @@ export const howTo = [
"title": "Create Fulfillment Provider",
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider"
},
{
"title": "Use Locking Module",
"path": "https://docs.medusajs.com/resources/references/locking-service"
},
{
"title": "Create Locking Provider",
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
},
{
"title": "Create Notification Provider",
"path": "https://docs.medusajs.com/resources/references/notification-provider-module"
+24 -24
View File
@@ -1,45 +1,45 @@
export * from "./user-guide.js"
export * from "./fulfillment.js"
export * from "./order.js"
export * from "./payment.js"
export * from "./cache.js"
export * from "./how-to.js"
export * from "./pricing.js"
export * from "./server.js"
export * from "./product.js"
export * from "./order.js"
export * from "./inventory.js"
export * from "./auth.js"
export * from "./pricing.js"
export * from "./event.js"
export * from "./promotion.js"
export * from "./customer.js"
export * from "./user.js"
export * from "./notification.js"
export * from "./auth.js"
export * from "./api-key.js"
export * from "./workflow.js"
export * from "./stock-location.js"
export * from "./notification.js"
export * from "./region.js"
export * from "./cart.js"
export * from "./tutorial.js"
export * from "./inventory.js"
export * from "./sales-channel.js"
export * from "./tax.js"
export * from "./user.js"
export * from "./region.js"
export * from "./store.js"
export * from "./query.js"
export * from "./checkout.js"
export * from "./storefront.js"
export * from "./cart.js"
export * from "./currency.js"
export * from "./extend-module.js"
export * from "./example.js"
export * from "./product-category.js"
export * from "./stripe.js"
export * from "./tutorial.js"
export * from "./fulfillment.js"
export * from "./tax.js"
export * from "./concept.js"
export * from "./remote-query.js"
export * from "./step.js"
export * from "./link.js"
export * from "./stripe.js"
export * from "./storefront.js"
export * from "./product-category.js"
export * from "./query.js"
export * from "./checkout.js"
export * from "./publishable-api-key.js"
export * from "./logger.js"
export * from "./event-bus.js"
export * from "./locking.js"
export * from "./product-collection.js"
export * from "./step.js"
export * from "./example.js"
export * from "./customer.js"
export * from "./locking.js"
export * from "./remote-query.js"
export * from "./link.js"
export * from "./event-bus.js"
export * from "./logger.js"
export * from "./file.js"
export * from "./js-sdk.js"
export * from "./admin.js"
+8
View File
@@ -42,5 +42,13 @@ export const locking = [
{
"title": "createReservationsWorkflow",
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/createReservationsWorkflow"
},
{
"title": "Use Locking Module",
"path": "https://docs.medusajs.com/resources/references/locking-service"
},
{
"title": "Create Locking Provider",
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
}
]
+8
View File
@@ -63,6 +63,14 @@ export const server = [
"title": "Create Fulfillment Provider",
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider"
},
{
"title": "Use Locking Module",
"path": "https://docs.medusajs.com/resources/references/locking-service"
},
{
"title": "Create Locking Provider",
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
},
{
"title": "Create Notification Provider",
"path": "https://docs.medusajs.com/resources/references/notification-provider-module"
File diff suppressed because it is too large Load Diff
@@ -27,6 +27,11 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
name: "auth-provider",
parentIgnore: true,
}),
locking: getOptions({
entryPointPath: "packages/core/types/src/locking/index.ts",
tsConfigName: "types.json",
name: "locking",
}),
dml: getOptions({
entryPointPath: [
"packages/core/utils/src/dml/entity-builder.ts",
@@ -6,14 +6,14 @@ const authProviderOptions: FormattingOptionsType = {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create an auth provider module and the methods you must implement in its main service.`,
reflectionDescription: `In this document, youll learn how to create an Auth Module Provider and the methods you must implement in its main service.`,
frontmatterData: {
slug: "/references/auth/provider",
tags: ["auth", "server", "how to"],
sidebar_label: "Create Auth Provider",
},
reflectionTitle: {
fullReplacement: "How to Create an Auth Provider Module",
fullReplacement: "How to Create an Auth Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -24,6 +24,11 @@ const authProviderOptions: FormattingOptionsType = {
reflection_typeParameters: false,
},
startSections: [
`## Implementation Example
As you implement your Auth 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 [Google Auth Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/auth-google).`,
`## 1. Create Module Provider Directory
Start by creating a new directory for your module provider.
@@ -37,9 +42,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-auth\` directory as an example.
</Note>`,
`## 2. Create the Auth Provider Service
`## 2. Create the Auth Module Provider's Service
Create the file \`src/modules/my-auth/service.ts\` that holds the module's main service. It must extend the \`AbstractAuthModuleProvider\` class imported from \`@medusajs/framework/utils\`:
Create the file \`src/modules/my-auth/service.ts\` that holds the module provider's main service. It must extend the \`AbstractAuthModuleProvider\` class imported from \`@medusajs/framework/utils\`:
\`\`\`ts title="src/modules/my-auth/service.ts"
import { AbstractAuthModuleProvider } from "@medusajs/framework/utils"
@@ -52,7 +57,7 @@ export default MyAuthProviderService
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-auth/index.ts\` with the following content:
@@ -68,8 +73,8 @@ export default ModuleProvider(Modules.AUTH, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyAuthProviderService\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyAuthProviderService\` is the module provider's service.`,
`## 4. Use Module Provider
To use your Auth Module Provider, add it to the \`providers\` array of the Auth Module in \`medusa-config.ts\`:
@@ -107,9 +112,9 @@ module.exports = defineConfig({
`,
`## 5. Test it Out
To test out your authentication provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
To test out your Authentication Module Provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
For example, to get a registration token for an admin user, send a \`POST\` request to \`/auth/user/my-auth/register\` replacing \`my-auth\` with your authentication provider's ID:
For example, to get a registration token for an admin user, send a \`POST\` request to \`/auth/user/my-auth/register\` replacing \`my-auth\` with your Authentication Module Provider's ID:
\`\`\`bash
curl -X POST http://localhost:9000/auth/user/my-auth/register
@@ -120,7 +125,7 @@ curl -X POST http://localhost:9000/auth/user/my-auth/register
}'
\`\`\`
Change the request body to pass the data required for your authentication provider to register the user.
Change the request body to pass the data required for your Authentication Module Provider to register the user.
If registration is successful, the response will have a \`token\` property.
`,
@@ -6,12 +6,12 @@ const fileOptions: FormattingOptionsType = {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a file provider module and the methods you must implement in its main service.`,
reflectionDescription: `In this document, youll learn how to create a File Module Provider and the methods you must implement in its main service.`,
frontmatterData: {
slug: "/references/file-provider-module",
},
reflectionTitle: {
fullReplacement: "How to Create a File Provider Module",
fullReplacement: "How to Create a File Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -22,6 +22,11 @@ const fileOptions: FormattingOptionsType = {
reflection_typeParameters: false,
},
startSections: [
`## Implementation Example
As you implement your File 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 [S3 File Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/file-s3).`,
`## Create Module Provider Directory
Start by creating a new directory for your module provider.
@@ -35,9 +40,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-file\` directory as an example.
</Note>`,
`## 2. Create the File Provider Service
`## 2. Create the File Module Provider's Service
Create the file \`src/modules/my-file/service.ts\` that holds the implementation of the module's main service. It must extend the \`AbstractFileProviderService\` class imported from \`@medusajs/framework/utils\`:
Create the file \`src/modules/my-file/service.ts\` that holds the implementation of the module provider's main service. It must extend the \`AbstractFileProviderService\` class imported from \`@medusajs/framework/utils\`:
\`\`\`ts title="src/modules/my-file/service.ts"
import { AbstractFileProviderService } from "@medusajs/framework/utils"
@@ -50,7 +55,7 @@ export default MyFileProviderService
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-file/index.ts\` with the following content:
@@ -66,8 +71,8 @@ export default ModuleProvider(Modules.FILE, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyFileProviderService\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyFileProviderService\` is the module provider's service.`,
`## 4. Use Module Provider
To use your File Module Provider, add it to the \`providers\` array of the File Module in \`medusa-config.ts\`:
@@ -107,7 +112,7 @@ module.exports = defineConfig({
`,
`## 5. Test it Out
To test out your file provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
To test out your File Module Provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
`,
],
},
@@ -6,14 +6,14 @@ const fulfillmentProviderOptions: FormattingOptionsType = {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a fulfillment provider module and the methods you must implement in its main service.`,
reflectionDescription: `In this document, youll learn how to create a Fulfillment Module Provider and the methods you must implement in its main service.`,
frontmatterData: {
slug: "/references/fulfillment/provider",
tags: ["fulfillment", "server", "how to"],
sidebar_label: "Create Fulfillment Provider",
},
reflectionTitle: {
fullReplacement: "How to Create a Fulfillment Provider Module",
fullReplacement: "How to Create a Fulfillment Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -24,11 +24,11 @@ const fulfillmentProviderOptions: FormattingOptionsType = {
reflection_typeParameters: false,
},
startSections: [
`## Understanding Fulfillment Provider Implementation
`## Understanding Fulfillment Module Provider Implementation
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your fulfillment provider within core operations.
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your Fulfillment Module Provider within core operations.
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your fulfillment provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your fulfillment provider.
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your Fulfillment Module Provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your Fulfillment Module Provider.
`,
`## 1. Create Module Provider Directory
@@ -43,9 +43,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-fulfillment\` directory as an example.
</Note>`,
`## 2. Create the Fulfillment Provider Service
`## 2. Create the Fulfillment Module Provider Service
Create the file \`src/modules/my-fulfillment/service.ts\` that holds the module's main service. It must extend the \`AbstractFulfillmentProviderService\` class imported from \`@medusajs/framework/utils\`:
Create the file \`src/modules/my-fulfillment/service.ts\` that holds the module provider's main service. It must extend the \`AbstractFulfillmentProviderService\` class imported from \`@medusajs/framework/utils\`:
\`\`\`ts title="src/modules/my-fulfillment/service.ts"
import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
@@ -58,7 +58,7 @@ export default MyFulfillmentProviderService
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-fulfillment/index.ts\` with the following content:
@@ -74,8 +74,8 @@ export default ModuleProvider(Modules.FULFILLMENT, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyFulfillmentProviderService\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyFulfillmentProviderService\` is the module provider's service.`,
`## 4. Use Module Provider
To use your Fulfillment Module Provider, add it to the \`providers\` array of the Fulfillment Module in \`medusa-config.ts\`:
@@ -109,9 +109,9 @@ module.exports = defineConfig({
`,
`## 5. Test it Out
Before you use your fulfillment provider, in the Medusa Admin:
Before you use your Fulfillment Module Provider, in the Medusa Admin:
1. Add the fulfillment provider to a location.
1. Add the Fulfillment Module Provider to a location.
2. Add in the location a delivery shipping option that uses the provider.
Then, place an order, choosing the shipping option you created during checkout, and create a fulfillment in the Medusa Admin. The fulfillment is created using your provider.
@@ -13,6 +13,7 @@ import workflowsOptions from "./workflows.js"
import dmlOptions from "./dml.js"
import coreFlowsOptions from "./core-flows.js"
import jsSdkOptions from "./js-sdk.js"
import lockingOptions from "./locking.js"
const mergerCustomOptions: FormattingOptionsType = {
...authProviderOptions,
@@ -22,6 +23,7 @@ const mergerCustomOptions: FormattingOptionsType = {
...fulfillmentProviderOptions,
...helperStepsOptions,
...jsSdkOptions,
...lockingOptions,
...medusaConfigOptions,
...medusaOptions,
...notificationOptions,
@@ -0,0 +1,170 @@
import { FormattingOptionsType } from "types"
import baseSectionsOptions from "../base-section-options.js"
const lockingOptions: FormattingOptionsType = {
"^locking/.*ILockingProvider": {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a Locking Module Provider and the methods you must implement in its main service.`,
frontmatterData: {
slug: "/references/locking-module-provider",
tags: ["locking", "server", "how to"],
sidebar_label: "Create Locking Provider",
},
reflectionTitle: {
fullReplacement: "How to Create a Locking Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
expandProperties: true,
sections: {
...baseSectionsOptions,
member_declaration_title: false,
reflection_typeParameters: false,
},
startSections: [
`## 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
\`\`\``,
],
endSections: [
`## 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)
`,
],
},
"^locking/.*ILockingModule": {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn about the different methods in the Locking Module's service and how to use them.`,
frontmatterData: {
slug: "/references/locking-service",
tags: ["locking", "server", "how to"],
sidebar_label: "Use Locking Module",
},
reflectionTitle: {
fullReplacement: "How to Use Locking Module",
},
expandMembers: true,
startSections: [
`## Resolve Locking Module's Service
In your workflow's step, you can resolve the Locking Module's service from the Medusa container:
\`\`\`ts
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
)
// TODO use lockingModuleService
}
)
\`\`\`
You can then use the Locking Module's service's methods in the step. The rest of this guide details these methods.
---
`,
],
},
}
export default lockingOptions
@@ -6,14 +6,14 @@ const notificationOptions: FormattingOptionsType = {
reflectionGroups: {
Constructors: false,
},
reflectionDescription: `In this document, youll learn how to create a notification provider module and the methods you must implement in it.`,
reflectionDescription: `In this document, youll learn how to create a Notification Module Provider and the methods you must implement in it.`,
frontmatterData: {
slug: "/references/notification-provider-module",
tags: ["notification", "server", "how to"],
sidebar_label: "Create Notification Provider",
},
reflectionTitle: {
fullReplacement: "How to Create a Notification Provider Module",
fullReplacement: "How to Create a Notification Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -24,6 +24,11 @@ const notificationOptions: FormattingOptionsType = {
reflection_typeParameters: false,
},
startSections: [
`## Implementation Example
As you implement your Notification 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 [SendGrid Notification Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/notification-sendgrid).`,
`## 1. Create Module Provider Directory
Start by creating a new directory for your module provider.
@@ -37,11 +42,11 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-notification\` directory as an example.
</Note>`,
`## 2. Create the Notification Provider Service
`## 2. Create the Notification Module Provider's Service
Create the file \`src/modules/my-notification/service.ts\` that holds the implementation of the notification service.
The Notification Provider Module's main service must extend the \`AbstractNotificationProviderService\` class imported from \`@medusajs/framework/utils\`:
The Notification Module Provider's main service must extend the \`AbstractNotificationProviderService\` class imported from \`@medusajs/framework/utils\`:
\`\`\`ts title="src/modules/my-notification/service.ts"
import {
@@ -56,7 +61,7 @@ export default MyNotificationProviderService
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-notification/index.ts\` with the following content:
@@ -72,8 +77,8 @@ export default ModuleProvider(Modules.NOTIFICATION, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyNotificationProviderService\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyNotificationProviderService\` is the module provider's service.`,
`## 4. Use Module Provider
To use your Notification Module Provider, add it to the \`providers\` array of the Notification Module in \`medusa-config.ts\`:
@@ -6,14 +6,14 @@ const paymentProviderOptions: FormattingOptionsType = {
maxLevel: 2,
},
"^payment_provider/.*AbstractPaymentProvider": {
reflectionDescription: `In this document, youll learn how to create a Payment Provider to be used with the Payment Module.`,
reflectionDescription: `In this document, youll learn how to create a Payment Module Provider to be used with the Payment Module.`,
frontmatterData: {
slug: "/references/payment/provider",
tags: ["payment", "server", "how to"],
sidebar_label: "Create Payment Provider",
},
reflectionTitle: {
fullReplacement: "How to Create a Payment Provider",
fullReplacement: "How to Create a Payment Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -24,11 +24,16 @@ const paymentProviderOptions: FormattingOptionsType = {
reflection_typeParameters: false,
},
startSections: [
`## Understanding Payment Provider Implementation
`## Implementation Example
As you implement your Payment Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your payment provider within core operations.
If you need to refer to an existing implementation as an example, check the [Stripe Payment Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/payment-stripe).`,
`## Understanding Payment Module Provider Implementation
For example, when the merchant captures an order's payment, the Payment Module uses the payment provider to capture the payment, the makes updates to the \`Payment\` record associated with the order. So, you only have to implement the third-party payment processing logic in your payment provider.
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your Payment Module Provider within core operations.
For example, when the merchant captures an order's payment, the Payment Module uses the Payment Module Provider to capture the payment, the makes updates to the \`Payment\` record associated with the order. So, you only have to implement the third-party payment processing logic in your Payment Module Provider.
`,
`## 1. Create Module Provider Directory
@@ -43,9 +48,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-payment\` directory as an example.
</Note>`,
`## 2. Create the Payment Provider Service
`## 2. Create the Payment Module Provider's Service
Create the file \`src/modules/my-payment/service.ts\` that holds the module's main service. It must extend the \`AbstractPaymentProvider\` class imported from \`@medusajs/framework/utils\`:
Create the file \`src/modules/my-payment/service.ts\` that holds the module provider's main service. It must extend the \`AbstractPaymentProvider\` class imported from \`@medusajs/framework/utils\`:
\`\`\`ts title="src/modules/my-payment/service.ts"
import { AbstractPaymentProvider } from "@medusajs/framework/utils"
@@ -64,7 +69,7 @@ export default MyPaymentProviderService
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-payment/index.ts\` with the following content:
@@ -80,8 +85,8 @@ export default ModuleProvider(Modules.PAYMENT, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyPaymentProviderService\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyPaymentProviderService\` is the module provider's service.`,
`## 4. Use Module Provider
To use your Payment Module Provider, add it to the \`providers\` array of the Payment Module in \`medusa-config.ts\`:
@@ -111,13 +116,13 @@ module.exports = defineConfig({
`,
`## 5. Test it Out
Before you use your payment provider, enable it in a region using the Medusa Admin.
Before you use your Payment Module Provider, enable it in a region using the Medusa Admin.
Then, go through checkout to place an order. Your payment provider is used to authorize the payment.
Then, go through checkout to place an order. Your Payment Module Provider is used to authorize the payment.
`,
`## Useful Guides
- [Storefront Guide: how to implement UI for your payment provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
- [Storefront Guide: how to implement UI for your Payment Module Provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
`,
],
},
@@ -5,14 +5,14 @@ const taxProviderOptions: FormattingOptionsType = {
reflectionGroups: {
Properties: false,
},
reflectionDescription: `In this document, youll learn how to create a tax provider to use with the Tax Module, and the methods to implement.`,
reflectionDescription: `In this document, youll learn how to create a Tax Module Provider to use with the Tax Module, and the methods to implement.`,
frontmatterData: {
slug: "/references/tax/provider",
tags: ["tax", "server", "how to"],
sidebar_label: "Create Tax Provider",
},
reflectionTitle: {
fullReplacement: "How to Create a Tax Provider",
fullReplacement: "How to Create a Tax Module Provider",
},
shouldIncrementAfterStartSections: true,
expandMembers: true,
@@ -20,12 +20,12 @@ const taxProviderOptions: FormattingOptionsType = {
startSections: [
`## Overview
A tax provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default \`system\` provider. You can create your own tax provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.`,
`## Understanding Tax Provider Implementation
A Tax Module Provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default \`system\` provider. You can create your own Tax Module Provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.`,
`## Understanding Tax Module Provider Implementation
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your tax provider within core operations.
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your Tax Module Provider within core operations.
For example, during checkout, the tax provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your tax provider.`,
For example, during checkout, the Tax Module Provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your Tax Module Provider.`,
`## 1. Create Module Provider Directory
Start by creating a new directory for your module provider.
@@ -39,9 +39,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
The rest of this guide always uses the \`src/modules/my-tax\` directory as an example.
</Note>`,
`## 2. Create the Tax Provider Service
`## 2. Create the Tax Module Provider's Service
Create the file \`src/modules/my-tax/service.ts\` that holds the module's main service. It must extend the \`ITaxProvider\` class imported from \`@medusajs/framework/types\`:
Create the file \`src/modules/my-tax/service.ts\` that holds the module provider's main service. It must extend the \`ITaxProvider\` class imported from \`@medusajs/framework/types\`:
\`\`\`ts title="src/modules/my-tax/service.ts"
import { ITaxProvider } from "@medusajs/framework/types"
@@ -52,7 +52,7 @@ export default class MyTaxProvider implements ITaxProvider {
\`\`\``,
],
endSections: [
`## 3. Create Module Definition File
`## 3. Create Module Provider Definition File
Create the file \`src/modules/my-tax/index.ts\` with the following content:
@@ -68,8 +68,8 @@ export default ModuleProvider(Modules.TAX, {
})
\`\`\`
This exports the module's definition, indicating that the \`MyTaxProvider\` is the module's service.`,
`## 4. Use Module
This exports the module provider's definition, indicating that the \`MyTaxProvider\` is the module provider's service.`,
`## 4. Use Module Provider
To use your Tax Module Provider, add it to the \`providers\` array of the Tax Module in \`medusa-config.ts\`:
@@ -38,6 +38,7 @@ const allReferences = [
"types",
"utils",
"workflows",
"locking",
]
export default allReferences