diff --git a/www/apps/api-reference/specs/admin/openapi.full.yaml b/www/apps/api-reference/specs/admin/openapi.full.yaml
index 4864fe859b..920b5b83a6 100644
--- a/www/apps/api-reference/specs/admin/openapi.full.yaml
+++ b/www/apps/api-reference/specs/admin/openapi.full.yaml
@@ -461,7 +461,7 @@ tags:
You can upload public files, such as product images, or private files, such as CSV files used to import products.
externalDocs:
description: Check out available file module providers.
- url: https://docs.medusajs.com/resources/architectural-modules/file
+ url: https://docs.medusajs.com/resources/infrastructure-modules/file
- name: Users
description: |
A user is an admin user that can authenticate and perform functionalities as an admin user.
@@ -481,7 +481,7 @@ tags:
Depending on the workflow engine you use, executions may only be retained for a short while, or only until the Medusa application is restarted.
externalDocs:
description: Check out available Workflow Engine Modules
- url: https://docs.medusajs.com/resources/architectural-modules/workflow-engine
+ url: https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine
paths:
/admin/api-keys:
get:
@@ -50279,7 +50279,7 @@ paths:
description: |
Generate a reset password token for an admin user. This API route doesn't reset the admin's password or send them the reset instructions in a notification.
- Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the user a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification), and it should have the URL to reset the password in the Medusa Admin dashboard, such as `http://localhost:9000/app/reset-password?token=123`.
+ Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the user a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification), and it should have the URL to reset the password in the Medusa Admin dashboard, such as `http://localhost:9000/app/reset-password?token=123`.
Use the generated token to update the user's password using the [Reset Password API route](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providerupdate).
diff --git a/www/apps/api-reference/specs/admin/openapi.yaml b/www/apps/api-reference/specs/admin/openapi.yaml
index cb0a0060d5..b66897200d 100644
--- a/www/apps/api-reference/specs/admin/openapi.yaml
+++ b/www/apps/api-reference/specs/admin/openapi.yaml
@@ -610,7 +610,7 @@ tags:
such as CSV files used to import products.
externalDocs:
description: Check out available file module providers.
- url: https://docs.medusajs.com/resources/architectural-modules/file
+ url: https://docs.medusajs.com/resources/infrastructure-modules/file
- name: Users
description: >
A user is an admin user that can authenticate and perform functionalities
@@ -638,7 +638,7 @@ tags:
externalDocs:
description: Check out available Workflow Engine Modules
url: >-
- https://docs.medusajs.com/resources/architectural-modules/workflow-engine
+ https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine
paths:
/admin/api-keys:
$ref: paths/admin_api-keys.yaml
diff --git a/www/apps/api-reference/specs/admin/paths/auth_user_{auth_provider}_reset-password.yaml b/www/apps/api-reference/specs/admin/paths/auth_user_{auth_provider}_reset-password.yaml
index 6a01dec6e7..6c7a2e1df8 100644
--- a/www/apps/api-reference/specs/admin/paths/auth_user_{auth_provider}_reset-password.yaml
+++ b/www/apps/api-reference/specs/admin/paths/auth_user_{auth_provider}_reset-password.yaml
@@ -14,7 +14,7 @@ post:
guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password),
then send the user a notification. The notification is sent using a
[Notification Module
- Provider](https://docs.medusajs.com/resources/architectural-modules/notification),
+ Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification),
and it should have the URL to reset the password in the Medusa Admin
dashboard, such as `http://localhost:9000/app/reset-password?token=123`.
diff --git a/www/apps/api-reference/specs/store/openapi.full.yaml b/www/apps/api-reference/specs/store/openapi.full.yaml
index 38fb1a7c35..d072824c71 100644
--- a/www/apps/api-reference/specs/store/openapi.full.yaml
+++ b/www/apps/api-reference/specs/store/openapi.full.yaml
@@ -447,7 +447,7 @@ paths:
description: |
Generate a reset password token for a customer. This API route doesn't reset the customer password or send them the reset instructions in a notification.
- Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the customer a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification), and it should have a URL that accepts a `token` query parameter, allowing the customer to reset their password from the storefront.
+ Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the customer a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification), and it should have a URL that accepts a `token` query parameter, allowing the customer to reset their password from the storefront.
Use the generated token to update the customer's password using the [Reset Password API route](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerupdate).
diff --git a/www/apps/api-reference/specs/store/paths/auth_customer_{auth_provider}_reset-password.yaml b/www/apps/api-reference/specs/store/paths/auth_customer_{auth_provider}_reset-password.yaml
index 323bdbeca0..77174b36cb 100644
--- a/www/apps/api-reference/specs/store/paths/auth_customer_{auth_provider}_reset-password.yaml
+++ b/www/apps/api-reference/specs/store/paths/auth_customer_{auth_provider}_reset-password.yaml
@@ -13,7 +13,7 @@ post:
guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password),
then send the customer a notification. The notification is sent using a
[Notification Module
- Provider](https://docs.medusajs.com/resources/architectural-modules/notification),
+ Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification),
and it should have a URL that accepts a `token` query parameter, allowing
the customer to reset their password from the storefront.
diff --git a/www/apps/book/app/learn/configurations/medusa-config/page.mdx b/www/apps/book/app/learn/configurations/medusa-config/page.mdx
index 78ae71cc02..38369d5318 100644
--- a/www/apps/book/app/learn/configurations/medusa-config/page.mdx
+++ b/www/apps/book/app/learn/configurations/medusa-config/page.mdx
@@ -636,7 +636,7 @@ The value of this configuration is prepended to `sess:`. For example, if you set
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/architectural-modules/cache/redis).
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/infrastructure-modules/cache/redis).
@@ -656,7 +656,7 @@ module.exports = defineConfig({
The `projectConfig.redisUrl` configuration specifies the connection URL to Redis to store the Medusa server session. When specified, the Medusa server uses Redis to store the session data. Otherwie, the session data is stored in-memory.
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/architectural-modules/cache/redis). You'll have to configure the Redis connection for those modules separately.
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/infrastructure-modules/cache/redis). You'll have to configure the Redis connection for those modules separately.
@@ -699,7 +699,7 @@ The `projectConfig.sessionOptions` configuration defines additional options to p
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/architectural-modules/cache/redis).
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](!resources!/infrastructure-modules/cache/redis).
@@ -895,7 +895,7 @@ module.exports = defineConfig({
## Module Configurations (`modules`)
-The `modules` configuration allows you to register and configure the [modules](../../fundamentals/modules/page.mdx) registered in the Medusa application. Medusa's commerce and architectural modules are configured by default. So, you only need to pass your custom modules, or override the default configurations of the existing modules.
+The `modules` configuration allows you to register and configure the [modules](../../fundamentals/modules/page.mdx) registered in the Medusa application. Medusa's commerce and Infrastructure Modules are configured by default. So, you only need to pass your custom modules, or override the default configurations of the existing modules.
`modules` is an array of objects for the modules to register. Each object has the following properties:
diff --git a/www/apps/book/app/learn/customization/extend-features/define-link/page.mdx b/www/apps/book/app/learn/customization/extend-features/define-link/page.mdx
index 3687c4134b..af9d0d1a00 100644
--- a/www/apps/book/app/learn/customization/extend-features/define-link/page.mdx
+++ b/www/apps/book/app/learn/customization/extend-features/define-link/page.mdx
@@ -8,7 +8,7 @@ export const metadata = {
In this chapter, you'll learn how to define a module link between a brand defined in the [custom Brand Module](../../custom-features/module/page.mdx), and a product defined in the [Product Module](!resources!/commerce-modules/product) that's available in your Medusa application out-of-the-box.
-Modules are [isolated](../../../fundamentals/modules/isolation/page.mdx) from other resources, ensuring that they're integrated into the Medusa application without side effects. However, you may need to associate data models of different modules, or you're trying to extend data models from commerce modules with custom properties. To do that, you define module links.
+Modules are [isolated](../../../fundamentals/modules/isolation/page.mdx) from other resources, ensuring that they're integrated into the Medusa application without side effects. However, you may need to associate data models of different modules, or you're trying to extend data models from Commerce Modules with custom properties. To do that, you define module links.
A module link forms an association between two data models of different modules while maintaining module isolation. You can then manage and query linked records of the data models using Medusa's Modules SDK.
diff --git a/www/apps/book/app/learn/customization/next-steps/page.mdx b/www/apps/book/app/learn/customization/next-steps/page.mdx
index 933175f405..216f0a9bfb 100644
--- a/www/apps/book/app/learn/customization/next-steps/page.mdx
+++ b/www/apps/book/app/learn/customization/next-steps/page.mdx
@@ -12,7 +12,7 @@ The next chapters will cover each of these concepts in depth, with the different
The following guides and references are useful for your development journey:
-3. [Commerce Modules](!resources!/commerce-modules): Browse the list of commerce modules in Medusa and their references to learn how to use them.
+3. [Commerce Modules](!resources!/commerce-modules): Browse the list of Commerce Modules in Medusa and their references to learn how to use them.
1. [Service Factory Reference](!resources!/service-factory-reference): Learn about the methods generated by `MedusaService` with examples.
2. [Workflows Reference](!resources!/medusa-workflows-reference): Browse the list of core workflows and their hooks that are useful for your customizations.
4. [Admin Injection Zones](!resources!/admin-widget-injection-zones): Browse the injection zones in the Medusa Admin to learn where you can inject widgets.
diff --git a/www/apps/book/app/learn/deployment/general/page.mdx b/www/apps/book/app/learn/deployment/general/page.mdx
index 92f9f3494a..e0259aa890 100644
--- a/www/apps/book/app/learn/deployment/general/page.mdx
+++ b/www/apps/book/app/learn/deployment/general/page.mdx
@@ -128,11 +128,11 @@ By default, your Medusa application uses modules and providers useful for develo
It’s highly recommended to instead use modules and providers suitable for production, including:
-- [Redis Cache Module](!resources!/architectural-modules/cache/redis)
-- [Redis Event Bus Module](!resources!/architectural-modules/event/redis)
-- [Workflow Engine Redis Module](!resources!/architectural-modules/workflow-engine/redis)
-- [S3 File Module Provider](!resources!/architectural-modules/file/s3) (or other file module providers production-ready).
-- [SendGrid Notification Module Provider](!resources!/architectural-modules/notification/sendgrid) (or other notification module providers production-ready).
+- [Redis Cache Module](!resources!/infrastructure-modules/cache/redis)
+- [Redis Event Bus Module](!resources!/infrastructure-modules/event/redis)
+- [Workflow Engine Redis Module](!resources!/infrastructure-modules/workflow-engine/redis)
+- [S3 File Module Provider](!resources!/infrastructure-modules/file/s3) (or other file module providers production-ready).
+- [SendGrid Notification Module Provider](!resources!/infrastructure-modules/notification/sendgrid) (or other notification module providers production-ready).
Then, add these modules in `medusa-config.ts`:
@@ -168,7 +168,7 @@ module.exports = defineConfig({
-Check out the [Integrations](!resources!/integrations) and [Architectural Modules](!resources!/architectural-modules) documentation for other modules and providers to use.
+Check out the [Integrations](!resources!/integrations) and [Infrastructure Modules](!resources!/infrastructure-modules) documentation for other modules and providers to use.
@@ -217,7 +217,7 @@ Where:
- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
- Set the Redis database's connection URL as the value of `REDIS_URL`.
-Feel free to add any other relevant environment variables, such as for integrations and architectural modules. If you're using environment variables in your admin customizations, make sure to set them as well, as they're inlined during the build process.
+Feel free to add any other relevant environment variables, such as for integrations and Infrastructure Modules. If you're using environment variables in your admin customizations, make sure to set them as well, as they're inlined during the build process.
### Set Start Command
@@ -296,7 +296,7 @@ Where:
- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
- Set the Redis database's connection URL as the value of `REDIS_URL`.
-Feel free to add any other relevant environment variables, such as for integrations and architectural modules.
+Feel free to add any other relevant environment variables, such as for integrations and Infrastructure Modules.
### Set Start Command
diff --git a/www/apps/book/app/learn/fundamentals/api-routes/additional-data/page.mdx b/www/apps/book/app/learn/fundamentals/api-routes/additional-data/page.mdx
index 909440232e..253b73a769 100644
--- a/www/apps/book/app/learn/fundamentals/api-routes/additional-data/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/api-routes/additional-data/page.mdx
@@ -12,7 +12,7 @@ In this chapter, you'll learn how to pass additional data in requests to Medusa'
Some of Medusa's API Routes accept an `additional_data` parameter whose type is an object. The API Route passes the `additional_data` to the workflow, which in turn passes it to its hooks.
-This is useful when you have a link from your custom module to a commerce module, and you want to perform an additional action when a request is sent to an existing API route.
+This is useful when you have a link from your custom module to a Commerce Module, and you want to perform an additional action when a request is sent to an existing API route.
For example, the [Create Product API Route](!api!/admin#products_postproducts) accepts an `additional_data` parameter. If you have a data model linked to it, you consume the `productsCreated` hook to create a record of the data model using the custom data and link it to the product.
diff --git a/www/apps/book/app/learn/fundamentals/api-routes/parse-body/page.mdx b/www/apps/book/app/learn/fundamentals/api-routes/parse-body/page.mdx
index bf166e23ab..287bfefce3 100644
--- a/www/apps/book/app/learn/fundamentals/api-routes/parse-body/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/api-routes/parse-body/page.mdx
@@ -152,7 +152,7 @@ The uploaded files are stored in the `req.files` property as an array of Multer
### Uploading Files using File Module Provider
-The recommended way to upload the files to storage using the configured [File Module Provider](!resources!/architectural-modules/file) is to use the [uploadFilesWorkflow](!resources!/references/medusa-workflows/uploadFilesWorkflow):
+The recommended way to upload the files to storage using the configured [File Module Provider](!resources!/infrastructure-modules/file) is to use the [uploadFilesWorkflow](!resources!/references/medusa-workflows/uploadFilesWorkflow):
```ts title="src/api/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
diff --git a/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx b/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx
index 89457a41d9..c22d339557 100644
--- a/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/events-and-subscribers/page.mdx
@@ -98,11 +98,11 @@ The first message indicates that the `order.placed` event was emitted, and the s
## Event Module
-The subscription and emitting of events is handled by an Event Module, an architectural module that implements the pub/sub functionalities of Medusa's event system.
+The subscription and emitting of events is handled by an Event Module, an Infrastructure Module that implements the pub/sub functionalities of Medusa's event system.
Medusa provides two Event Modules out of the box:
-- [Local Event Module](!resources!/architectural-modules/event/local), used by default. It's useful for development, as you don't need additional setup to use it.
-- [Redis Event Module](!resources!/architectural-modules/event/redis), which is useful in production. It uses [Redis](https://redis.io/) to implement Medusa's pub/sub events system.
+- [Local Event Module](!resources!/infrastructure-modules/event/local), used by default. It's useful for development, as you don't need additional setup to use it.
+- [Redis Event Module](!resources!/infrastructure-modules/event/redis), which is useful in production. It uses [Redis](https://redis.io/) to implement Medusa's pub/sub events system.
-Medusa's [architecture](../../introduction/architecture/page.mdx) also allows you to build a custom Event Module that uses a different service or logic to implement the pub/sub system. Learn how to build an Event Module in [this guide](!resources!/architectural-modules/event/create).
+Medusa's [architecture](../../introduction/architecture/page.mdx) also allows you to build a custom Event Module that uses a different service or logic to implement the pub/sub system. Learn how to build an Event Module in [this guide](!resources!/infrastructure-modules/event/create).
diff --git a/www/apps/book/app/learn/fundamentals/module-links/page.mdx b/www/apps/book/app/learn/fundamentals/module-links/page.mdx
index e05d1d5e51..5a30a2902f 100644
--- a/www/apps/book/app/learn/fundamentals/module-links/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/module-links/page.mdx
@@ -10,7 +10,7 @@ In this chapter, you’ll learn what a module link is and how to define one.
Medusa's modular architecture isolates modules from one another to ensure they can be integrated into your application without side effects. Module isolation has other benefits, which you can learn about in the [Module Isolation chapter](../modules/isolation/page.mdx). Since modules are isolated, you can't access another module's data models to add a relation to it or extend it. Instead, you use a module link.
-A module link forms an association between two data models of different modules while maintaining module isolation. Using module links, you can build virtual relations between your custom data models and data models in the commerce modules, which is useful as you extend the features provided by the commerce modules. Then, Medusa creates a link table in the database to store the IDs of the linked records. You'll learn more about link tables later in this chapter.
+A module link forms an association between two data models of different modules while maintaining module isolation. Using module links, you can build virtual relations between your custom data models and data models in the Commerce Modules, which is useful as you extend the features provided by the Commerce Modules. Then, Medusa creates a link table in the database to store the IDs of the linked records. You'll learn more about link tables later in this chapter.
For example, the [Brand Customizations Tutorial](../../customization/extend-features/page.mdx) shows how to create a Brand Module that adds the concept of brands to your application, then link those brands to a product.
diff --git a/www/apps/book/app/learn/fundamentals/module-links/query/page.mdx b/www/apps/book/app/learn/fundamentals/module-links/query/page.mdx
index e6012a420d..2f382aebe1 100644
--- a/www/apps/book/app/learn/fundamentals/module-links/query/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/module-links/query/page.mdx
@@ -12,7 +12,7 @@ In this chapter, you’ll learn about Query and how to use it to fetch data from
Query fetches data across modules. It’s a set of methods registered in the Medusa container under the `query` key.
-In all resources that can access the [Medusa Container](../../medusa-container/page.mdx), such as API routes or workflows, you can resolve Query to fetch data across custom modules and Medusa’s commerce modules.
+In all resources that can access the [Medusa Container](../../medusa-container/page.mdx), such as API routes or workflows, you can resolve Query to fetch data across custom modules and Medusa’s Commerce Modules.
---
diff --git a/www/apps/book/app/learn/fundamentals/modules/commerce-modules/page.mdx b/www/apps/book/app/learn/fundamentals/modules/commerce-modules/page.mdx
index 1862f1514f..cdb49360af 100644
--- a/www/apps/book/app/learn/fundamentals/modules/commerce-modules/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/modules/commerce-modules/page.mdx
@@ -4,13 +4,13 @@ export const metadata = {
# {metadata.title}
-In this chapter, you'll learn about Medusa's commerce modules.
+In this chapter, you'll learn about Medusa's Commerce Modules.
## What is a Commerce Module?
-Commerce modules are built-in [modules](../page.mdx) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more.
+Commerce Modules are built-in [modules](../page.mdx) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more.
-Medusa's commerce modules are used to form Medusa's default [workflows](!resources!/medusa-workflows-reference) and [APIs](!api!/store). For example, when you call the add to cart endpoint. the add to cart workflow runs which uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart.
+Medusa's Commerce Modules are used to form Medusa's default [workflows](!resources!/medusa-workflows-reference) and [APIs](!api!/store). For example, when you call the add to cart endpoint. the add to cart workflow runs which uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart.
@@ -22,13 +22,13 @@ The core commerce logic contained in Commerce Modules is also available directly
### List of Medusa's Commerce Modules
-Refer to [this reference](!resources!/commerce-modules) for a full list of commerce modules in Medusa.
+Refer to [this reference](!resources!/commerce-modules) for a full list of Commerce Modules in Medusa.
---
## Use Commerce Modules in Custom Flows
-Similar to your [custom modules](../page.mdx), the Medusa application registers a commerce module's service in the [container](../../medusa-container/page.mdx). So, you can resolve it in your custom flows. This is useful as you build unique requirements extending core commerce features.
+Similar to your [custom modules](../page.mdx), the Medusa application registers a Commerce Module's service in the [container](../../medusa-container/page.mdx). So, you can resolve it in your custom flows. This is useful as you build unique requirements extending core commerce features.
For example, consider you have a [workflow](../../../fundamentals/workflows/page.mdx) (a special function that performs a task in a series of steps with rollback mechanism) that needs a step to retrieve the total number of products. You can create a step in the workflow that resolves the Product Module's service from the container to use its methods:
@@ -52,4 +52,4 @@ export const countProductsStep = createStep(
)
```
-Your workflow can use services of both custom and commerce modules, supporting you in building custom flows without having to re-build core commerce features.
+Your workflow can use services of both custom and Commerce Modules, supporting you in building custom flows without having to re-build core commerce features.
diff --git a/www/apps/book/app/learn/fundamentals/modules/architectural-modules/page.mdx b/www/apps/book/app/learn/fundamentals/modules/infrastructure-modules/page.mdx
similarity index 52%
rename from www/apps/book/app/learn/fundamentals/modules/architectural-modules/page.mdx
rename to www/apps/book/app/learn/fundamentals/modules/infrastructure-modules/page.mdx
index 84ac04ca21..1e96976816 100644
--- a/www/apps/book/app/learn/fundamentals/modules/architectural-modules/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/modules/infrastructure-modules/page.mdx
@@ -1,33 +1,34 @@
export const metadata = {
- title: `${pageNumber} Architectural Modules`,
+ title: `${pageNumber} Infrastructure Modules`,
}
# {metadata.title}
-In this chapter, you’ll learn about architectural modules.
+In this chapter, you’ll learn about Infrastructure Modules.
-## What is an Architectural Module?
+## What is an Infrastructure Module?
-An architectural module implements features and mechanisms related to the Medusa application’s architecture and infrastructure.
+An Infrastructure Module implements features and mechanisms related to the Medusa application’s architecture and infrastructure.
Since modules are interchangeable, you have more control over Medusa’s architecture. For example, you can choose to use Memcached for event handling instead of Redis.
---
-## Architectural Module Types
+## Infrastructure Module Types
-There are different architectural module types including:
+There are different Infrastructure Module types including:
-
+
- Cache Module: Defines the caching mechanism or logic to cache computational results.
- Event Module: Integrates a pub/sub service to handle subscribing to and emitting events.
- Workflow Engine Module: Integrates a service to store and track workflow executions and steps.
- File Module: Integrates a storage service to handle uploading and managing files.
- Notification Module: Integrates a third-party service or defines custom logic to send notifications to users and customers.
+- Locking Module: Integrates a service that manages access to shared resources by multiple processes or threads.
---
-## Architectural Modules List
+## Infrastructure Modules List
-Refer to the [Architectural Modules reference](!resources!/architectural-modules) for a list of Medusa’s architectural modules, available modules to install, and how to create an architectural module.
+Refer to the [Infrastructure Modules reference](!resources!/infrastructure-modules) for a list of Medusa’s Infrastructure Modules, available modules to install, and how to create an Infrastructure Module.
diff --git a/www/apps/book/app/learn/fundamentals/plugins/create/page.mdx b/www/apps/book/app/learn/fundamentals/plugins/create/page.mdx
index b1e31853f5..faf9af4214 100644
--- a/www/apps/book/app/learn/fundamentals/plugins/create/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/plugins/create/page.mdx
@@ -502,9 +502,9 @@ import BlogType from "@myorg/plugin-name/types/blog"
### Create Module Providers
-The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or architectural module.
+The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or Infrastructure Module.
-For example, assuming your plugin has a [Notification Module Provider](!resources!/architectural-modules/notification) called `my-notification`, you can register it in your Medusa application's configuration like this:
+For example, assuming your plugin has a [Notification Module Provider](!resources!/infrastructure-modules/notification) called `my-notification`, you can register it in your Medusa application's configuration like this:
diff --git a/www/apps/book/app/learn/fundamentals/workflows/store-executions/page.mdx b/www/apps/book/app/learn/fundamentals/workflows/store-executions/page.mdx
index 9a214f9c79..b7fc743cc4 100644
--- a/www/apps/book/app/learn/fundamentals/workflows/store-executions/page.mdx
+++ b/www/apps/book/app/learn/fundamentals/workflows/store-executions/page.mdx
@@ -28,7 +28,7 @@ You can view stored workflow executions from the Medusa Admin dashboard by going
items={[
{
text: "Redis Workflow Engine must be installed and configured.",
- link: "!resources!/architectural-modules/workflow-engine/redis"
+ link: "!resources!/infrastructure-modules/workflow-engine/redis"
}
]}
/>
diff --git a/www/apps/book/app/learn/introduction/architecture/page.mdx b/www/apps/book/app/learn/introduction/architecture/page.mdx
index 1196eeb58e..53a475c068 100644
--- a/www/apps/book/app/learn/introduction/architecture/page.mdx
+++ b/www/apps/book/app/learn/introduction/architecture/page.mdx
@@ -49,7 +49,7 @@ Modules can be implemented within [plugins](../../fundamentals/plugins/page.mdx)
## Third-Party Integrations Layer
-Third-party services and systems are integrated through Medusa's Commerce and Architectural modules. You also create custom third-party integrations through a [custom module](../../fundamentals/modules/page.mdx).
+Third-party services and systems are integrated through Medusa's Commerce and Infrastructure Modules. You also create custom third-party integrations through a [custom module](../../fundamentals/modules/page.mdx).
@@ -59,28 +59,28 @@ Modules can be implemented within [plugins](../../fundamentals/plugins/page.mdx)
### Commerce Modules
-[Commerce modules](!resources!/commerce-modules) integrate third-party services relevant for commerce or user-facing features. For example, you can integrate [Stripe](!resources!/commerce-modules/payment/payment-provider/stripe) through a Payment Module Provider, or [ShipStation](!resources!/integrations/guides/shipstation) through a Fulfillment Module Provider.
+[Commerce Modules](!resources!/commerce-modules) integrate third-party services relevant for commerce or user-facing features. For example, you can integrate [Stripe](!resources!/commerce-modules/payment/payment-provider/stripe) through a Payment Module Provider, or [ShipStation](!resources!/integrations/guides/shipstation) through a Fulfillment Module Provider.
You can also integrate third-party services for custom functionalities. For example, you can integrate [Sanity](!resources!/integrations/guides/sanity) for rich CMS capabilities, or [Odoo](!resources!/recipes/erp/odoo) to sync your Medusa application with your ERP system.
You can replace any of the third-party services mentioned above to build your preferred commerce ecosystem.
-
+
-### Architectural Modules
+### Infrastructure Modules
-[Architectural modules](!resources!/architectural-modules) integrate third-party services and systems for architectural features. Medusa has the following Architectural modules:
+[Infrastructure Modules](!resources!/infrastructure-modules) integrate third-party services and systems that customize Medusa's infrastructure. Medusa has the following Infrastructure Modules:
-- [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.
+- [Cache Module](!resources!/infrastructure-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!/infrastructure-modules/cache/redis).
+- [Event Module](!resources!/infrastructure-modules/event): A pub/sub system that allows you to subscribe to events and trigger them. You can integrate [Redis](!resources!/infrastructure-modules/event/redis) as the pub/sub system.
+- [File Module](!resources!/infrastructure-modules/file): Manages file uploads and storage, such as upload of product images. You can integrate [AWS S3](!resources!/infrastructure-modules/file/s3) for file storage.
+- [Locking Module](!resources!/infrastructure-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!/infrastructure-modules/locking/redis) for locking.
+- [Notification Module](!resources!/infrastructure-modules/notification): Sends notifications to customers and users, such as for order updates or newsletters. You can integrate [SendGrid](!resources!/infrastructure-modules/notification/sendgrid) for sending emails.
+- [Workflow Engine Module](!resources!/infrastructure-modules/workflow-engine): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](!resources!/infrastructure-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.
-
+
---
diff --git a/www/apps/book/app/learn/page.mdx b/www/apps/book/app/learn/page.mdx
index 1f9ce3744f..77c6a557e9 100644
--- a/www/apps/book/app/learn/page.mdx
+++ b/www/apps/book/app/learn/page.mdx
@@ -12,7 +12,7 @@ Medusa is a digital commerce platform with a built-in Framework for customizatio
Medusa ships with three main tools:
-1. A suite of [commerce modules](!resources!/commerce-modules) with core commerce functionalities, such as tracking inventory, calculating cart totals, accepting payments, managing orders, and much more.
+1. A suite of [Commerce Modules](!resources!/commerce-modules) with core commerce functionalities, such as tracking inventory, calculating cart totals, accepting payments, managing orders, and much more.
2. A [Framework](./fundamentals/framework/page.mdx) for building custom functionalities specific to your business, product, or industry. This includes tools for introducing custom API endpoints, business logic, and data models; building workflows and automations; and integrating with third-party services.
3. A customizable admin dashboard for merchants to configure and operate their store.
@@ -100,7 +100,7 @@ This documentation is split into the following sections:
Product
- Documentation for the [Framework](./fundamentals/framework/page.mdx), [Commerce Modules](!resources!/commerce-modules), and [Architectural Modules](!resources!/architectural-modules). These sections include concepts, guides, and references for each of those products.
+ Documentation for the [Framework](./fundamentals/framework/page.mdx), [Commerce Modules](!resources!/commerce-modules), and [Infrastructure Modules](!resources!/infrastructure-modules). These sections include concepts, guides, and references for each of those products.
diff --git a/www/apps/book/components/Homepage/ModulesSection/index.tsx b/www/apps/book/components/Homepage/ModulesSection/index.tsx
index f508e31771..16b2a1c370 100644
--- a/www/apps/book/components/Homepage/ModulesSection/index.tsx
+++ b/www/apps/book/components/Homepage/ModulesSection/index.tsx
@@ -156,7 +156,7 @@ const HomepageModulesSection = () => {
All commerce features are provided as extendable modules in Medusa.
- Click on any of the commerce modules below to learn more about their
+ Click on any of the Commerce Modules below to learn more about their
commerce features, and how to extend and use them for your custom
use-case.
diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs
index 06b73a29fa..dd6827a3f1 100644
--- a/www/apps/book/generated/edit-dates.mjs
+++ b/www/apps/book/generated/edit-dates.mjs
@@ -2,8 +2,8 @@ export const generatedEditDates = {
"app/learn/fundamentals/scheduled-jobs/page.mdx": "2024-12-09T10:51:40.570Z",
"app/learn/fundamentals/workflows/page.mdx": "2024-12-09T14:45:17.837Z",
"app/learn/deployment/page.mdx": "2025-03-11T14:53:25.540Z",
- "app/learn/page.mdx": "2025-04-17T07:45:47.162Z",
- "app/learn/fundamentals/modules/commerce-modules/page.mdx": "2024-12-09T10:46:29.339Z",
+ "app/learn/page.mdx": "2025-04-17T08:50:17.036Z",
+ "app/learn/fundamentals/modules/commerce-modules/page.mdx": "2025-04-17T08:51:32.723Z",
"app/learn/fundamentals/workflows/retry-failed-steps/page.mdx": "2025-03-28T07:15:19.388Z",
"app/learn/fundamentals/workflows/workflow-hooks/page.mdx": "2024-12-09T10:44:33.781Z",
"app/learn/debugging-and-testing/logging/page.mdx": "2024-09-30T08:43:53.135Z",
@@ -17,7 +17,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/api-routes/page.mdx": "2024-12-04T11:02:57.134Z",
"app/learn/fundamentals/modules/modules-directory-structure/page.mdx": "2024-12-09T10:32:46.839Z",
"app/learn/fundamentals/workflows/access-workflow-errors/page.mdx": "2024-10-21T13:30:21.371Z",
- "app/learn/fundamentals/events-and-subscribers/page.mdx": "2024-12-09T10:48:09.285Z",
+ "app/learn/fundamentals/events-and-subscribers/page.mdx": "2025-04-17T08:29:09.896Z",
"app/learn/fundamentals/modules/container/page.mdx": "2025-03-18T15:10:03.574Z",
"app/learn/fundamentals/workflows/execute-another-workflow/page.mdx": "2024-12-09T15:56:22.895Z",
"app/learn/fundamentals/modules/loaders/page.mdx": "2025-03-24T06:40:39.948Z",
@@ -67,13 +67,13 @@ export const generatedEditDates = {
"app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx": "2025-03-24T06:54:21.249Z",
"app/learn/fundamentals/module-links/custom-columns/page.mdx": "2025-03-11T13:29:54.752Z",
"app/learn/fundamentals/module-links/directions/page.mdx": "2025-03-17T12:52:06.161Z",
- "app/learn/fundamentals/module-links/page.mdx": "2025-03-11T13:39:14.345Z",
- "app/learn/fundamentals/module-links/query/page.mdx": "2025-03-24T06:42:32.337Z",
+ "app/learn/fundamentals/module-links/page.mdx": "2025-04-17T08:50:17.036Z",
+ "app/learn/fundamentals/module-links/query/page.mdx": "2025-04-17T08:50:17.036Z",
"app/learn/fundamentals/modules/db-operations/page.mdx": "2025-03-21T09:21:46.901Z",
"app/learn/fundamentals/modules/multiple-services/page.mdx": "2025-03-18T15:11:44.632Z",
"app/learn/fundamentals/modules/page.mdx": "2025-03-18T07:51:09.049Z",
"app/learn/debugging-and-testing/instrumentation/page.mdx": "2025-02-24T08:12:53.132Z",
- "app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-01-27T08:45:19.025Z",
+ "app/learn/fundamentals/api-routes/additional-data/page.mdx": "2025-04-17T08:50:17.036Z",
"app/learn/fundamentals/workflows/variable-manipulation/page.mdx": "2025-01-27T08:45:19.029Z",
"app/learn/customization/custom-features/api-route/page.mdx": "2024-12-09T10:39:30.046Z",
"app/learn/customization/custom-features/module/page.mdx": "2025-03-18T07:49:30.590Z",
@@ -83,38 +83,38 @@ export const generatedEditDates = {
"app/learn/customization/customize-admin/page.mdx": "2024-12-09T11:02:38.801Z",
"app/learn/customization/customize-admin/route/page.mdx": "2025-02-11T15:56:03.835Z",
"app/learn/customization/customize-admin/widget/page.mdx": "2025-02-05T09:10:18.163Z",
- "app/learn/customization/extend-features/define-link/page.mdx": "2024-12-09T11:02:39.346Z",
+ "app/learn/customization/extend-features/define-link/page.mdx": "2025-04-17T08:50:17.036Z",
"app/learn/customization/extend-features/page.mdx": "2024-12-09T11:02:39.244Z",
"app/learn/customization/extend-features/query-linked-records/page.mdx": "2025-03-25T13:48:49.973Z",
"app/learn/customization/integrate-systems/handle-event/page.mdx": "2024-12-24T15:09:24.653Z",
"app/learn/customization/integrate-systems/page.mdx": "2024-12-09T10:40:08.528Z",
"app/learn/customization/integrate-systems/schedule-task/page.mdx": "2025-01-28T16:42:42.071Z",
"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-12T14:39:09.724Z",
+ "app/learn/customization/next-steps/page.mdx": "2025-04-17T08:50:17.036Z",
+ "app/learn/fundamentals/modules/infrastructure-modules/page.mdx": "2025-04-17T08:29:09.895Z",
+ "app/learn/introduction/architecture/page.mdx": "2025-04-17T08:51:32.723Z",
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2025-03-18T07:41:01.936Z",
"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",
"app/learn/build/page.mdx": "2024-12-09T11:05:17.383Z",
- "app/learn/deployment/general/page.mdx": "2025-03-25T13:32:56.287Z",
+ "app/learn/deployment/general/page.mdx": "2025-04-17T08:29:09.878Z",
"app/learn/fundamentals/workflows/multiple-step-usage/page.mdx": "2024-11-25T16:19:32.169Z",
"app/learn/installation/page.mdx": "2025-03-11T08:55:12.967Z",
"app/learn/fundamentals/data-models/check-constraints/page.mdx": "2024-12-06T14:34:50.384Z",
"app/learn/fundamentals/module-links/link/page.mdx": "2025-04-07T08:03:14.513Z",
- "app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-02-11T15:56:03.835Z",
- "app/learn/fundamentals/plugins/create/page.mdx": "2025-04-15T06:41:10.746Z",
+ "app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-04-17T08:29:10.166Z",
+ "app/learn/fundamentals/plugins/create/page.mdx": "2025-04-17T08:29:09.910Z",
"app/learn/fundamentals/plugins/page.mdx": "2025-01-22T10:14:10.433Z",
"app/learn/customization/reuse-customizations/page.mdx": "2025-01-22T10:01:57.665Z",
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z",
"app/learn/fundamentals/module-links/query-context/page.mdx": "2025-02-12T16:59:20.963Z",
"app/learn/fundamentals/admin/environment-variables/page.mdx": "2025-03-25T13:32:19.713Z",
- "app/learn/fundamentals/api-routes/parse-body/page.mdx": "2025-02-14T08:32:25.596Z",
+ "app/learn/fundamentals/api-routes/parse-body/page.mdx": "2025-04-17T08:29:10.145Z",
"app/learn/fundamentals/admin/routing/page.mdx": "2025-02-24T09:50:37.495Z",
"app/learn/resources/contribution-guidelines/admin-translations/page.mdx": "2025-02-11T16:57:46.726Z",
"app/learn/resources/contribution-guidelines/docs/page.mdx": "2025-03-06T09:32:26.010Z",
"app/learn/resources/usage/page.mdx": "2025-02-26T13:35:34.824Z",
- "app/learn/configurations/medusa-config/page.mdx": "2025-03-11T14:27:04.528Z",
+ "app/learn/configurations/medusa-config/page.mdx": "2025-04-17T08:29:09.907Z",
"app/learn/configurations/ts-aliases/page.mdx": "2025-02-11T16:57:46.683Z",
"app/learn/production/worker-mode/page.mdx": "2025-03-11T15:21:50.906Z",
"app/learn/fundamentals/module-links/read-only/page.mdx": "2025-03-21T09:14:54.944Z",
diff --git a/www/apps/book/generated/sidebar.mjs b/www/apps/book/generated/sidebar.mjs
index fe6d7ba254..9616c95d5b 100644
--- a/www/apps/book/generated/sidebar.mjs
+++ b/www/apps/book/generated/sidebar.mjs
@@ -364,10 +364,10 @@ export const generatedSidebars = [
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/learn/fundamentals/modules/architectural-modules",
- "title": "Architectural Modules",
+ "path": "/learn/fundamentals/modules/infrastructure-modules",
+ "title": "Infrastructure Modules",
"children": [],
- "chapterTitle": "3.3.11. Architectural Modules",
+ "chapterTitle": "3.3.11. Infrastructure Modules",
"number": "3.3.11."
}
],
diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt
index 9a6f826351..fea33efdc0 100644
--- a/www/apps/book/public/llms-full.txt
+++ b/www/apps/book/public/llms-full.txt
@@ -6,7 +6,7 @@ Medusa is a digital commerce platform with a built-in Framework for customizatio
Medusa ships with three main tools:
-1. A suite of [commerce modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md) with core commerce functionalities, such as tracking inventory, calculating cart totals, accepting payments, managing orders, and much more.
+1. A suite of [Commerce Modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md) with core commerce functionalities, such as tracking inventory, calculating cart totals, accepting payments, managing orders, and much more.
2. A [Framework](https://docs.medusajs.com/learn/fundamentals/framework/index.html.md) for building custom functionalities specific to your business, product, or industry. This includes tools for introducing custom API endpoints, business logic, and data models; building workflows and automations; and integrating with third-party services.
3. A customizable admin dashboard for merchants to configure and operate their store.
@@ -330,6 +330,28 @@ Refer to [this documentation](https://docs.medusajs.com/learn/update/index.html.
In the next chapters, you'll learn about the architecture of your Medusa application, then learn how to customize your application to build custom features.
+# Storefront Development
+
+The Medusa application is made up of a Node.js server and an admin dashboard. Storefronts are installed, built, and hosted separately from the Medusa application, giving you the flexibility to choose the frontend tech stack that you and your team are proficient in, and implement unique design systems and user experience.
+
+You can build your storefront from scratch with your preferred tech stack, or start with our Next.js Starter storefront. The Next.js Starter storefront provides rich commerce features and a sleek design. Developers and businesses can use it as-is or build on top of it to tailor it for the business's unique use case, design, and customer experience.
+
+- [Install Next.js Starter Storefront](https://docs.medusajs.com/resources/nextjs-starter/index.html.md)
+- [Build Custom Storefront](https://docs.medusajs.com/resources/storefront-development/index.html.md)
+
+***
+
+## Passing a Publishable API Key in Storefront Requests
+
+When sending a request to an API route starting with `/store`, you must include a publishable API key in the header of your request.
+
+A publishable API key sets the scope of your request to one or more sales channels.
+
+Then, when you retrieve products, only products of those sales channels are retrieved. This also ensures you retrieve correct inventory data, and associate created orders with the scoped sales channel.
+
+Learn more about passing the publishable API key in [this storefront development guide](https://docs.medusajs.com/resources/storefront-development/publishable-api-keys/index.html.md).
+
+
# Updating Medusa
In this chapter, you'll learn about updating your Medusa application and packages.
@@ -436,28 +458,6 @@ npm install
```
-# Storefront Development
-
-The Medusa application is made up of a Node.js server and an admin dashboard. Storefronts are installed, built, and hosted separately from the Medusa application, giving you the flexibility to choose the frontend tech stack that you and your team are proficient in, and implement unique design systems and user experience.
-
-You can build your storefront from scratch with your preferred tech stack, or start with our Next.js Starter storefront. The Next.js Starter storefront provides rich commerce features and a sleek design. Developers and businesses can use it as-is or build on top of it to tailor it for the business's unique use case, design, and customer experience.
-
-- [Install Next.js Starter Storefront](https://docs.medusajs.com/resources/nextjs-starter/index.html.md)
-- [Build Custom Storefront](https://docs.medusajs.com/resources/storefront-development/index.html.md)
-
-***
-
-## Passing a Publishable API Key in Storefront Requests
-
-When sending a request to an API route starting with `/store`, you must include a publishable API key in the header of your request.
-
-A publishable API key sets the scope of your request to one or more sales channels.
-
-Then, when you retrieve products, only products of those sales channels are retrieved. This also ensures you retrieve correct inventory data, and associate created orders with the scoped sales channel.
-
-Learn more about passing the publishable API key in [this storefront development guide](https://docs.medusajs.com/resources/storefront-development/publishable-api-keys/index.html.md).
-
-
# Medusa Application Configuration
In this chapter, you'll learn available configurations in the Medusa application. You can change the application's configurations to customize the behavior of the application, its integrated modules and plugins, and more.
@@ -983,7 +983,7 @@ The `projectConfig.redisPrefix` configuration defines a prefix on all keys store
The value of this configuration is prepended to `sess:`. For example, if you set it to `medusa:`, then a key stored in Redis is prefixed by `medusa:sess`.
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/redis/index.html.md).
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html.md).
#### Example
@@ -1001,7 +1001,7 @@ module.exports = defineConfig({
The `projectConfig.redisUrl` configuration specifies the connection URL to Redis to store the Medusa server session. When specified, the Medusa server uses Redis to store the session data. Otherwie, the session data is stored in-memory.
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/redis/index.html.md). You'll have to configure the Redis connection for those modules separately.
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html.md). You'll have to configure the Redis connection for those modules separately.
You must first have Redis installed. You can refer to [Redis's installation guide](https://redis.io/docs/getting-started/installation/).
@@ -1038,7 +1038,7 @@ module.exports = defineConfig({
The `projectConfig.sessionOptions` configuration defines additional options to pass to [express-session](https://www.npmjs.com/package/express-session), which is used to store the Medusa server session.
-This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/redis/index.html.md).
+This configuration is not used for modules that also connect to Redis, such as the [Redis Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html.md).
#### Example
@@ -1195,7 +1195,7 @@ module.exports = defineConfig({
## Module Configurations (`modules`)
-The `modules` configuration allows you to register and configure the [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) registered in the Medusa application. Medusa's commerce and architectural modules are configured by default. So, you only need to pass your custom modules, or override the default configurations of the existing modules.
+The `modules` configuration allows you to register and configure the [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) registered in the Medusa application. Medusa's commerce and Infrastructure Modules are configured by default. So, you only need to pass your custom modules, or override the default configurations of the existing modules.
`modules` is an array of objects for the modules to register. Each object has the following properties:
@@ -1405,6 +1405,410 @@ import { BrandModuleService } from "@/modules/brand/service"
```
+# Medusa Testing Tools
+
+In this chapter, you'll learn about Medusa's testing tools and how to install and configure them.
+
+## @medusajs/test-utils Package
+
+Medusa provides a Testing Framework to create integration tests for your custom API routes, modules, or other Medusa customizations.
+
+To use the Testing Framework, install `@medusajs/test-utils` as a `devDependency`:
+
+```bash npm2yarn
+npm install --save-dev @medusajs/test-utils@latest
+```
+
+***
+
+## Install and Configure Jest
+
+Writing tests with `@medusajs/test-utils`'s tools requires installing and configuring Jest in your project.
+
+Run the following command to install the required Jest dependencies:
+
+```bash npm2yarn
+npm install --save-dev jest @types/jest @swc/jest
+```
+
+Then, create the file `jest.config.js` with the following content:
+
+```js title="jest.config.js"
+const { loadEnv } = require("@medusajs/framework/utils")
+loadEnv("test", process.cwd())
+
+module.exports = {
+ transform: {
+ "^.+\\.[jt]s$": [
+ "@swc/jest",
+ {
+ jsc: {
+ parser: { syntax: "typescript", decorators: true },
+ },
+ },
+ ],
+ },
+ testEnvironment: "node",
+ moduleFileExtensions: ["js", "ts", "json"],
+ modulePathIgnorePatterns: ["dist/"],
+ setupFiles: ["./integration-tests/setup.js"],
+}
+
+if (process.env.TEST_TYPE === "integration:http") {
+ module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"]
+} else if (process.env.TEST_TYPE === "integration:modules") {
+ module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"]
+} else if (process.env.TEST_TYPE === "unit") {
+ module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"]
+}
+```
+
+Next, create the `integration-tests/setup.js` file with the following content:
+
+```js title="integration-tests/setup.js"
+const { MetadataStorage } = require("@mikro-orm/core")
+
+MetadataStorage.clear()
+```
+
+***
+
+## Add Test Commands
+
+Finally, add the following scripts to `package.json`:
+
+```json title="package.json"
+"scripts": {
+ // ...
+ "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
+ "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
+ "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
+},
+```
+
+You now have two commands:
+
+- `test:integration:http` to run integration tests (for example, for API routes and workflows) available under the `integration-tests/http` directory.
+- `test:integration:modules` to run integration tests for modules available in any `__tests__` directory under `src/modules`.
+- `test:unit` to run unit tests in any `__tests__` directory under the `src` directory.
+
+Medusa's Testing Framework works for integration tests only. You can write unit tests using Jest.
+
+***
+
+## Test Tools and Writing Tests
+
+The next chapters explain how to use the testing tools provided by `@medusajs/test-utils` to write tests.
+
+
+# General Medusa Application Deployment Guide
+
+In this document, you'll learn the general steps to deploy your Medusa application. How you apply these steps depend on your chosen hosting provider or platform.
+
+Find how-to guides for specific platforms in [this documentation](https://docs.medusajs.com/resources/deployment/index.html.md).
+
+Want Medusa to manage and maintain your infrastructure? [Sign up and learn more about Medusa Cloud](https://medusajs.com/pricing)
+
+Medusa Cloud is our managed services offering that makes deploying and operating Medusa applications possible without having to worry about configuring, scaling, and maintaining infrastructure. Medusa Cloud hosts your server, Admin dashboard, database, and Redis instance.
+
+With Medusa Cloud, you maintain full customization control as you deploy your own modules and customizations directly from GitHub:
+
+- Push to deploy.
+- Multiple testing environments.
+- Preview environments for new PRs.
+- Test on production-like data.
+
+### Prerequisites
+
+- [Medusa application’s codebase hosted on GitHub repository.](https://docs.medusajs.com/learn/index.html.md)
+
+## What You'll Deploy
+
+When you deploy the Medusa application, you need to deploy the following resources:
+
+1. PostgreSQL database: This is the database that will hold your Medusa application's details.
+2. Redis database: This is the database that will store the Medusa server's session.
+3. Medusa application in [server and worker mode](https://docs.medusajs.com/learn/production/worker-mode/index.html.md), where:
+ - The server mode handles incoming API requests and serving the Medusa Admin dashboard.
+ - The worker mode handles background tasks, such as scheduled jobs and subscribers.
+
+So, when choosing a hosting provider, make sure it supports deploying these resources. Also, for optimal experience, the hosting provider and plan must offer at least 2GB of RAM.
+
+***
+
+## 1. Configure Medusa Application
+
+### Worker Mode
+
+The `workerMode` configuration determines which mode the Medusa application runs in. When you deploy the Medusa application, you deploy two instances: one in server mode, and one in worker mode.
+
+Learn more about worker mode in the [Worker Module chapter](https://docs.medusajs.com/learn/production/worker-mode/index.html.md).
+
+So, add the following configuration in `medusa-config.ts`:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ projectConfig: {
+ // ...
+ workerMode: process.env.MEDUSA_WORKER_MODE as "shared" | "worker" | "server",
+ },
+})
+```
+
+Later, you’ll set different values of the `MEDUSA_WORKER_MODE` environment variable for each Medusa application deployment or instance.
+
+### Configure Medusa Admin
+
+The Medusa Admin is served by the Medusa server application. So, you need to disable it in the worker Medusa application only.
+
+To disable the Medusa Admin in the worker Medusa application while keeping it enabled in the server Medusa application, add the following configuration in `medusa-config.ts`:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ // ...
+ admin: {
+ disable: process.env.DISABLE_MEDUSA_ADMIN === "true",
+ },
+})
+```
+
+Later, you’ll set different values of the `DISABLE_MEDUSA_ADMIN` environment variable for each Medusa application instance.
+
+### Configure Redis URL
+
+The `redisUrl` configuration specifies the connection URL to Redis to store the Medusa server's session.
+
+Learn more in the [Medusa Configuration documentation](https://docs.medusajs.com/learn/configurations/medusa-config#redisurl/index.html.md).
+
+So, add the following configuration in `medusa-config.ts` :
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ projectConfig: {
+ // ...
+ redisUrl: process.env.REDIS_URL,
+ },
+})
+```
+
+***
+
+## 2. Add predeploy Script
+
+Before you start the Medusa application in production, you should always run migrations and sync links.
+
+So, add the following script in `package.json`:
+
+```json
+"scripts": {
+ // ...
+ "predeploy": "medusa db:migrate"
+},
+```
+
+***
+
+## 3. Install Production Modules and Providers
+
+By default, your Medusa application uses modules and providers useful for development, such as the In-Memory Cache Module or the Local File Module Provider.
+
+It’s highly recommended to instead use modules and providers suitable for production, including:
+
+- [Redis Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html.md)
+- [Redis Event Bus Module](https://docs.medusajs.com/resources/infrastructure-modules/event/redis/index.html.md)
+- [Workflow Engine Redis Module](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/redis/index.html.md)
+- [S3 File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file/s3/index.html.md) (or other file module providers production-ready).
+- [SendGrid Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification/sendgrid/index.html.md) (or other notification module providers production-ready).
+
+Then, add these modules in `medusa-config.ts`:
+
+```ts title="medusa-config.ts"
+import { Modules } from "@medusajs/framework/utils"
+
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/medusa/cache-redis",
+ options: {
+ redisUrl: process.env.REDIS_URL,
+ },
+ },
+ {
+ resolve: "@medusajs/medusa/event-bus-redis",
+ options: {
+ redisUrl: process.env.REDIS_URL,
+ },
+ },
+ {
+ resolve: "@medusajs/medusa/workflow-engine-redis",
+ options: {
+ redis: {
+ url: process.env.REDIS_URL,
+ },
+ },
+ },
+ ],
+})
+```
+
+Check out the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) and [Infrastructure Modules](https://docs.medusajs.com/resources/infrastructure-modules/index.html.md) documentation for other modules and providers to use.
+
+***
+
+## 4. Create PostgreSQL and Redis Databases
+
+Your Medusa application must connect to PostgreSQL and Redis databases. So, before you deploy it, create production PostgreSQL and Redis databases.
+
+If your hosting provider doesn't support databases, you can use [Neon for PostgreSQL database hosting](https://neon.tech/), and [Redis Cloud for the Redis database hosting](https://redis.io/cloud/).
+
+After hosting both databases, keep their connection URLs for the next steps.
+
+***
+
+## 5. Deploy Medusa Application in Server Mode
+
+As mentioned earlier, you'll deploy two instances or create two deployments of your Medusa application: one in server mode, and the other in worker mode.
+
+The deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment.
+
+### Set Environment Variables
+
+When setting the environment variables of the Medusa application, set the following variables:
+
+```bash
+COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET
+JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET
+STORE_CORS= # STOREFRONT URL
+ADMIN_CORS= # ADMIN URL
+AUTH_CORS= # STOREFRONT AND ADMIN URLS, SEPARATED BY COMMAS
+DISABLE_MEDUSA_ADMIN=false
+MEDUSA_WORKER_MODE=server
+PORT=9000
+DATABASE_URL # POSTGRES DATABASE URL
+REDIS_URL= # REDIS DATABASE URL
+```
+
+Where:
+
+- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret.
+- `STORE_CORS`'s value is the URL of your storefront. If you don’t have it yet, you can skip adding it for now.
+- `ADMIN_CORS`'s value is the URL of the admin dashboard, which is the same as the server Medusa application. You can add it later if you don't currently have it.
+- `AUTH_CORS`'s value is the URLs of any application authenticating users, customers, or other actor types, such as the storefront and admin URLs. The URLs are separated by commas. If you don’t have the URLs yet, you can set its value later.
+- Set `DISABLE_MEDUSA_ADMIN`'s value to `false` so that the admin is built with the server application.
+- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
+- Set the Redis database's connection URL as the value of `REDIS_URL`.
+
+Feel free to add any other relevant environment variables, such as for integrations and Infrastructure Modules. If you're using environment variables in your admin customizations, make sure to set them as well, as they're inlined during the build process.
+
+### Set Start Command
+
+The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. So, you must install the dependencies in the `.medusa/server` directory, then run the `start` command in it.
+
+If your hosting provider doesn't support setting a current-working directory, set the start command to the following:
+
+```bash npm2yarn
+cd .medusa/server && npm install && npm run predeploy && npm run start
+```
+
+Notice that you run the `predeploy` command before starting the Medusa application to run migrations and sync links whenever there's an update.
+
+### Set Backend URL in Admin Configuration
+
+The Medusa Admin is built and hosted statically. To send requests to the Medusa server application, you must set the backend URL in the Medusa Admin's configuration.
+
+After you’ve obtained the Medusa application’s URL, add the following configuration to `medusa-config.ts`:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ // ...
+ admin: {
+ // ...
+ backendUrl: process.env.MEDUSA_BACKEND_URL,
+ },
+})
+```
+
+Then, push the changes to the GitHub repository or deployed application.
+
+In your hosting provider, add or modify the following environment variables for the Medusa application in server mode:
+
+```bash
+ADMIN_CORS= # MEDUSA APPLICATION URL
+AUTH_CORS= # ADD MEDUSA APPLICATION URL
+MEDUSA_BACKEND_URL= # URL TO DEPLOYED MEDUSA APPLICATION
+```
+
+Where you set the value of `ADMIN_CORS` and `MEDUSA_BACKEND_URL` to the Medusa application’s URL, and you add the URL to `AUTH_CORS`.
+
+After setting the environment variables, make sure to restart the deployment for the changes to take effect.
+
+Remember to separate URLs in `AUTH_CORS` by commas.
+
+***
+
+## 6. Deploy Medusa Application in Worker Mode
+
+Next, you'll deploy the Medusa application in worker mode.
+
+As explained in the previous section, the deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment.
+
+### Set Environment Variables
+
+When setting the environment variables of the Medusa application, set the following variables:
+
+```bash
+COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET
+JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET
+DISABLE_MEDUSA_ADMIN=true
+MEDUSA_WORKER_MODE=worker
+PORT=9000
+DATABASE_URL # POSTGRES DATABASE URL
+REDIS_URL= # REDIS DATABASE URL
+```
+
+Where:
+
+- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret.
+- Set `DISABLE_MEDUSA_ADMIN`'s value to `true` so that the admin isn't built with the worker application.
+- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
+- Set the Redis database's connection URL as the value of `REDIS_URL`.
+
+Feel free to add any other relevant environment variables, such as for integrations and Infrastructure Modules.
+
+### Set Start Command
+
+The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. So, you must install the dependencies in the `.medusa/server` directory, then run the `start` command in it.
+
+If your hosting provider doesn't support setting a current-working directory, set the start command to the following:
+
+```bash npm2yarn
+cd .medusa/server && npm run install && npm run start
+```
+
+***
+
+## 7. Test Deployed Application
+
+Once the application is deployed and live, go to `/health`, where `` is the URL of the Medusa application in server mode. If the deployment was successful, you’ll see the `OK` response.
+
+The Medusa Admin is also available at `/app`.
+
+***
+
+## Create Admin User
+
+If your hosting provider supports running commands in your Medusa application's directory, run the following command to create an admin user:
+
+```bash
+npx medusa user -e admin-medusa@test.com -p supersecret
+```
+
+Replace the email `admin-medusa@test.com` and password `supersecret` with the credentials you want.
+
+You can use these credentials to log into the Medusa Admin dashboard.
+
+
# Configure Instrumentation
In this chapter, you'll learn about observability in Medusa and how to configure instrumentation with OpenTelemetry.
@@ -1655,102 +2059,6 @@ The `activity` method returns the ID of the started activity. This ID can then b
If you configured the `LOG_LEVEL` environment variable to a level higher than those associated with the above methods, their messages won’t be logged.
-# Medusa Testing Tools
-
-In this chapter, you'll learn about Medusa's testing tools and how to install and configure them.
-
-## @medusajs/test-utils Package
-
-Medusa provides a Testing Framework to create integration tests for your custom API routes, modules, or other Medusa customizations.
-
-To use the Testing Framework, install `@medusajs/test-utils` as a `devDependency`:
-
-```bash npm2yarn
-npm install --save-dev @medusajs/test-utils@latest
-```
-
-***
-
-## Install and Configure Jest
-
-Writing tests with `@medusajs/test-utils`'s tools requires installing and configuring Jest in your project.
-
-Run the following command to install the required Jest dependencies:
-
-```bash npm2yarn
-npm install --save-dev jest @types/jest @swc/jest
-```
-
-Then, create the file `jest.config.js` with the following content:
-
-```js title="jest.config.js"
-const { loadEnv } = require("@medusajs/framework/utils")
-loadEnv("test", process.cwd())
-
-module.exports = {
- transform: {
- "^.+\\.[jt]s$": [
- "@swc/jest",
- {
- jsc: {
- parser: { syntax: "typescript", decorators: true },
- },
- },
- ],
- },
- testEnvironment: "node",
- moduleFileExtensions: ["js", "ts", "json"],
- modulePathIgnorePatterns: ["dist/"],
- setupFiles: ["./integration-tests/setup.js"],
-}
-
-if (process.env.TEST_TYPE === "integration:http") {
- module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"]
-} else if (process.env.TEST_TYPE === "integration:modules") {
- module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"]
-} else if (process.env.TEST_TYPE === "unit") {
- module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"]
-}
-```
-
-Next, create the `integration-tests/setup.js` file with the following content:
-
-```js title="integration-tests/setup.js"
-const { MetadataStorage } = require("@mikro-orm/core")
-
-MetadataStorage.clear()
-```
-
-***
-
-## Add Test Commands
-
-Finally, add the following scripts to `package.json`:
-
-```json title="package.json"
-"scripts": {
- // ...
- "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
- "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
- "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
-},
-```
-
-You now have two commands:
-
-- `test:integration:http` to run integration tests (for example, for API routes and workflows) available under the `integration-tests/http` directory.
-- `test:integration:modules` to run integration tests for modules available in any `__tests__` directory under `src/modules`.
-- `test:unit` to run unit tests in any `__tests__` directory under the `src` directory.
-
-Medusa's Testing Framework works for integration tests only. You can write unit tests using Jest.
-
-***
-
-## Test Tools and Writing Tests
-
-The next chapters explain how to use the testing tools provided by `@medusajs/test-utils` to write tests.
-
-
# Build Custom Features
In the upcoming chapters, you'll follow step-by-step guides to build custom features in Medusa. These guides gradually introduce Medusa's concepts to help you understand what they are and how to use them.
@@ -1776,29 +2084,6 @@ The next chapters will guide you to:
3. Expose an API route that allows admin users to create a brand using the workflow.
-# Extend Core Commerce Features
-
-In the upcoming chapters, you'll learn about the concepts and tools to extend Medusa's core commerce features.
-
-In other commerce platforms, you extend core features and models through hacky workarounds that can introduce unexpected issues and side effects across the platform. It also makes your application difficult to maintain and upgrade in the long run.
-
-The Medusa Framework and orchestration tools mitigate these issues while supporting all your customization needs:
-
-- [Module Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md): Link data models of different modules without building direct dependencies, ensuring that the Medusa application integrates your modules without side effects.
-- [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md): inject custom functionalities into a workflow at predefined points, called hooks. This allows you to perform custom actions as a part of a core workflow without hacky workarounds.
-- [Additional Data in API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md): Configure core API routes to accept request parameters relevant to your customizations. These parameters are passed to the underlying workflow's hooks, where you can manage your custom data as part of an existing flow.
-
-***
-
-## Next Chapters: Link Brands to Products Example
-
-The next chapters explain how to use the tools mentioned above with step-by-step guides. You'll continue with the [brands example from the previous chapters](https://docs.medusajs.com/learn/customization/custom-features/index.html.md) to:
-
-- Link brands from the custom [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) to products from Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md).
-- Extend the core product-creation workflow and the API route that uses it to allow setting the brand of a newly created product.
-- Retrieve a product's associated brand's details.
-
-
# Customize Medusa Admin Dashboard
In the previous chapters, you've customized your Medusa application to [add brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), [expose an API route to create brands](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), and [linked brands to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md).
@@ -1843,6 +2128,29 @@ In the previous chapters, you've [added brands](https://docs.medusajs.com/learn/
3. Sync brands from the CMS at a daily schedule.
+# Extend Core Commerce Features
+
+In the upcoming chapters, you'll learn about the concepts and tools to extend Medusa's core commerce features.
+
+In other commerce platforms, you extend core features and models through hacky workarounds that can introduce unexpected issues and side effects across the platform. It also makes your application difficult to maintain and upgrade in the long run.
+
+The Medusa Framework and orchestration tools mitigate these issues while supporting all your customization needs:
+
+- [Module Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md): Link data models of different modules without building direct dependencies, ensuring that the Medusa application integrates your modules without side effects.
+- [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md): inject custom functionalities into a workflow at predefined points, called hooks. This allows you to perform custom actions as a part of a core workflow without hacky workarounds.
+- [Additional Data in API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md): Configure core API routes to accept request parameters relevant to your customizations. These parameters are passed to the underlying workflow's hooks, where you can manage your custom data as part of an existing flow.
+
+***
+
+## Next Chapters: Link Brands to Products Example
+
+The next chapters explain how to use the tools mentioned above with step-by-step guides. You'll continue with the [brands example from the previous chapters](https://docs.medusajs.com/learn/customization/custom-features/index.html.md) to:
+
+- Link brands from the custom [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) to products from Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md).
+- Extend the core product-creation workflow and the API route that uses it to allow setting the brand of a newly created product.
+- Retrieve a product's associated brand's details.
+
+
# Customizations Next Steps: Learn the Fundamentals
The previous guides introduced Medusa's different concepts and how you can use them to customize Medusa for a realistic use case, You added brands to your application, linked them to products, customized the admin dashboard, and integrated a third-party CMS.
@@ -1853,7 +2161,7 @@ The next chapters will cover each of these concepts in depth, with the different
The following guides and references are useful for your development journey:
-3. [Commerce Modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md): Browse the list of commerce modules in Medusa and their references to learn how to use them.
+3. [Commerce Modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md): Browse the list of Commerce Modules in Medusa and their references to learn how to use them.
4. [Service Factory Reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md): Learn about the methods generated by `MedusaService` with examples.
5. [Workflows Reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md): Browse the list of core workflows and their hooks that are useful for your customizations.
6. [Admin Injection Zones](https://docs.medusajs.com/resources/admin-widget-injection-zones/index.html.md): Browse the injection zones in the Medusa Admin to learn where you can inject widgets.
@@ -1880,314 +2188,6 @@ Medusa provides the tooling to create a plugin package, test it in a local Medus
To learn more about plugins and how to create them, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
-# General Medusa Application Deployment Guide
-
-In this document, you'll learn the general steps to deploy your Medusa application. How you apply these steps depend on your chosen hosting provider or platform.
-
-Find how-to guides for specific platforms in [this documentation](https://docs.medusajs.com/resources/deployment/index.html.md).
-
-Want Medusa to manage and maintain your infrastructure? [Sign up and learn more about Medusa Cloud](https://medusajs.com/pricing)
-
-Medusa Cloud is our managed services offering that makes deploying and operating Medusa applications possible without having to worry about configuring, scaling, and maintaining infrastructure. Medusa Cloud hosts your server, Admin dashboard, database, and Redis instance.
-
-With Medusa Cloud, you maintain full customization control as you deploy your own modules and customizations directly from GitHub:
-
-- Push to deploy.
-- Multiple testing environments.
-- Preview environments for new PRs.
-- Test on production-like data.
-
-### Prerequisites
-
-- [Medusa application’s codebase hosted on GitHub repository.](https://docs.medusajs.com/learn/index.html.md)
-
-## What You'll Deploy
-
-When you deploy the Medusa application, you need to deploy the following resources:
-
-1. PostgreSQL database: This is the database that will hold your Medusa application's details.
-2. Redis database: This is the database that will store the Medusa server's session.
-3. Medusa application in [server and worker mode](https://docs.medusajs.com/learn/production/worker-mode/index.html.md), where:
- - The server mode handles incoming API requests and serving the Medusa Admin dashboard.
- - The worker mode handles background tasks, such as scheduled jobs and subscribers.
-
-So, when choosing a hosting provider, make sure it supports deploying these resources. Also, for optimal experience, the hosting provider and plan must offer at least 2GB of RAM.
-
-***
-
-## 1. Configure Medusa Application
-
-### Worker Mode
-
-The `workerMode` configuration determines which mode the Medusa application runs in. When you deploy the Medusa application, you deploy two instances: one in server mode, and one in worker mode.
-
-Learn more about worker mode in the [Worker Module chapter](https://docs.medusajs.com/learn/production/worker-mode/index.html.md).
-
-So, add the following configuration in `medusa-config.ts`:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- projectConfig: {
- // ...
- workerMode: process.env.MEDUSA_WORKER_MODE as "shared" | "worker" | "server",
- },
-})
-```
-
-Later, you’ll set different values of the `MEDUSA_WORKER_MODE` environment variable for each Medusa application deployment or instance.
-
-### Configure Medusa Admin
-
-The Medusa Admin is served by the Medusa server application. So, you need to disable it in the worker Medusa application only.
-
-To disable the Medusa Admin in the worker Medusa application while keeping it enabled in the server Medusa application, add the following configuration in `medusa-config.ts`:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- // ...
- admin: {
- disable: process.env.DISABLE_MEDUSA_ADMIN === "true",
- },
-})
-```
-
-Later, you’ll set different values of the `DISABLE_MEDUSA_ADMIN` environment variable for each Medusa application instance.
-
-### Configure Redis URL
-
-The `redisUrl` configuration specifies the connection URL to Redis to store the Medusa server's session.
-
-Learn more in the [Medusa Configuration documentation](https://docs.medusajs.com/learn/configurations/medusa-config#redisurl/index.html.md).
-
-So, add the following configuration in `medusa-config.ts` :
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- projectConfig: {
- // ...
- redisUrl: process.env.REDIS_URL,
- },
-})
-```
-
-***
-
-## 2. Add predeploy Script
-
-Before you start the Medusa application in production, you should always run migrations and sync links.
-
-So, add the following script in `package.json`:
-
-```json
-"scripts": {
- // ...
- "predeploy": "medusa db:migrate"
-},
-```
-
-***
-
-## 3. Install Production Modules and Providers
-
-By default, your Medusa application uses modules and providers useful for development, such as the In-Memory Cache Module or the Local File Module Provider.
-
-It’s highly recommended to instead use modules and providers suitable for production, including:
-
-- [Redis Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/redis/index.html.md)
-- [Redis Event Bus Module](https://docs.medusajs.com/resources/architectural-modules/event/redis/index.html.md)
-- [Workflow Engine Redis Module](https://docs.medusajs.com/resources/architectural-modules/workflow-engine/redis/index.html.md)
-- [S3 File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file/s3/index.html.md) (or other file module providers production-ready).
-- [SendGrid Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification/sendgrid/index.html.md) (or other notification module providers production-ready).
-
-Then, add these modules in `medusa-config.ts`:
-
-```ts title="medusa-config.ts"
-import { Modules } from "@medusajs/framework/utils"
-
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/medusa/cache-redis",
- options: {
- redisUrl: process.env.REDIS_URL,
- },
- },
- {
- resolve: "@medusajs/medusa/event-bus-redis",
- options: {
- redisUrl: process.env.REDIS_URL,
- },
- },
- {
- resolve: "@medusajs/medusa/workflow-engine-redis",
- options: {
- redis: {
- url: process.env.REDIS_URL,
- },
- },
- },
- ],
-})
-```
-
-Check out the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) and [Architectural Modules](https://docs.medusajs.com/resources/architectural-modules/index.html.md) documentation for other modules and providers to use.
-
-***
-
-## 4. Create PostgreSQL and Redis Databases
-
-Your Medusa application must connect to PostgreSQL and Redis databases. So, before you deploy it, create production PostgreSQL and Redis databases.
-
-If your hosting provider doesn't support databases, you can use [Neon for PostgreSQL database hosting](https://neon.tech/), and [Redis Cloud for the Redis database hosting](https://redis.io/cloud/).
-
-After hosting both databases, keep their connection URLs for the next steps.
-
-***
-
-## 5. Deploy Medusa Application in Server Mode
-
-As mentioned earlier, you'll deploy two instances or create two deployments of your Medusa application: one in server mode, and the other in worker mode.
-
-The deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment.
-
-### Set Environment Variables
-
-When setting the environment variables of the Medusa application, set the following variables:
-
-```bash
-COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET
-JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET
-STORE_CORS= # STOREFRONT URL
-ADMIN_CORS= # ADMIN URL
-AUTH_CORS= # STOREFRONT AND ADMIN URLS, SEPARATED BY COMMAS
-DISABLE_MEDUSA_ADMIN=false
-MEDUSA_WORKER_MODE=server
-PORT=9000
-DATABASE_URL # POSTGRES DATABASE URL
-REDIS_URL= # REDIS DATABASE URL
-```
-
-Where:
-
-- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret.
-- `STORE_CORS`'s value is the URL of your storefront. If you don’t have it yet, you can skip adding it for now.
-- `ADMIN_CORS`'s value is the URL of the admin dashboard, which is the same as the server Medusa application. You can add it later if you don't currently have it.
-- `AUTH_CORS`'s value is the URLs of any application authenticating users, customers, or other actor types, such as the storefront and admin URLs. The URLs are separated by commas. If you don’t have the URLs yet, you can set its value later.
-- Set `DISABLE_MEDUSA_ADMIN`'s value to `false` so that the admin is built with the server application.
-- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
-- Set the Redis database's connection URL as the value of `REDIS_URL`.
-
-Feel free to add any other relevant environment variables, such as for integrations and architectural modules. If you're using environment variables in your admin customizations, make sure to set them as well, as they're inlined during the build process.
-
-### Set Start Command
-
-The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. So, you must install the dependencies in the `.medusa/server` directory, then run the `start` command in it.
-
-If your hosting provider doesn't support setting a current-working directory, set the start command to the following:
-
-```bash npm2yarn
-cd .medusa/server && npm install && npm run predeploy && npm run start
-```
-
-Notice that you run the `predeploy` command before starting the Medusa application to run migrations and sync links whenever there's an update.
-
-### Set Backend URL in Admin Configuration
-
-The Medusa Admin is built and hosted statically. To send requests to the Medusa server application, you must set the backend URL in the Medusa Admin's configuration.
-
-After you’ve obtained the Medusa application’s URL, add the following configuration to `medusa-config.ts`:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- // ...
- admin: {
- // ...
- backendUrl: process.env.MEDUSA_BACKEND_URL,
- },
-})
-```
-
-Then, push the changes to the GitHub repository or deployed application.
-
-In your hosting provider, add or modify the following environment variables for the Medusa application in server mode:
-
-```bash
-ADMIN_CORS= # MEDUSA APPLICATION URL
-AUTH_CORS= # ADD MEDUSA APPLICATION URL
-MEDUSA_BACKEND_URL= # URL TO DEPLOYED MEDUSA APPLICATION
-```
-
-Where you set the value of `ADMIN_CORS` and `MEDUSA_BACKEND_URL` to the Medusa application’s URL, and you add the URL to `AUTH_CORS`.
-
-After setting the environment variables, make sure to restart the deployment for the changes to take effect.
-
-Remember to separate URLs in `AUTH_CORS` by commas.
-
-***
-
-## 6. Deploy Medusa Application in Worker Mode
-
-Next, you'll deploy the Medusa application in worker mode.
-
-As explained in the previous section, the deployment steps depend on your hosting provider. This section provides the general steps to perform during the deployment.
-
-### Set Environment Variables
-
-When setting the environment variables of the Medusa application, set the following variables:
-
-```bash
-COOKIE_SECRET=supersecret # TODO GENERATE SECURE SECRET
-JWT_SECRET=supersecret # TODO GENERATE SECURE SECRET
-DISABLE_MEDUSA_ADMIN=true
-MEDUSA_WORKER_MODE=worker
-PORT=9000
-DATABASE_URL # POSTGRES DATABASE URL
-REDIS_URL= # REDIS DATABASE URL
-```
-
-Where:
-
-- The value of `COOKIE_SECRET` and `JWT_SECRET` must be a randomly generated secret.
-- Set `DISABLE_MEDUSA_ADMIN`'s value to `true` so that the admin isn't built with the worker application.
-- Set the PostgreSQL database's connection URL as the value of `DATABASE_URL`
-- Set the Redis database's connection URL as the value of `REDIS_URL`.
-
-Feel free to add any other relevant environment variables, such as for integrations and architectural modules.
-
-### Set Start Command
-
-The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. So, you must install the dependencies in the `.medusa/server` directory, then run the `start` command in it.
-
-If your hosting provider doesn't support setting a current-working directory, set the start command to the following:
-
-```bash npm2yarn
-cd .medusa/server && npm run install && npm run start
-```
-
-***
-
-## 7. Test Deployed Application
-
-Once the application is deployed and live, go to `/health`, where `` is the URL of the Medusa application in server mode. If the deployment was successful, you’ll see the `OK` response.
-
-The Medusa Admin is also available at `/app`.
-
-***
-
-## Create Admin User
-
-If your hosting provider supports running commands in your Medusa application's directory, run the following command to create an admin user:
-
-```bash
-npx medusa user -e admin-medusa@test.com -p supersecret
-```
-
-Replace the email `admin-medusa@test.com` and password `supersecret` with the credentials you want.
-
-You can use these credentials to log into the Medusa Admin dashboard.
-
-
# Admin Development
In the next chapters, you'll learn more about possible admin customizations.
@@ -2214,65 +2214,6 @@ Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/index.html.m
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components.
-# API Routes
-
-In this chapter, you’ll learn what API Routes are and how to create them.
-
-## What is an API Route?
-
-An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
-
-The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
-
-***
-
-## How to Create an API Route?
-
-An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`.
-
-
-
-Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
-
-For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
-
-```ts title="src/api/hello-world/route.ts"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: "[GET] Hello world!",
- })
-}
-```
-
-### Test API Route
-
-To test the API route above, start the Medusa application:
-
-```bash npm2yarn
-npm run dev
-```
-
-Then, send a `GET` request to the `/hello-world` API Route:
-
-```bash
-curl http://localhost:9000/hello-world
-```
-
-***
-
-## When to Use API Routes
-
-You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application.
-
-
# Custom CLI Scripts
In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool.
@@ -2343,6 +2284,169 @@ npx medusa exec ./src/scripts/my-script.ts arg1 arg2
```
+# Data Models
+
+In this chapter, you'll learn what a data model is and how to create a data model.
+
+## What is a Data Model?
+
+A data model represents a table in the database. You create data models using Medusa's data modeling language (DML). It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations.
+
+You create a data model in a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). The module's service provides the methods to store and manage those data models. Then, you can resolve the module's service in other customizations, such as a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), to manage the data models' records.
+
+***
+
+## How to Create a Data Model
+
+In a module, you can create a data model in a TypeScript or JavaScript file under the module's `models` directory.
+
+So, for example, assuming you have a Blog Module at `src/modules/blog`, you can create a `Post` data model by creating the `src/modules/blog/models/post.ts` file with the following content:
+
+
+
+```ts title="src/modules/blog/models/post.ts"
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ id: model.id().primaryKey(),
+ title: model.text(),
+})
+
+export default Post
+```
+
+You define the data model using the `define` method of the DML. It accepts two parameters:
+
+1. The first one is the name of the data model's table in the database. Use snake-case names.
+2. The second is an object, which is the data model's schema. The schema's properties are defined using the `model`'s methods, such as `text` and `id`.
+ - Data models automatically have the date properties `created_at`, `updated_at`, and `deleted_at`, so you don't need to add them manually.
+
+The code snippet above defines a `Post` data model with `id` and `title` properties.
+
+***
+
+## Generate Migrations
+
+After you create a data model in a module, then [register that module in your Medusa configurations](https://docs.medusajs.com/learn/fundamentals/modules#4-add-module-to-medusas-configurations/index.html.md), you must generate a migration to create the data model's table in the database.
+
+A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
+
+For example, to generate a migration for the Blog Module, run the following command in your Medusa application's directory:
+
+If you're creating the module in a plugin, use the [plugin:db:generate command](https://docs.medusajs.com/resources/medusa-cli/commands/plugin#plugindbgenerate/index.html.md) instead.
+
+```bash
+npx medusa db:generate blog
+```
+
+The `db:generate` command of the Medusa CLI accepts one or more module names to generate the migration for. It will create a migration file for the Blog Module in the directory `src/modules/blog/migrations` similar to the following:
+
+```ts
+import { Migration } from "@mikro-orm/migrations"
+
+export class Migration20241121103722 extends Migration {
+
+ async up(): Promise {
+ this.addSql("create table if not exists \"post\" (\"id\" text not null, \"title\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"post_pkey\" primary key (\"id\"));")
+ }
+
+ async down(): Promise {
+ this.addSql("drop table if exists \"post\" cascade;")
+ }
+
+}
+```
+
+In the migration class, the `up` method creates the table `post` and defines its columns using PostgreSQL syntax. The `down` method drops the table.
+
+### Run Migrations
+
+To reflect the changes in the generated migration file on the database, run the `db:migrate` command:
+
+If you're creating the module in a plugin, run this command on the Medusa application that the plugin is installed in.
+
+```bash
+npx medusa db:migrate
+```
+
+This creates the `post` table in the database.
+
+### Migrations on Data Model Changes
+
+Whenever you make a change to a data model, you must generate and run the migrations.
+
+For example, if you add a new column to the `Post` data model, you must generate a new migration and run it.
+
+***
+
+## Manage Data Models
+
+Your module's service should extend the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md), which generates data-management methods for your module's data models.
+
+For example, the Blog Module's service would have methods like `retrievePost` and `createPosts`.
+
+Refer to the [Service Factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) chapter to learn more about how to extend the service factory and manage data models, and refer to the [Service Factory Reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) for the full list of generated methods and how to use them.
+
+
+# API Routes
+
+In this chapter, you’ll learn what API Routes are and how to create them.
+
+## What is an API Route?
+
+An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems.
+
+The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities.
+
+***
+
+## How to Create an API Route?
+
+An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`.
+
+
+
+Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…).
+
+For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content:
+
+```ts title="src/api/hello-world/route.ts"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: "[GET] Hello world!",
+ })
+}
+```
+
+### Test API Route
+
+To test the API route above, start the Medusa application:
+
+```bash npm2yarn
+npm run dev
+```
+
+Then, send a `GET` request to the `/hello-world` API Route:
+
+```bash
+curl http://localhost:9000/hello-world
+```
+
+***
+
+## When to Use API Routes
+
+You're exposing custom functionality to be used by a storefront, admin dashboard, or any external application.
+
+
# Environment Variables
In this chapter, you'll learn how environment variables are loaded in Medusa.
@@ -2514,118 +2618,14 @@ The first message indicates that the `order.placed` event was emitted, and the s
## Event Module
-The subscription and emitting of events is handled by an Event Module, an architectural module that implements the pub/sub functionalities of Medusa's event system.
+The subscription and emitting of events is handled by an Event Module, an Infrastructure Module that implements the pub/sub functionalities of Medusa's event system.
Medusa provides two Event Modules out of the box:
-- [Local Event Module](https://docs.medusajs.com/resources/architectural-modules/event/local/index.html.md), used by default. It's useful for development, as you don't need additional setup to use it.
-- [Redis Event Module](https://docs.medusajs.com/resources/architectural-modules/event/redis/index.html.md), which is useful in production. It uses [Redis](https://redis.io/) to implement Medusa's pub/sub events system.
+- [Local Event Module](https://docs.medusajs.com/resources/infrastructure-modules/event/local/index.html.md), used by default. It's useful for development, as you don't need additional setup to use it.
+- [Redis Event Module](https://docs.medusajs.com/resources/infrastructure-modules/event/redis/index.html.md), which is useful in production. It uses [Redis](https://redis.io/) to implement Medusa's pub/sub events system.
-Medusa's [architecture](https://docs.medusajs.com/learn/introduction/architecture/index.html.md) also allows you to build a custom Event Module that uses a different service or logic to implement the pub/sub system. Learn how to build an Event Module in [this guide](https://docs.medusajs.com/resources/architectural-modules/event/create/index.html.md).
-
-
-# Data Models
-
-In this chapter, you'll learn what a data model is and how to create a data model.
-
-## What is a Data Model?
-
-A data model represents a table in the database. You create data models using Medusa's data modeling language (DML). It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations.
-
-You create a data model in a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). The module's service provides the methods to store and manage those data models. Then, you can resolve the module's service in other customizations, such as a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), to manage the data models' records.
-
-***
-
-## How to Create a Data Model
-
-In a module, you can create a data model in a TypeScript or JavaScript file under the module's `models` directory.
-
-So, for example, assuming you have a Blog Module at `src/modules/blog`, you can create a `Post` data model by creating the `src/modules/blog/models/post.ts` file with the following content:
-
-
-
-```ts title="src/modules/blog/models/post.ts"
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- id: model.id().primaryKey(),
- title: model.text(),
-})
-
-export default Post
-```
-
-You define the data model using the `define` method of the DML. It accepts two parameters:
-
-1. The first one is the name of the data model's table in the database. Use snake-case names.
-2. The second is an object, which is the data model's schema. The schema's properties are defined using the `model`'s methods, such as `text` and `id`.
- - Data models automatically have the date properties `created_at`, `updated_at`, and `deleted_at`, so you don't need to add them manually.
-
-The code snippet above defines a `Post` data model with `id` and `title` properties.
-
-***
-
-## Generate Migrations
-
-After you create a data model in a module, then [register that module in your Medusa configurations](https://docs.medusajs.com/learn/fundamentals/modules#4-add-module-to-medusas-configurations/index.html.md), you must generate a migration to create the data model's table in the database.
-
-A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
-
-For example, to generate a migration for the Blog Module, run the following command in your Medusa application's directory:
-
-If you're creating the module in a plugin, use the [plugin:db:generate command](https://docs.medusajs.com/resources/medusa-cli/commands/plugin#plugindbgenerate/index.html.md) instead.
-
-```bash
-npx medusa db:generate blog
-```
-
-The `db:generate` command of the Medusa CLI accepts one or more module names to generate the migration for. It will create a migration file for the Blog Module in the directory `src/modules/blog/migrations` similar to the following:
-
-```ts
-import { Migration } from "@mikro-orm/migrations"
-
-export class Migration20241121103722 extends Migration {
-
- async up(): Promise {
- this.addSql("create table if not exists \"post\" (\"id\" text not null, \"title\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"post_pkey\" primary key (\"id\"));")
- }
-
- async down(): Promise {
- this.addSql("drop table if exists \"post\" cascade;")
- }
-
-}
-```
-
-In the migration class, the `up` method creates the table `post` and defines its columns using PostgreSQL syntax. The `down` method drops the table.
-
-### Run Migrations
-
-To reflect the changes in the generated migration file on the database, run the `db:migrate` command:
-
-If you're creating the module in a plugin, run this command on the Medusa application that the plugin is installed in.
-
-```bash
-npx medusa db:migrate
-```
-
-This creates the `post` table in the database.
-
-### Migrations on Data Model Changes
-
-Whenever you make a change to a data model, you must generate and run the migrations.
-
-For example, if you add a new column to the `Post` data model, you must generate a new migration and run it.
-
-***
-
-## Manage Data Models
-
-Your module's service should extend the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md), which generates data-management methods for your module's data models.
-
-For example, the Blog Module's service would have methods like `retrievePost` and `createPosts`.
-
-Refer to the [Service Factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) chapter to learn more about how to extend the service factory and manage data models, and refer to the [Service Factory Reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) for the full list of generated methods and how to use them.
+Medusa's [architecture](https://docs.medusajs.com/learn/introduction/architecture/index.html.md) also allows you to build a custom Event Module that uses a different service or logic to implement the pub/sub system. Learn how to build an Event Module in [this guide](https://docs.medusajs.com/resources/infrastructure-modules/event/create/index.html.md).
# Framework Overview
@@ -3556,7 +3556,7 @@ In this chapter, you’ll learn what a module link is and how to define one.
Medusa's modular architecture isolates modules from one another to ensure they can be integrated into your application without side effects. Module isolation has other benefits, which you can learn about in the [Module Isolation chapter](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md). Since modules are isolated, you can't access another module's data models to add a relation to it or extend it. Instead, you use a module link.
-A module link forms an association between two data models of different modules while maintaining module isolation. Using module links, you can build virtual relations between your custom data models and data models in the commerce modules, which is useful as you extend the features provided by the commerce modules. Then, Medusa creates a link table in the database to store the IDs of the linked records. You'll learn more about link tables later in this chapter.
+A module link forms an association between two data models of different modules while maintaining module isolation. Using module links, you can build virtual relations between your custom data models and data models in the Commerce Modules, which is useful as you extend the features provided by the Commerce Modules. Then, Medusa creates a link table in the database to store the IDs of the linked records. You'll learn more about link tables later in this chapter.
For example, the [Brand Customizations Tutorial](https://docs.medusajs.com/learn/customization/extend-features/index.html.md) shows how to create a Brand Module that adds the concept of brands to your application, then link those brands to a product.
@@ -3763,211 +3763,6 @@ npx medusa db:migrate
```
-# Plugins
-
-In this chapter, you'll learn what a plugin is in Medusa.
-
-Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
-
-## What is a Plugin?
-
-A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. The supported customizations are [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), [API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md), [Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), [Subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md), [Scheduled Jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md), and [Admin Extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md).
-
-Plugins allow you to reuse your Medusa customizations across multiple projects or share them with the community. They can be published to npm and installed in any Medusa project.
-
-
-
-Learn how to create a wishlist plugin in [this guide](https://docs.medusajs.com/resources/plugins/guides/wishlist/index.html.md).
-
-***
-
-## Plugin vs Module
-
-A [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) is an isolated package related to a single domain or functionality, such as product reviews or integrating a Content Management System. A module can't access any resources in the Medusa application that are outside its codebase.
-
-A plugin, on the other hand, can contain multiple Medusa customizations, including modules. Your plugin can define a module, then build flows around it.
-
-For example, in a plugin, you can define a module that integrates a third-party service, then add a workflow that uses the module when a certain event occurs to sync data to that service.
-
-- You want to reuse your Medusa customizations across multiple projects.
-- You want to share your Medusa customizations with the community.
-
-- You want to build a custom feature related to a single domain or integrate a third-party service. Instead, use a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). You can wrap that module in a plugin if it's used in other customizations, such as if it has a module link or it's used in a workflow.
-
-***
-
-## How to Create a Plugin?
-
-The next chapter explains how you can create and publish a plugin.
-
-
-# Scheduled Jobs
-
-In this chapter, you’ll learn about scheduled jobs and how to use them.
-
-## What is a Scheduled Job?
-
-When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day.
-
-In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling.
-
-Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP.
-
-- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job.
-- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) instead.
-- You want to execute the action if an event occurs. Use [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) instead.
-
-***
-
-## How to Create a Scheduled Job?
-
-You create a scheduled job in a TypeScript or JavaScript file under the `src/jobs` directory. The file exports the asynchronous function to run, and the configurations indicating the schedule to run the function.
-
-For example, create the file `src/jobs/hello-world.ts` with the following content:
-
-
-
-```ts title="src/jobs/hello-world.ts" highlights={highlights}
-import { MedusaContainer } from "@medusajs/framework/types"
-
-export default async function greetingJob(container: MedusaContainer) {
- const logger = container.resolve("logger")
-
- logger.info("Greeting!")
-}
-
-export const config = {
- name: "greeting-every-minute",
- schedule: "* * * * *",
-}
-```
-
-You export an asynchronous function that receives the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) as a parameter. In the function, you resolve the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) from the Medusa container and log a message.
-
-You also export a `config` object that has the following properties:
-
-- `name`: A unique name for the job.
-- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job.
-
-This scheduled job executes every minute and logs into the terminal `Greeting!`.
-
-### Test the Scheduled Job
-
-To test out your scheduled job, start the Medusa application:
-
-```bash npm2yarn
-npm run dev
-```
-
-After a minute, the following message will be logged to the terminal:
-
-```bash
-info: Greeting!
-```
-
-***
-
-## Example: Sync Products Once a Day
-
-In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service.
-
-When implementing flows spanning across systems or [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), you use [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
-
-You can learn how to create a workflow in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
-
-```ts title="src/jobs/sync-products.ts"
-import { MedusaContainer } from "@medusajs/framework/types"
-import { syncProductToErpWorkflow } from "../workflows/sync-products-to-erp"
-
-export default async function syncProductsJob(container: MedusaContainer) {
- await syncProductToErpWorkflow(container)
- .run()
-}
-
-export const config = {
- name: "sync-products-job",
- schedule: "0 0 * * *",
-}
-```
-
-In the scheduled job function, you execute the `syncProductToErpWorkflow` by invoking it and passing it the container, then invoking the `run` method. You also specify in the exported configurations the schedule `0 0 * * *` which indicates midnight time of every day.
-
-The next time you start the Medusa application, it will run this job every day at midnight.
-
-
-# Medusa's Architecture
-
-In this chapter, you'll learn about the architectural layers in Medusa.
-
-Find the full architectural diagram at the [end of this chapter](#full-diagram-of-medusas-architecture).
-
-## HTTP, Workflow, and Module Layers
-
-Medusa is a headless commerce platform. So, storefronts, admin dashboards, and other clients consume Medusa's functionalities through its API routes.
-
-In a common Medusa application, requests go through four layers in the stack. In order of entry, those are:
-
-1. API Routes (HTTP): Our API Routes are the typical entry point. The Medusa server is based on Express.js, which handles incoming requests. It can also connect to a Redis database that stores the server session data.
-2. Workflows: API Routes consume workflows that hold the opinionated business logic of your application.
-3. Modules: Workflows use domain-specific modules for resource management.
-4. Data store: Modules query the underlying datastore, which is a PostgreSQL database in common cases.
-
-These layers of stack can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
-
-
-
-***
-
-## Database Layer
-
-The Medusa application injects into each module, including your [custom modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), a connection to the configured PostgreSQL database. Modules use that connection to read and write data to the database.
-
-Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
-
-
-
-***
-
-## Third-Party Integrations Layer
-
-Third-party services and systems are integrated through Medusa's Commerce and Architectural modules. You also create custom third-party integrations through a [custom module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
-
-Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
-
-### Commerce Modules
-
-[Commerce modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md) integrate third-party services relevant for commerce or user-facing features. For example, you can integrate [Stripe](https://docs.medusajs.com/resources/commerce-modules/payment/payment-provider/stripe/index.html.md) through a Payment Module Provider, or [ShipStation](https://docs.medusajs.com/resources/integrations/guides/shipstation/index.html.md) through a Fulfillment Module Provider.
-
-You can also integrate third-party services for custom functionalities. For example, you can integrate [Sanity](https://docs.medusajs.com/resources/integrations/guides/sanity/index.html.md) for rich CMS capabilities, or [Odoo](https://docs.medusajs.com/resources/recipes/erp/odoo/index.html.md) to sync your Medusa application with your ERP system.
-
-You can replace any of the third-party services mentioned above to build your preferred commerce ecosystem.
-
-
-
-### Architectural Modules
-
-[Architectural modules](https://docs.medusajs.com/resources/architectural-modules/index.html.md) integrate third-party services and systems for architectural features. Medusa has the following Architectural modules:
-
-- [Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/index.html.md): 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](https://docs.medusajs.com/resources/architectural-modules/cache/redis/index.html.md).
-- [Event Module](https://docs.medusajs.com/resources/architectural-modules/event/index.html.md): A pub/sub system that allows you to subscribe to events and trigger them. You can integrate [Redis](https://docs.medusajs.com/resources/architectural-modules/event/redis/index.html.md) as the pub/sub system.
-- [File Module](https://docs.medusajs.com/resources/architectural-modules/file/index.html.md): Manages file uploads and storage, such as upload of product images. You can integrate [AWS S3](https://docs.medusajs.com/resources/architectural-modules/file/s3/index.html.md) for file storage.
-- [Locking Module](https://docs.medusajs.com/resources/architectural-modules/locking/index.html.md): Manages access to shared resources by multiple processes or threads, preventing conflict between processes and ensuring data consistency. You can integrate [Redis](https://docs.medusajs.com/resources/architectural-modules/locking/redis/index.html.md) for locking.
-- [Notification Module](https://docs.medusajs.com/resources/architectural-modules/notification/index.html.md): Sends notifications to customers and users, such as for order updates or newsletters. You can integrate [SendGrid](https://docs.medusajs.com/resources/architectural-modules/notification/sendgrid/index.html.md) for sending emails.
-- [Workflow Engine Module](https://docs.medusajs.com/resources/architectural-modules/workflow-engine/index.html.md): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](https://docs.medusajs.com/resources/architectural-modules/workflow-engine/redis/index.html.md) to orchestrate workflows.
-
-All of the third-party services mentioned above can be replaced to help you build your preferred architecture and ecosystem.
-
-
-
-***
-
-## Full Diagram of Medusa's Architecture
-
-The following diagram illustrates Medusa's architecture including all its layers.
-
-
-
-
# Modules
In this chapter, you’ll learn about modules and how to create them.
@@ -4268,6 +4063,303 @@ This will create a post and return it in the response:
You can also execute the workflow from a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) when an event occurs, or from a [scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) to run it at a specified interval.
+# Plugins
+
+In this chapter, you'll learn what a plugin is in Medusa.
+
+Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
+
+## What is a Plugin?
+
+A plugin is a package of reusable Medusa customizations that you can install in any Medusa application. The supported customizations are [Modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), [API Routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), [Workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), [Workflow Hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md), [Links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), [Subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md), [Scheduled Jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md), and [Admin Extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md).
+
+Plugins allow you to reuse your Medusa customizations across multiple projects or share them with the community. They can be published to npm and installed in any Medusa project.
+
+
+
+Learn how to create a wishlist plugin in [this guide](https://docs.medusajs.com/resources/plugins/guides/wishlist/index.html.md).
+
+***
+
+## Plugin vs Module
+
+A [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) is an isolated package related to a single domain or functionality, such as product reviews or integrating a Content Management System. A module can't access any resources in the Medusa application that are outside its codebase.
+
+A plugin, on the other hand, can contain multiple Medusa customizations, including modules. Your plugin can define a module, then build flows around it.
+
+For example, in a plugin, you can define a module that integrates a third-party service, then add a workflow that uses the module when a certain event occurs to sync data to that service.
+
+- You want to reuse your Medusa customizations across multiple projects.
+- You want to share your Medusa customizations with the community.
+
+- You want to build a custom feature related to a single domain or integrate a third-party service. Instead, use a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). You can wrap that module in a plugin if it's used in other customizations, such as if it has a module link or it's used in a workflow.
+
+***
+
+## How to Create a Plugin?
+
+The next chapter explains how you can create and publish a plugin.
+
+
+# Scheduled Jobs
+
+In this chapter, you’ll learn about scheduled jobs and how to use them.
+
+## What is a Scheduled Job?
+
+When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day.
+
+In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling.
+
+Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP.
+
+- You want the action to execute at a specified schedule while the Medusa application **isn't** running. Instead, use the operating system's equivalent of a cron job.
+- You want to execute the action once when the application loads. Use [loaders](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md) instead.
+- You want to execute the action if an event occurs. Use [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) instead.
+
+***
+
+## How to Create a Scheduled Job?
+
+You create a scheduled job in a TypeScript or JavaScript file under the `src/jobs` directory. The file exports the asynchronous function to run, and the configurations indicating the schedule to run the function.
+
+For example, create the file `src/jobs/hello-world.ts` with the following content:
+
+
+
+```ts title="src/jobs/hello-world.ts" highlights={highlights}
+import { MedusaContainer } from "@medusajs/framework/types"
+
+export default async function greetingJob(container: MedusaContainer) {
+ const logger = container.resolve("logger")
+
+ logger.info("Greeting!")
+}
+
+export const config = {
+ name: "greeting-every-minute",
+ schedule: "* * * * *",
+}
+```
+
+You export an asynchronous function that receives the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) as a parameter. In the function, you resolve the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) from the Medusa container and log a message.
+
+You also export a `config` object that has the following properties:
+
+- `name`: A unique name for the job.
+- `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job.
+
+This scheduled job executes every minute and logs into the terminal `Greeting!`.
+
+### Test the Scheduled Job
+
+To test out your scheduled job, start the Medusa application:
+
+```bash npm2yarn
+npm run dev
+```
+
+After a minute, the following message will be logged to the terminal:
+
+```bash
+info: Greeting!
+```
+
+***
+
+## Example: Sync Products Once a Day
+
+In this section, you'll find a brief example of how you use a scheduled job to sync products to a third-party service.
+
+When implementing flows spanning across systems or [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), you use [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). A workflow is a task made up of a series of steps, and you construct it like you would a regular function, but it's a special function that supports rollback mechanism in case of errors, background execution, and more.
+
+You can learn how to create a workflow in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), but this example assumes you already have a `syncProductToErpWorkflow` implemented. To execute this workflow once a day, create a scheduled job at `src/jobs/sync-products.ts` with the following content:
+
+```ts title="src/jobs/sync-products.ts"
+import { MedusaContainer } from "@medusajs/framework/types"
+import { syncProductToErpWorkflow } from "../workflows/sync-products-to-erp"
+
+export default async function syncProductsJob(container: MedusaContainer) {
+ await syncProductToErpWorkflow(container)
+ .run()
+}
+
+export const config = {
+ name: "sync-products-job",
+ schedule: "0 0 * * *",
+}
+```
+
+In the scheduled job function, you execute the `syncProductToErpWorkflow` by invoking it and passing it the container, then invoking the `run` method. You also specify in the exported configurations the schedule `0 0 * * *` which indicates midnight time of every day.
+
+The next time you start the Medusa application, it will run this job every day at midnight.
+
+
+# Medusa's Architecture
+
+In this chapter, you'll learn about the architectural layers in Medusa.
+
+Find the full architectural diagram at the [end of this chapter](#full-diagram-of-medusas-architecture).
+
+## HTTP, Workflow, and Module Layers
+
+Medusa is a headless commerce platform. So, storefronts, admin dashboards, and other clients consume Medusa's functionalities through its API routes.
+
+In a common Medusa application, requests go through four layers in the stack. In order of entry, those are:
+
+1. API Routes (HTTP): Our API Routes are the typical entry point. The Medusa server is based on Express.js, which handles incoming requests. It can also connect to a Redis database that stores the server session data.
+2. Workflows: API Routes consume workflows that hold the opinionated business logic of your application.
+3. Modules: Workflows use domain-specific modules for resource management.
+4. Data store: Modules query the underlying datastore, which is a PostgreSQL database in common cases.
+
+These layers of stack can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
+
+
+
+***
+
+## Database Layer
+
+The Medusa application injects into each module, including your [custom modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), a connection to the configured PostgreSQL database. Modules use that connection to read and write data to the database.
+
+Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
+
+
+
+***
+
+## Third-Party Integrations Layer
+
+Third-party services and systems are integrated through Medusa's Commerce and Infrastructure Modules. You also create custom third-party integrations through a [custom module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
+
+Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md).
+
+### Commerce Modules
+
+[Commerce Modules](https://docs.medusajs.com/resources/commerce-modules/index.html.md) integrate third-party services relevant for commerce or user-facing features. For example, you can integrate [Stripe](https://docs.medusajs.com/resources/commerce-modules/payment/payment-provider/stripe/index.html.md) through a Payment Module Provider, or [ShipStation](https://docs.medusajs.com/resources/integrations/guides/shipstation/index.html.md) through a Fulfillment Module Provider.
+
+You can also integrate third-party services for custom functionalities. For example, you can integrate [Sanity](https://docs.medusajs.com/resources/integrations/guides/sanity/index.html.md) for rich CMS capabilities, or [Odoo](https://docs.medusajs.com/resources/recipes/erp/odoo/index.html.md) to sync your Medusa application with your ERP system.
+
+You can replace any of the third-party services mentioned above to build your preferred commerce ecosystem.
+
+
+
+### Infrastructure Modules
+
+[Infrastructure Modules](https://docs.medusajs.com/resources/infrastructure-modules/index.html.md) integrate third-party services and systems that customize Medusa's infrastructure. Medusa has the following Infrastructure Modules:
+
+- [Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/index.html.md): 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](https://docs.medusajs.com/resources/infrastructure-modules/cache/redis/index.html.md).
+- [Event Module](https://docs.medusajs.com/resources/infrastructure-modules/event/index.html.md): A pub/sub system that allows you to subscribe to events and trigger them. You can integrate [Redis](https://docs.medusajs.com/resources/infrastructure-modules/event/redis/index.html.md) as the pub/sub system.
+- [File Module](https://docs.medusajs.com/resources/infrastructure-modules/file/index.html.md): Manages file uploads and storage, such as upload of product images. You can integrate [AWS S3](https://docs.medusajs.com/resources/infrastructure-modules/file/s3/index.html.md) for file storage.
+- [Locking Module](https://docs.medusajs.com/resources/infrastructure-modules/locking/index.html.md): Manages access to shared resources by multiple processes or threads, preventing conflict between processes and ensuring data consistency. You can integrate [Redis](https://docs.medusajs.com/resources/infrastructure-modules/locking/redis/index.html.md) for locking.
+- [Notification Module](https://docs.medusajs.com/resources/infrastructure-modules/notification/index.html.md): Sends notifications to customers and users, such as for order updates or newsletters. You can integrate [SendGrid](https://docs.medusajs.com/resources/infrastructure-modules/notification/sendgrid/index.html.md) for sending emails.
+- [Workflow Engine Module](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/index.html.md): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/redis/index.html.md) to orchestrate workflows.
+
+All of the third-party services mentioned above can be replaced to help you build your preferred architecture and ecosystem.
+
+
+
+***
+
+## Full Diagram of Medusa's Architecture
+
+The following diagram illustrates Medusa's architecture including all its layers.
+
+
+
+
+# Worker Mode of Medusa Instance
+
+In this chapter, you'll learn about the different modes of running a Medusa instance and how to configure the mode.
+
+## What is Worker Mode?
+
+By default, the Medusa application runs both the server, which handles all incoming requests, and the worker, which processes background tasks, in a single process. While this setup is suitable for development, it is not optimal for production environments where background tasks can be long-running or resource-intensive.
+
+In a production environment, you should deploy two separate instances of your Medusa application:
+
+1. A server instance that handles incoming requests to the application's API routes.
+2. A worker instance that processes background tasks. This includes scheduled jobs and subscribers.
+
+You don't need to set up different projects for each instance. Instead, you can configure the Medusa application to run in different modes based on environment variables, as you'll see later in this chapter.
+
+This separation ensures that the server instance remains responsive to incoming requests, while the worker instance processes tasks in the background.
+
+
+
+***
+
+## How to Set Worker Mode
+
+You can set the worker mode of your application using the `projectConfig.workerMode` configuration in the `medusa-config.ts`. The `workerMode` configuration accepts the following values:
+
+- `shared`: (default) run the application in a single process, meaning the worker and server run in the same process.
+- `worker`: run a worker process only.
+- `server`: run the application server only.
+
+Instead of creating different projects with different worker mode configurations, you can set the worker mode using an environment variable. Then, the worker mode configuration will change based on the environment variable.
+
+For example, set the worker mode in `medusa-config.ts` to the following:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ projectConfig: {
+ workerMode: process.env.WORKER_MODE || "shared",
+ // ...
+ },
+ // ...
+})
+```
+
+You set the worker mode configuration to the `process.env.WORKER_MODE` environment variable and set a default value of `shared`.
+
+Then, in the deployed server Medusa instance, set `WORKER_MODE` to `server`, and in the worker Medusa instance, set `WORKER_MODE` to `worker`:
+
+### Server Medusa Instance
+
+```bash
+WORKER_MODE=server
+```
+
+### Worker Medusa Instance
+
+```bash
+WORKER_MODE=worker
+```
+
+### Disable Admin in Worker Mode
+
+Since the worker instance only processes background tasks, you should disable the admin interface in it. That will save resources in the worker instance.
+
+To disable the admin interface, set the `admin.disable` configuration in the `medusa-config.ts` file:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ admin: {
+ disable: process.env.ADMIN_DISABLED === "true" ||
+ false,
+ },
+ // ...
+})
+```
+
+Similar to before, you set the value in an environment variable, allowing you to enable or disable the admin interface based on the environment.
+
+Then, in the deployed server Medusa instance, set `ADMIN_DISABLED` to `false`, and in the worker Medusa instance, set `ADMIN_DISABLED` to `true`:
+
+### Server Medusa Instance
+
+```bash
+ADMIN_DISABLED=false
+```
+
+### Worker Medusa Instance
+
+```bash
+ADMIN_DISABLED=true
+```
+
+
# Workflows
In this chapter, you’ll learn about workflows and how to define and execute them.
@@ -4522,98 +4614,6 @@ You can now execute this workflow in a custom API route, scheduled job, or subsc
Find a full list of the registered resources in the Medusa container and their registration key in [this reference](https://docs.medusajs.com/resources/medusa-container-resources/index.html.md). You can use these resources in your custom workflows.
-# Worker Mode of Medusa Instance
-
-In this chapter, you'll learn about the different modes of running a Medusa instance and how to configure the mode.
-
-## What is Worker Mode?
-
-By default, the Medusa application runs both the server, which handles all incoming requests, and the worker, which processes background tasks, in a single process. While this setup is suitable for development, it is not optimal for production environments where background tasks can be long-running or resource-intensive.
-
-In a production environment, you should deploy two separate instances of your Medusa application:
-
-1. A server instance that handles incoming requests to the application's API routes.
-2. A worker instance that processes background tasks. This includes scheduled jobs and subscribers.
-
-You don't need to set up different projects for each instance. Instead, you can configure the Medusa application to run in different modes based on environment variables, as you'll see later in this chapter.
-
-This separation ensures that the server instance remains responsive to incoming requests, while the worker instance processes tasks in the background.
-
-
-
-***
-
-## How to Set Worker Mode
-
-You can set the worker mode of your application using the `projectConfig.workerMode` configuration in the `medusa-config.ts`. The `workerMode` configuration accepts the following values:
-
-- `shared`: (default) run the application in a single process, meaning the worker and server run in the same process.
-- `worker`: run a worker process only.
-- `server`: run the application server only.
-
-Instead of creating different projects with different worker mode configurations, you can set the worker mode using an environment variable. Then, the worker mode configuration will change based on the environment variable.
-
-For example, set the worker mode in `medusa-config.ts` to the following:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- projectConfig: {
- workerMode: process.env.WORKER_MODE || "shared",
- // ...
- },
- // ...
-})
-```
-
-You set the worker mode configuration to the `process.env.WORKER_MODE` environment variable and set a default value of `shared`.
-
-Then, in the deployed server Medusa instance, set `WORKER_MODE` to `server`, and in the worker Medusa instance, set `WORKER_MODE` to `worker`:
-
-### Server Medusa Instance
-
-```bash
-WORKER_MODE=server
-```
-
-### Worker Medusa Instance
-
-```bash
-WORKER_MODE=worker
-```
-
-### Disable Admin in Worker Mode
-
-Since the worker instance only processes background tasks, you should disable the admin interface in it. That will save resources in the worker instance.
-
-To disable the admin interface, set the `admin.disable` configuration in the `medusa-config.ts` file:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- admin: {
- disable: process.env.ADMIN_DISABLED === "true" ||
- false,
- },
- // ...
-})
-```
-
-Similar to before, you set the value in an environment variable, allowing you to enable or disable the admin interface based on the environment.
-
-Then, in the deployed server Medusa instance, set `ADMIN_DISABLED` to `false`, and in the worker Medusa instance, set `ADMIN_DISABLED` to `true`:
-
-### Server Medusa Instance
-
-```bash
-ADMIN_DISABLED=false
-```
-
-### Worker Medusa Instance
-
-```bash
-ADMIN_DISABLED=true
-```
-
-
# Usage Information
At Medusa, we strive to provide the best experience for developers using our platform. For that reason, Medusa collects anonymous and non-sensitive data that provides a global understanding of how users are using Medusa.
@@ -4852,6 +4852,125 @@ To manage that database, such as changing its name or perform operations on it i
The next chapters provide examples of writing integration tests for API routes and workflows.
+# Write Tests for Modules
+
+In this chapter, you'll learn about `moduleIntegrationTestRunner` from Medusa's Testing Framework and how to use it to write integration tests for a module's main service.
+
+### Prerequisites
+
+- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md)
+
+## moduleIntegrationTestRunner Utility
+
+`moduleIntegrationTestRunner` creates integration tests for a module. The integration tests run on a test Medusa application with only the specified module enabled.
+
+For example, assuming you have a `blog` module, create a test file at `src/modules/blog/__tests__/service.spec.ts`:
+
+```ts title="src/modules/blog/__tests__/service.spec.ts"
+import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
+import { BLOG_MODULE } from ".."
+import BlogModuleService from "../service"
+import Post from "../models/post"
+
+moduleIntegrationTestRunner({
+ moduleName: BLOG_MODULE,
+ moduleModels: [Post],
+ resolve: "./src/modules/blog",
+ testSuite: ({ service }) => {
+ // TODO write tests
+ },
+})
+
+jest.setTimeout(60 * 1000)
+```
+
+The `moduleIntegrationTestRunner` function accepts as a parameter an object with the following properties:
+
+- `moduleName`: The name of the module.
+- `moduleModels`: An array of models in the module. Refer to [this section](#write-tests-for-modules-without-data-models) if your module doesn't have data models.
+- `resolve`: The path to the module's directory.
+- `testSuite`: A function that defines the tests to run.
+
+The `testSuite` function accepts as a parameter an object having the `service` property, which is an instance of the module's main service.
+
+The type argument provided to the `moduleIntegrationTestRunner` function is used as the type of the `service` property.
+
+The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
+
+***
+
+## Run Tests
+
+Run the following command to run your module integration tests:
+
+```bash npm2yarn
+npm run test:integration:modules
+```
+
+If you don't have a `test:integration:modules` script in `package.json`, refer to the [Medusa Testing Tools chapter](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools#add-test-commands/index.html.md).
+
+This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src/modules` directory.
+
+***
+
+## Pass Module Options
+
+If your module accepts options, you can set them using the `moduleOptions` property of the `moduleIntegrationTestRunner`'s parameter.
+
+For example:
+
+```ts
+import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
+import BlogModuleService from "../service"
+
+moduleIntegrationTestRunner({
+ moduleOptions: {
+ apiKey: "123",
+ },
+ // ...
+})
+```
+
+***
+
+## Write Tests for Modules without Data Models
+
+If your module doesn't have a data model, pass a dummy model in the `moduleModels` property.
+
+For example:
+
+```ts
+import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
+import BlogModuleService from "../service"
+import { model } from "@medusajs/framework/utils"
+
+const DummyModel = model.define("dummy_model", {
+ id: model.id().primaryKey(),
+})
+
+moduleIntegrationTestRunner({
+ moduleModels: [DummyModel],
+ // ...
+})
+
+jest.setTimeout(60 * 1000)
+```
+
+***
+
+### Other Options and Inputs
+
+Refer to [the Test Tooling Reference](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md) for other available parameter options and inputs of the `testSuite` function.
+
+***
+
+## Database Used in Tests
+
+The `moduleIntegrationTestRunner` function creates a database with a random name before running the tests. Then, it drops that database after all the tests end.
+
+To manage that database, such as changing its name or perform operations on it in your tests, refer to [the Test Tooling Reference](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md).
+
+
# Guide: Create Brand API Route
In the previous two chapters, you created a [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) that added the concepts of brands to your application, then created a [workflow to create a brand](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md). In this chapter, you'll expose an API route that allows admin users to create a brand using the workflow from the previous chapter.
@@ -5060,263 +5179,6 @@ Now that you have brands in your Medusa application, you want to associate a bra
In the next chapters, you'll learn how to build associations between data models defined in different modules.
-# Write Tests for Modules
-
-In this chapter, you'll learn about `moduleIntegrationTestRunner` from Medusa's Testing Framework and how to use it to write integration tests for a module's main service.
-
-### Prerequisites
-
-- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md)
-
-## moduleIntegrationTestRunner Utility
-
-`moduleIntegrationTestRunner` creates integration tests for a module. The integration tests run on a test Medusa application with only the specified module enabled.
-
-For example, assuming you have a `blog` module, create a test file at `src/modules/blog/__tests__/service.spec.ts`:
-
-```ts title="src/modules/blog/__tests__/service.spec.ts"
-import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
-import { BLOG_MODULE } from ".."
-import BlogModuleService from "../service"
-import Post from "../models/post"
-
-moduleIntegrationTestRunner({
- moduleName: BLOG_MODULE,
- moduleModels: [Post],
- resolve: "./src/modules/blog",
- testSuite: ({ service }) => {
- // TODO write tests
- },
-})
-
-jest.setTimeout(60 * 1000)
-```
-
-The `moduleIntegrationTestRunner` function accepts as a parameter an object with the following properties:
-
-- `moduleName`: The name of the module.
-- `moduleModels`: An array of models in the module. Refer to [this section](#write-tests-for-modules-without-data-models) if your module doesn't have data models.
-- `resolve`: The path to the module's directory.
-- `testSuite`: A function that defines the tests to run.
-
-The `testSuite` function accepts as a parameter an object having the `service` property, which is an instance of the module's main service.
-
-The type argument provided to the `moduleIntegrationTestRunner` function is used as the type of the `service` property.
-
-The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
-
-***
-
-## Run Tests
-
-Run the following command to run your module integration tests:
-
-```bash npm2yarn
-npm run test:integration:modules
-```
-
-If you don't have a `test:integration:modules` script in `package.json`, refer to the [Medusa Testing Tools chapter](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools#add-test-commands/index.html.md).
-
-This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src/modules` directory.
-
-***
-
-## Pass Module Options
-
-If your module accepts options, you can set them using the `moduleOptions` property of the `moduleIntegrationTestRunner`'s parameter.
-
-For example:
-
-```ts
-import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
-import BlogModuleService from "../service"
-
-moduleIntegrationTestRunner({
- moduleOptions: {
- apiKey: "123",
- },
- // ...
-})
-```
-
-***
-
-## Write Tests for Modules without Data Models
-
-If your module doesn't have a data model, pass a dummy model in the `moduleModels` property.
-
-For example:
-
-```ts
-import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
-import BlogModuleService from "../service"
-import { model } from "@medusajs/framework/utils"
-
-const DummyModel = model.define("dummy_model", {
- id: model.id().primaryKey(),
-})
-
-moduleIntegrationTestRunner({
- moduleModels: [DummyModel],
- // ...
-})
-
-jest.setTimeout(60 * 1000)
-```
-
-***
-
-### Other Options and Inputs
-
-Refer to [the Test Tooling Reference](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md) for other available parameter options and inputs of the `testSuite` function.
-
-***
-
-## Database Used in Tests
-
-The `moduleIntegrationTestRunner` function creates a database with a random name before running the tests. Then, it drops that database after all the tests end.
-
-To manage that database, such as changing its name or perform operations on it in your tests, refer to [the Test Tooling Reference](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md).
-
-
-# Guide: Create Brand Workflow
-
-This chapter builds on the work from the [previous chapter](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) where you created a Brand Module.
-
-After adding custom modules to your application, you build commerce features around them using workflows. A workflow is a series of queries and actions, called steps, that complete a task spanning across modules. You construct a workflow similar to a regular function, but it's a special function that allows you to define roll-back logic, retry configurations, and more advanced features.
-
-The workflow you'll create in this chapter will use the Brand Module's service to implement the feature of creating a brand. In the [next chapter](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), you'll expose an API route that allows admin users to create a brand, and you'll use this workflow in the route's implementation.
-
-Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md).
-
-### Prerequisites
-
-- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
-
-***
-
-## 1. Create createBrandStep
-
-A workflow consists of a series of steps, each step created in a TypeScript or JavaScript file under the `src/workflows` directory. A step is defined using `createStep` from the Workflows SDK
-
-The workflow you're creating in this guide has one step to create the brand. So, create the file `src/workflows/create-brand.ts` with the following content:
-
-
-
-```ts title="src/workflows/create-brand.ts"
-import {
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { BRAND_MODULE } from "../modules/brand"
-import BrandModuleService from "../modules/brand/service"
-
-export type CreateBrandStepInput = {
- name: string
-}
-
-export const createBrandStep = createStep(
- "create-brand-step",
- async (input: CreateBrandStepInput, { container }) => {
- const brandModuleService: BrandModuleService = container.resolve(
- BRAND_MODULE
- )
-
- const brand = await brandModuleService.createBrands(input)
-
- return new StepResponse(brand, brand.id)
- }
-)
-```
-
-You create a `createBrandStep` using the `createStep` function. It accepts the step's unique name as a first parameter, and the step's function as a second parameter.
-
-The step function receives two parameters: input passed to the step when it's invoked, and an object of general context and configurations. This object has a `container` property, which is the Medusa container.
-
-The [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) is a registry of Framework and commerce tools accessible in your customizations, such as a workflow's step. The Medusa application registers the services of core and custom modules in the container, allowing you to resolve and use them.
-
-So, In the step function, you use the Medusa container to resolve the Brand Module's service and use its generated `createBrands` method, which accepts an object of brands to create.
-
-Learn more about the generated `create` method's usage in [this reference](https://docs.medusajs.com/resources/service-factory-reference/methods/create/index.html.md).
-
-A step must return an instance of `StepResponse`. Its first parameter is the data returned by the step, and the second is the data passed to the compensation function, which you'll learn about next.
-
-### Add Compensation Function to Step
-
-You define for each step a compensation function that's executed when an error occurs in the workflow. The compensation function defines the logic to roll-back the changes made by the step. This ensures your data remains consistent if an error occurs, which is especially useful when you integrate third-party services.
-
-Learn more about the compensation function in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md).
-
-To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`:
-
-```ts title="src/workflows/create-brand.ts"
-export const createBrandStep = createStep(
- // ...
- async (id: string, { container }) => {
- const brandModuleService: BrandModuleService = container.resolve(
- BRAND_MODULE
- )
-
- await brandModuleService.deleteBrands(id)
- }
-)
-```
-
-The compensation function's first parameter is the brand's ID which you passed as a second parameter to the step function's returned `StepResponse`. It also accepts a context object with a `container` property as a second parameter, similar to the step function.
-
-In the compensation function, you resolve the Brand Module's service from the Medusa container, then use its generated `deleteBrands` method to delete the brand created by the step. This method accepts the ID of the brand to delete.
-
-Learn more about the generated `delete` method's usage in [this reference](https://docs.medusajs.com/resources/service-factory-reference/methods/delete/index.html.md).
-
-So, if an error occurs during the workflow's execution, the brand that was created by the step is deleted to maintain data consistency.
-
-***
-
-## 2. Create createBrandWorkflow
-
-You can now create the workflow that runs the `createBrandStep`. A workflow is created in a TypeScript or JavaScript file under the `src/workflows` directory. In the file, you use `createWorkflow` from the Workflows SDK to create the workflow.
-
-Add the following content in the same `src/workflows/create-brand.ts` file:
-
-```ts title="src/workflows/create-brand.ts"
-// other imports...
-import {
- // ...
- createWorkflow,
- WorkflowResponse,
-} from "@medusajs/framework/workflows-sdk"
-
-// ...
-
-type CreateBrandWorkflowInput = {
- name: string
-}
-
-export const createBrandWorkflow = createWorkflow(
- "create-brand",
- (input: CreateBrandWorkflowInput) => {
- const brand = createBrandStep(input)
-
- return new WorkflowResponse(brand)
- }
-)
-```
-
-You create the `createBrandWorkflow` using the `createWorkflow` function. This function accepts two parameters: the workflow's unique name, and the workflow's constructor function holding the workflow's implementation.
-
-The constructor function accepts the workflow's input as a parameter. In the function, you invoke the `createBrandStep` you created in the previous step to create a brand.
-
-A workflow must return an instance of `WorkflowResponse`. It accepts as a parameter the data to return to the workflow's executor.
-
-***
-
-## Next Steps: Expose Create Brand API Route
-
-You now have a `createBrandWorkflow` that you can execute to create a brand.
-
-In the next chapter, you'll add an API route that allows admin users to create a brand. You'll learn how to create the API route, and execute in it the workflow you implemented in this chapter.
-
-
# Guide: Implement Brand Module
In this chapter, you'll build a Brand Module that adds a `brand` table to the database and provides data-management features for it.
@@ -5473,216 +5335,142 @@ The Brand Module now creates a `brand` table in the database and provides a clas
In the next chapter, you'll implement the functionality to create a brand in a workflow. You'll then use that workflow in a later chapter to expose an endpoint that allows admin users to create a brand.
-# Guide: Extend Create Product Flow
+# Guide: Create Brand Workflow
-After linking the [custom Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) in the [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md), you'll extend the create product workflow and API route to allow associating a brand with a product.
+This chapter builds on the work from the [previous chapter](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) where you created a Brand Module.
-Some API routes, including the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), accept an `additional_data` request body parameter. This parameter can hold custom data that's passed to the [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md) of the workflow executed in the API route, allowing you to consume those hooks and perform actions with the custom data.
+After adding custom modules to your application, you build commerce features around them using workflows. A workflow is a series of queries and actions, called steps, that complete a task spanning across modules. You construct a workflow similar to a regular function, but it's a special function that allows you to define roll-back logic, retry configurations, and more advanced features.
-So, in this chapter, to extend the create product flow and associate a brand with a product, you will:
+The workflow you'll create in this chapter will use the Brand Module's service to implement the feature of creating a brand. In the [next chapter](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), you'll expose an API route that allows admin users to create a brand, and you'll use this workflow in the route's implementation.
-- Consume the [productsCreated](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow#productsCreated/index.html.md) hook of the [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md), which is executed within the workflow after the product is created. You'll link the product with the brand passed in the `additional_data` parameter.
-- Extend the Create Product API route to allow passing a brand ID in `additional_data`.
-
-To learn more about the `additional_data` property and the API routes that accept additional data, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md).
+Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md).
### Prerequisites
- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
-- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md)
***
-## 1. Consume the productsCreated Hook
+## 1. Create createBrandStep
-A workflow hook is a point in a workflow where you can inject a step to perform a custom functionality. Consuming a workflow hook allows you to extend the features of a workflow and, consequently, the API route that uses it.
+A workflow consists of a series of steps, each step created in a TypeScript or JavaScript file under the `src/workflows` directory. A step is defined using `createStep` from the Workflows SDK
-Learn more about the workflow hooks in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md).
+The workflow you're creating in this guide has one step to create the brand. So, create the file `src/workflows/create-brand.ts` with the following content:
-The [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) used in the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts) has a `productsCreated` hook that runs after the product is created. You'll consume this hook to link the created product with the brand specified in the request parameters.
+
-To consume the `productsCreated` hook, create the file `src/workflows/hooks/created-product.ts` with the following content:
+```ts title="src/workflows/create-brand.ts"
+import {
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { BRAND_MODULE } from "../modules/brand"
+import BrandModuleService from "../modules/brand/service"
-
-
-```ts title="src/workflows/hooks/created-product.ts" highlights={hook1Highlights}
-import { createProductsWorkflow } from "@medusajs/medusa/core-flows"
-import { StepResponse } from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-import { LinkDefinition } from "@medusajs/framework/types"
-import { BRAND_MODULE } from "../../modules/brand"
-import BrandModuleService from "../../modules/brand/service"
-
-createProductsWorkflow.hooks.productsCreated(
- (async ({ products, additional_data }, { container }) => {
- if (!additional_data?.brand_id) {
- return new StepResponse([], [])
- }
+export type CreateBrandStepInput = {
+ name: string
+}
+export const createBrandStep = createStep(
+ "create-brand-step",
+ async (input: CreateBrandStepInput, { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
- // if the brand doesn't exist, an error is thrown.
- await brandModuleService.retrieveBrand(additional_data.brand_id as string)
- // TODO link brand to product
- })
+ const brand = await brandModuleService.createBrands(input)
+
+ return new StepResponse(brand, brand.id)
+ }
)
```
-Workflows have a special `hooks` property to access its hooks and consume them. Each hook, such as `productsCreated`, accepts a step function as a parameter. The step function accepts the following parameters:
+You create a `createBrandStep` using the `createStep` function. It accepts the step's unique name as a first parameter, and the step's function as a second parameter.
-1. An object having an `additional_data` property, which is the custom data passed in the request body under `additional_data`. The object will also have properties passed from the workflow to the hook, which in this case is the `products` property that holds an array of the created products.
-2. An object of properties related to the step's context. It has a `container` property whose value is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) to resolve Framework and commerce tools.
+The step function receives two parameters: input passed to the step when it's invoked, and an object of general context and configurations. This object has a `container` property, which is the Medusa container.
-In the step, if a brand ID is passed in `additional_data`, you resolve the Brand Module's service and use its generated `retrieveBrand` method to retrieve the brand by its ID. The `retrieveBrand` method will throw an error if the brand doesn't exist.
+The [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) is a registry of Framework and commerce tools accessible in your customizations, such as a workflow's step. The Medusa application registers the services of core and custom modules in the container, allowing you to resolve and use them.
-### Link Brand to Product
+So, In the step function, you use the Medusa container to resolve the Brand Module's service and use its generated `createBrands` method, which accepts an object of brands to create.
-Next, you want to create a link between the created products and the brand. To do so, you use Link, which is a class from the Modules SDK that provides methods to manage linked records.
+Learn more about the generated `create` method's usage in [this reference](https://docs.medusajs.com/resources/service-factory-reference/methods/create/index.html.md).
-Learn more about Link in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md).
+A step must return an instance of `StepResponse`. Its first parameter is the data returned by the step, and the second is the data passed to the compensation function, which you'll learn about next.
-To use Link in the `productsCreated` hook, replace the `TODO` with the following:
+### Add Compensation Function to Step
-```ts title="src/workflows/hooks/created-product.ts" highlights={hook2Highlights}
-const link = container.resolve("link")
-const logger = container.resolve("logger")
+You define for each step a compensation function that's executed when an error occurs in the workflow. The compensation function defines the logic to roll-back the changes made by the step. This ensures your data remains consistent if an error occurs, which is especially useful when you integrate third-party services.
-const links: LinkDefinition[] = []
+Learn more about the compensation function in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md).
-for (const product of products) {
- links.push({
- [Modules.PRODUCT]: {
- product_id: product.id,
- },
- [BRAND_MODULE]: {
- brand_id: additional_data.brand_id,
- },
- })
-}
+To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`:
-await link.create(links)
-
-logger.info("Linked brand to products")
-
-return new StepResponse(links, links)
-```
-
-You resolve Link from the container. Then you loop over the created products to assemble an array of links to be created. After that, you pass the array of links to Link's `create` method, which will link the product and brand records.
-
-Each property in the link object is the name of a module, and its value is an object having a `{model_name}_id` property, where `{model_name}` is the snake-case name of the module's data model. Its value is the ID of the record to be linked. The link object's properties must be set in the same order as the link configurations passed to `defineLink`.
-
-
-
-Finally, you return an instance of `StepResponse` returning the created links.
-
-### Dismiss Links in Compensation
-
-You can pass as a second parameter of the hook a compensation function that undoes what the step did. It receives as a first parameter the returned `StepResponse`'s second parameter, and the step context object as a second parameter.
-
-To undo creating the links in the hook, pass the following compensation function as a second parameter to `productsCreated`:
-
-```ts title="src/workflows/hooks/created-product.ts"
-createProductsWorkflow.hooks.productsCreated(
+```ts title="src/workflows/create-brand.ts"
+export const createBrandStep = createStep(
// ...
- (async (links, { container }) => {
- if (!links?.length) {
- return
- }
+ async (id: string, { container }) => {
+ const brandModuleService: BrandModuleService = container.resolve(
+ BRAND_MODULE
+ )
- const link = container.resolve("link")
-
- await link.dismiss(links)
- })
+ await brandModuleService.deleteBrands(id)
+ }
)
```
-In the compensation function, if the `links` parameter isn't empty, you resolve Link from the container and use its `dismiss` method. This method removes a link between two records. It accepts the same parameter as the `create` method.
+The compensation function's first parameter is the brand's ID which you passed as a second parameter to the step function's returned `StepResponse`. It also accepts a context object with a `container` property as a second parameter, similar to the step function.
+
+In the compensation function, you resolve the Brand Module's service from the Medusa container, then use its generated `deleteBrands` method to delete the brand created by the step. This method accepts the ID of the brand to delete.
+
+Learn more about the generated `delete` method's usage in [this reference](https://docs.medusajs.com/resources/service-factory-reference/methods/delete/index.html.md).
+
+So, if an error occurs during the workflow's execution, the brand that was created by the step is deleted to maintain data consistency.
***
-## 2. Configure Additional Data Validation
+## 2. Create createBrandWorkflow
-Now that you've consumed the `productsCreated` hook, you want to configure the `/admin/products` API route that creates a new product to accept a brand ID in its `additional_data` parameter.
+You can now create the workflow that runs the `createBrandStep`. A workflow is created in a TypeScript or JavaScript file under the `src/workflows` directory. In the file, you use `createWorkflow` from the Workflows SDK to create the workflow.
-You configure the properties accepted in `additional_data` in the `src/api/middlewares.ts` that exports middleware configurations. So, create the file (or, if already existing, add to the file) `src/api/middlewares.ts` the following content:
+Add the following content in the same `src/workflows/create-brand.ts` file:
-
-
-```ts title="src/api/middlewares.ts"
-import { defineMiddlewares } from "@medusajs/framework/http"
-import { z } from "zod"
+```ts title="src/workflows/create-brand.ts"
+// other imports...
+import {
+ // ...
+ createWorkflow,
+ WorkflowResponse,
+} from "@medusajs/framework/workflows-sdk"
// ...
-export default defineMiddlewares({
- routes: [
- // ...
- {
- matcher: "/admin/products",
- method: ["POST"],
- additionalDataValidator: {
- brand_id: z.string().optional(),
- },
- },
- ],
-})
+type CreateBrandWorkflowInput = {
+ name: string
+}
+
+export const createBrandWorkflow = createWorkflow(
+ "create-brand",
+ (input: CreateBrandWorkflowInput) => {
+ const brand = createBrandStep(input)
+
+ return new WorkflowResponse(brand)
+ }
+)
```
-Objects in `routes` accept an `additionalDataValidator` property that configures the validation rules for custom properties passed in the `additional_data` request parameter. It accepts an object whose keys are custom property names, and their values are validation rules created using [Zod](https://zod.dev/).
+You create the `createBrandWorkflow` using the `createWorkflow` function. This function accepts two parameters: the workflow's unique name, and the workflow's constructor function holding the workflow's implementation.
-So, `POST` requests sent to `/admin/products` can now pass the ID of a brand in the `brand_id` property of `additional_data`.
+The constructor function accepts the workflow's input as a parameter. In the function, you invoke the `createBrandStep` you created in the previous step to create a brand.
+
+A workflow must return an instance of `WorkflowResponse`. It accepts as a parameter the data to return to the workflow's executor.
***
-## Test it Out
+## Next Steps: Expose Create Brand API Route
-To test it out, first, retrieve the authentication token of your admin user by sending a `POST` request to `/auth/user/emailpass`:
+You now have a `createBrandWorkflow` that you can execute to create a brand.
-```bash
-curl -X POST 'http://localhost:9000/auth/user/emailpass' \
--H 'Content-Type: application/json' \
---data-raw '{
- "email": "admin@medusa-test.com",
- "password": "supersecret"
-}'
-```
-
-Make sure to replace the email and password in the request body with your user's credentials.
-
-Then, send a `POST` request to `/admin/products` to create a product, and pass in the `additional_data` parameter a brand's ID:
-
-```bash
-curl -X POST 'http://localhost:9000/admin/products' \
--H 'Content-Type: application/json' \
--H 'Authorization: Bearer {token}' \
---data '{
- "title": "Product 1",
- "options": [
- {
- "title": "Default option",
- "values": ["Default option value"]
- }
- ],
- "shipping_profile_id": "{shipping_profile_id}",
- "additional_data": {
- "brand_id": "{brand_id}"
- }
-}'
-```
-
-Make sure to replace `{token}` with the token you received from the previous request, `shipping_profile_id` with the ID of a shipping profile in your application, and `{brand_id}` with the ID of a brand in your application. You can retrieve the ID of a shipping profile either from the Medusa Admin, or the [List Shipping Profiles API route](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofiles).
-
-The request creates a product and returns it.
-
-In the Medusa application's logs, you'll find the message `Linked brand to products`, indicating that the workflow hook handler ran and linked the brand to the products.
-
-***
-
-## Next Steps: Query Linked Brands and Products
-
-Now that you've extending the create-product flow to link a brand to it, you want to retrieve the brand details of a product. You'll learn how to do so in the next chapter.
+In the next chapter, you'll add an API route that allows admin users to create a brand. You'll learn how to create the API route, and execute in it the workflow you implemented in this chapter.
# Create Brands UI Route in Admin
@@ -6057,224 +5845,6 @@ Your customizations often span across systems, where you need to retrieve data o
In the next chapters, you'll learn about the concepts that facilitate integrating third-party systems in your application. You'll integrate a dummy third-party system and sync the brands between it and the Medusa application.
-# Guide: Query Product's Brands
-
-In the previous chapters, you [defined a link](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) between the [custom Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md), then [extended the create-product flow](https://docs.medusajs.com/learn/customization/extend-features/extend-create-product/index.html.md) to link a product to a brand.
-
-In this chapter, you'll learn how to retrieve a product's brand (and vice-versa) in two ways: Using Medusa's existing API route, or in customizations, such as a custom API route.
-
-### Prerequisites
-
-- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
-- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md)
-
-***
-
-## Approach 1: Retrieve Brands in Existing API Routes
-
-Medusa's existing API routes accept a `fields` query parameter that allows you to specify the fields and relations of a model to retrieve. So, when you send a request to the [List Products](https://docs.medusajs.com/api/admin#products_getproducts), [Get Product](https://docs.medusajs.com/api/admin#products_getproductsid), or any product-related store or admin routes that accept a `fields` query parameter, you can specify in this parameter to return the product's brands.
-
-Learn more about selecting fields and relations in the [API Reference](https://docs.medusajs.com/api/admin#select-fields-and-relations).
-
-For example, send the following request to retrieve the list of products with their brands:
-
-```bash
-curl 'http://localhost:9000/admin/products?fields=+brand.*' \
---header 'Authorization: Bearer {token}'
-```
-
-Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](https://docs.medusajs.com/api/store#authentication).
-
-Any product that is linked to a brand will have a `brand` property in its object:
-
-```json title="Example Product Object"
-{
- "id": "prod_123",
- // ...
- "brand": {
- "id": "01JEB44M61BRM3ARM2RRMK7GJF",
- "name": "Acme",
- "created_at": "2024-12-05T09:59:08.737Z",
- "updated_at": "2024-12-05T09:59:08.737Z",
- "deleted_at": null
- }
-}
-```
-
-By using the `fields` query parameter, you don't have to re-create existing API routes to get custom data models that you linked to core data models.
-
-### Limitations: Filtering by Brands in Existing API Routes
-
-While you can retrieve linked records using the `fields` query parameter of an existing API route, you can't filter by linked records.
-
-Instead, you'll have to create a custom API route that uses Query to retrieve linked records with filters, as explained in the [Query documentation](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-filters-and-pagination-on-linked-records/index.html.md).
-
-***
-
-## Approach 2: Use Query to Retrieve Linked Records
-
-You can also retrieve linked records using Query. Query allows you to retrieve data across modules with filters, pagination, and more. You can resolve Query from the Medusa container and use it in your API route or workflow.
-
-Learn more about Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md).
-
-For example, you can create an API route that retrieves brands and their products. If you followed the [Create Brands API route chapter](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), you'll have the file `src/api/admin/brands/route.ts` with a `POST` API route. Add a new `GET` function to the same file:
-
-```ts title="src/api/admin/brands/route.ts" highlights={highlights}
-// other imports...
-import {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- const query = req.scope.resolve("query")
-
- const { data: brands } = await query.graph({
- entity: "brand",
- fields: ["*", "products.*"],
- })
-
- res.json({ brands })
-}
-```
-
-This adds a `GET` API route at `/admin/brands`. In the API route, you resolve Query from the Medusa container. Query has a `graph` method that runs a query to retrieve data. It accepts an object having the following properties:
-
-- `entity`: The data model's name as specified in the first parameter of `model.define`.
-- `fields`: An array of properties and relations to retrieve. You can pass:
- - A property's name, such as `id`, or `*` for all properties.
- - A relation or linked model's name, such as `products` (use the plural name since brands are linked to list of products). You suffix the name with `.*` to retrieve all its properties.
-
-`graph` returns an object having a `data` property, which is the retrieved brands. You return the brands in the response.
-
-### Test it Out
-
-To test the API route out, send a `GET` request to `/admin/brands`:
-
-```bash
-curl 'http://localhost:9000/admin/brands' \
--H 'Authorization: Bearer {token}'
-```
-
-Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](https://docs.medusajs.com/api/store#authentication).
-
-This returns the brands in your store with their linked products. For example:
-
-```json title="Example Response"
-{
- "brands": [
- {
- "id": "123",
- // ...
- "products": [
- {
- "id": "prod_123",
- // ...
- }
- ]
- }
- ]
-}
-```
-
-### Limitations: Filtering by Brand in Query
-
-While you can use Query to retrieve linked records, you can't filter by linked records.
-
-For an alternative approach, refer to the [Query documentation](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-filters-and-pagination-on-linked-records/index.html.md).
-
-***
-
-## Summary
-
-By following the examples of the previous chapters, you:
-
-- Defined a link between the Brand and Product modules's data models, allowing you to associate a product with a brand.
-- Extended the create-product workflow and route to allow setting the product's brand while creating the product.
-- Queried a product's brand, and vice versa.
-
-***
-
-## Next Steps: Customize Medusa Admin
-
-Clients, such as the Medusa Admin dashboard, can now use brand-related features, such as creating a brand or setting the brand of a product.
-
-In the next chapters, you'll learn how to customize the Medusa Admin to show a product's brand on its details page, and to show a new page with the list of brands in your store.
-
-
-# Guide: Define Module Link Between Brand and Product
-
-In this chapter, you'll learn how to define a module link between a brand defined in the [custom Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), and a product defined in the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) that's available in your Medusa application out-of-the-box.
-
-Modules are [isolated](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md) from other resources, ensuring that they're integrated into the Medusa application without side effects. However, you may need to associate data models of different modules, or you're trying to extend data models from commerce modules with custom properties. To do that, you define module links.
-
-A module link forms an association between two data models of different modules while maintaining module isolation. You can then manage and query linked records of the data models using Medusa's Modules SDK.
-
-In this chapter, you'll define a module link between the `Brand` data model of the Brand Module, and the `Product` data model of the Product Module. In later chapters, you'll manage and retrieve linked product and brand records.
-
-Learn more about module links in [this chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
-
-### Prerequisites
-
-- [Brand Module having a Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
-
-## 1. Define Link
-
-Links are defined in a TypeScript or JavaScript file under the `src/links` directory. The file defines and exports the link using `defineLink` from the Modules SDK.
-
-So, to define a link between the `Product` and `Brand` models, create the file `src/links/product-brand.ts` with the following content:
-
-
-
-```ts title="src/links/product-brand.ts" highlights={highlights}
-import BrandModule from "../modules/brand"
-import ProductModule from "@medusajs/medusa/product"
-import { defineLink } from "@medusajs/framework/utils"
-
-export default defineLink(
- {
- linkable: ProductModule.linkable.product,
- isList: true,
- },
- BrandModule.linkable.brand
-)
-```
-
-You import each module's definition object from the `index.ts` file of the module's directory. Each module object has a special `linkable` property that holds the data models' link configurations.
-
-The `defineLink` function accepts two parameters of the same type, which is either:
-
-- The data model's link configuration, which you access from the Module's `linkable` property;
-- Or an object that has two properties:
- - `linkable`: the data model's link configuration, which you access from the Module's `linkable` property.
- - `isList`: A boolean indicating whether many records of the data model can be linked to the other model.
-
-So, in the above code snippet, you define a link between the `Product` and `Brand` data models. Since a brand can be associated with multiple products, you enable `isList` in the `Product` model's object.
-
-***
-
-## 2. Sync the Link to the Database
-
-A module link is represented in the database as a table that stores the IDs of linked records. So, after defining the link, run the following command to create the module link's table in the database:
-
-```bash
-npx medusa db:migrate
-```
-
-This command reflects migrations on the database and syncs module links, which creates a table for the `product-brand` link.
-
-You can also run the `npx medusa db:sync-links` to just sync module links without running migrations.
-
-***
-
-## Next Steps: Extend Create Product Flow
-
-In the next chapter, you'll extend Medusa's workflow and API route that create a product to allow associating a brand with a product. You'll also learn how to link brand and product records.
-
-
# Guide: Add Product's Brand Widget in Admin
In this chapter, you'll customize the product details page of the Medusa Admin dashboard to show the product's [brand](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md). You'll create a widget that is injected into a pre-defined zone in the page, and in the widget you'll retrieve the product's brand from the server and display it.
@@ -6701,51 +6271,6 @@ info: API Key: "123"
You can also automate syncing data from a third-party system to Medusa at a regular interval. In the next chapter, you'll learn how to sync brands from the third-party CMS to Medusa once a day.
-# Admin Development Constraints
-
-This chapter lists some constraints of admin widgets and UI routes.
-
-## Arrow Functions
-
-Widget and UI route components must be created as arrow functions.
-
-```ts highlights={arrowHighlights}
-// Don't
-function ProductWidget() {
- // ...
-}
-
-// Do
-const ProductWidget = () => {
- // ...
-}
-```
-
-***
-
-## Widget Zone
-
-A widget zone's value must be wrapped in double or single quotes. It can't be a template literal or a variable.
-
-```ts highlights={zoneHighlights}
-// Don't
-export const config = defineWidgetConfig({
- zone: `product.details.before`,
-})
-
-// Don't
-const ZONE = "product.details.after"
-export const config = defineWidgetConfig({
- zone: ZONE,
-})
-
-// Do
-export const config = defineWidgetConfig({
- zone: "product.details.before",
-})
-```
-
-
# Guide: Schedule Syncing Brands from Third-Party
In the previous chapters, you've [integrated a third-party CMS](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md) and implemented the logic to [sync created brands](https://docs.medusajs.com/learn/customization/integrate-systems/handle-event/index.html.md) from Medusa to the CMS.
@@ -7214,101 +6739,515 @@ You can now use the CMS Module's service to perform actions on the third-party C
In the next chapter, you'll learn how to emit an event when a brand is created, then handle that event to sync the brand from Medusa to the third-party service.
-# Admin Development Tips
+# Guide: Define Module Link Between Brand and Product
-In this chapter, you'll find some tips for your admin development.
+In this chapter, you'll learn how to define a module link between a brand defined in the [custom Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md), and a product defined in the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) that's available in your Medusa application out-of-the-box.
-## Send Requests to API Routes
+Modules are [isolated](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md) from other resources, ensuring that they're integrated into the Medusa application without side effects. However, you may need to associate data models of different modules, or you're trying to extend data models from Commerce Modules with custom properties. To do that, you define module links.
-To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default.
+A module link forms an association between two data models of different modules while maintaining module isolation. You can then manage and query linked records of the data models using Medusa's Modules SDK.
-Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency.
+In this chapter, you'll define a module link between the `Brand` data model of the Brand Module, and the `Product` data model of the Product Module. In later chapters, you'll manage and retrieve linked product and brand records.
-First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations:
+Learn more about module links in [this chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
-```ts
-import Medusa from "@medusajs/js-sdk"
+### Prerequisites
-export const sdk = new Medusa({
- baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
- debug: import.meta.env.DEV,
- auth: {
- type: "session",
+- [Brand Module having a Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
+
+## 1. Define Link
+
+Links are defined in a TypeScript or JavaScript file under the `src/links` directory. The file defines and exports the link using `defineLink` from the Modules SDK.
+
+So, to define a link between the `Product` and `Brand` models, create the file `src/links/product-brand.ts` with the following content:
+
+
+
+```ts title="src/links/product-brand.ts" highlights={highlights}
+import BrandModule from "../modules/brand"
+import ProductModule from "@medusajs/medusa/product"
+import { defineLink } from "@medusajs/framework/utils"
+
+export default defineLink(
+ {
+ linkable: ProductModule.linkable.product,
+ isList: true,
},
+ BrandModule.linkable.brand
+)
+```
+
+You import each module's definition object from the `index.ts` file of the module's directory. Each module object has a special `linkable` property that holds the data models' link configurations.
+
+The `defineLink` function accepts two parameters of the same type, which is either:
+
+- The data model's link configuration, which you access from the Module's `linkable` property;
+- Or an object that has two properties:
+ - `linkable`: the data model's link configuration, which you access from the Module's `linkable` property.
+ - `isList`: A boolean indicating whether many records of the data model can be linked to the other model.
+
+So, in the above code snippet, you define a link between the `Product` and `Brand` data models. Since a brand can be associated with multiple products, you enable `isList` in the `Product` model's object.
+
+***
+
+## 2. Sync the Link to the Database
+
+A module link is represented in the database as a table that stores the IDs of linked records. So, after defining the link, run the following command to create the module link's table in the database:
+
+```bash
+npx medusa db:migrate
+```
+
+This command reflects migrations on the database and syncs module links, which creates a table for the `product-brand` link.
+
+You can also run the `npx medusa db:sync-links` to just sync module links without running migrations.
+
+***
+
+## Next Steps: Extend Create Product Flow
+
+In the next chapter, you'll extend Medusa's workflow and API route that create a product to allow associating a brand with a product. You'll also learn how to link brand and product records.
+
+
+# Guide: Extend Create Product Flow
+
+After linking the [custom Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md) in the [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md), you'll extend the create product workflow and API route to allow associating a brand with a product.
+
+Some API routes, including the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), accept an `additional_data` request body parameter. This parameter can hold custom data that's passed to the [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md) of the workflow executed in the API route, allowing you to consume those hooks and perform actions with the custom data.
+
+So, in this chapter, to extend the create product flow and associate a brand with a product, you will:
+
+- Consume the [productsCreated](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow#productsCreated/index.html.md) hook of the [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md), which is executed within the workflow after the product is created. You'll link the product with the brand passed in the `additional_data` parameter.
+- Extend the Create Product API route to allow passing a brand ID in `additional_data`.
+
+To learn more about the `additional_data` property and the API routes that accept additional data, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md).
+
+### Prerequisites
+
+- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
+- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md)
+
+***
+
+## 1. Consume the productsCreated Hook
+
+A workflow hook is a point in a workflow where you can inject a step to perform a custom functionality. Consuming a workflow hook allows you to extend the features of a workflow and, consequently, the API route that uses it.
+
+Learn more about the workflow hooks in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md).
+
+The [createProductsWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) used in the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts) has a `productsCreated` hook that runs after the product is created. You'll consume this hook to link the created product with the brand specified in the request parameters.
+
+To consume the `productsCreated` hook, create the file `src/workflows/hooks/created-product.ts` with the following content:
+
+
+
+```ts title="src/workflows/hooks/created-product.ts" highlights={hook1Highlights}
+import { createProductsWorkflow } from "@medusajs/medusa/core-flows"
+import { StepResponse } from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+import { LinkDefinition } from "@medusajs/framework/types"
+import { BRAND_MODULE } from "../../modules/brand"
+import BrandModuleService from "../../modules/brand/service"
+
+createProductsWorkflow.hooks.productsCreated(
+ (async ({ products, additional_data }, { container }) => {
+ if (!additional_data?.brand_id) {
+ return new StepResponse([], [])
+ }
+
+ const brandModuleService: BrandModuleService = container.resolve(
+ BRAND_MODULE
+ )
+ // if the brand doesn't exist, an error is thrown.
+ await brandModuleService.retrieveBrand(additional_data.brand_id as string)
+
+ // TODO link brand to product
+ })
+)
+```
+
+Workflows have a special `hooks` property to access its hooks and consume them. Each hook, such as `productsCreated`, accepts a step function as a parameter. The step function accepts the following parameters:
+
+1. An object having an `additional_data` property, which is the custom data passed in the request body under `additional_data`. The object will also have properties passed from the workflow to the hook, which in this case is the `products` property that holds an array of the created products.
+2. An object of properties related to the step's context. It has a `container` property whose value is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) to resolve Framework and commerce tools.
+
+In the step, if a brand ID is passed in `additional_data`, you resolve the Brand Module's service and use its generated `retrieveBrand` method to retrieve the brand by its ID. The `retrieveBrand` method will throw an error if the brand doesn't exist.
+
+### Link Brand to Product
+
+Next, you want to create a link between the created products and the brand. To do so, you use Link, which is a class from the Modules SDK that provides methods to manage linked records.
+
+Learn more about Link in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md).
+
+To use Link in the `productsCreated` hook, replace the `TODO` with the following:
+
+```ts title="src/workflows/hooks/created-product.ts" highlights={hook2Highlights}
+const link = container.resolve("link")
+const logger = container.resolve("logger")
+
+const links: LinkDefinition[] = []
+
+for (const product of products) {
+ links.push({
+ [Modules.PRODUCT]: {
+ product_id: product.id,
+ },
+ [BRAND_MODULE]: {
+ brand_id: additional_data.brand_id,
+ },
+ })
+}
+
+await link.create(links)
+
+logger.info("Linked brand to products")
+
+return new StepResponse(links, links)
+```
+
+You resolve Link from the container. Then you loop over the created products to assemble an array of links to be created. After that, you pass the array of links to Link's `create` method, which will link the product and brand records.
+
+Each property in the link object is the name of a module, and its value is an object having a `{model_name}_id` property, where `{model_name}` is the snake-case name of the module's data model. Its value is the ID of the record to be linked. The link object's properties must be set in the same order as the link configurations passed to `defineLink`.
+
+
+
+Finally, you return an instance of `StepResponse` returning the created links.
+
+### Dismiss Links in Compensation
+
+You can pass as a second parameter of the hook a compensation function that undoes what the step did. It receives as a first parameter the returned `StepResponse`'s second parameter, and the step context object as a second parameter.
+
+To undo creating the links in the hook, pass the following compensation function as a second parameter to `productsCreated`:
+
+```ts title="src/workflows/hooks/created-product.ts"
+createProductsWorkflow.hooks.productsCreated(
+ // ...
+ (async (links, { container }) => {
+ if (!links?.length) {
+ return
+ }
+
+ const link = container.resolve("link")
+
+ await link.dismiss(links)
+ })
+)
+```
+
+In the compensation function, if the `links` parameter isn't empty, you resolve Link from the container and use its `dismiss` method. This method removes a link between two records. It accepts the same parameter as the `create` method.
+
+***
+
+## 2. Configure Additional Data Validation
+
+Now that you've consumed the `productsCreated` hook, you want to configure the `/admin/products` API route that creates a new product to accept a brand ID in its `additional_data` parameter.
+
+You configure the properties accepted in `additional_data` in the `src/api/middlewares.ts` that exports middleware configurations. So, create the file (or, if already existing, add to the file) `src/api/middlewares.ts` the following content:
+
+
+
+```ts title="src/api/middlewares.ts"
+import { defineMiddlewares } from "@medusajs/framework/http"
+import { z } from "zod"
+
+// ...
+
+export default defineMiddlewares({
+ routes: [
+ // ...
+ {
+ matcher: "/admin/products",
+ method: ["POST"],
+ additionalDataValidator: {
+ brand_id: z.string().optional(),
+ },
+ },
+ ],
})
```
-Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md).
+Objects in `routes` accept an `additionalDataValidator` property that configures the validation rules for custom properties passed in the `additional_data` request parameter. It accepts an object whose keys are custom property names, and their values are validation rules created using [Zod](https://zod.dev/).
-Learn more about the JS SDK's configurations [this documentation](https://docs.medusajs.com/resources/js-sdk#js-sdk-configurations/index.html.md).
+So, `POST` requests sent to `/admin/products` can now pass the ID of a brand in the `brand_id` property of `additional_data`.
-Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests.
+***
+
+## Test it Out
+
+To test it out, first, retrieve the authentication token of your admin user by sending a `POST` request to `/auth/user/emailpass`:
+
+```bash
+curl -X POST 'http://localhost:9000/auth/user/emailpass' \
+-H 'Content-Type: application/json' \
+--data-raw '{
+ "email": "admin@medusa-test.com",
+ "password": "supersecret"
+}'
+```
+
+Make sure to replace the email and password in the request body with your user's credentials.
+
+Then, send a `POST` request to `/admin/products` to create a product, and pass in the `additional_data` parameter a brand's ID:
+
+```bash
+curl -X POST 'http://localhost:9000/admin/products' \
+-H 'Content-Type: application/json' \
+-H 'Authorization: Bearer {token}' \
+--data '{
+ "title": "Product 1",
+ "options": [
+ {
+ "title": "Default option",
+ "values": ["Default option value"]
+ }
+ ],
+ "shipping_profile_id": "{shipping_profile_id}",
+ "additional_data": {
+ "brand_id": "{brand_id}"
+ }
+}'
+```
+
+Make sure to replace `{token}` with the token you received from the previous request, `shipping_profile_id` with the ID of a shipping profile in your application, and `{brand_id}` with the ID of a brand in your application. You can retrieve the ID of a shipping profile either from the Medusa Admin, or the [List Shipping Profiles API route](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofiles).
+
+The request creates a product and returns it.
+
+In the Medusa application's logs, you'll find the message `Linked brand to products`, indicating that the workflow hook handler ran and linked the brand to the products.
+
+***
+
+## Next Steps: Query Linked Brands and Products
+
+Now that you've extending the create-product flow to link a brand to it, you want to retrieve the brand details of a product. You'll learn how to do so in the next chapter.
+
+
+# Guide: Query Product's Brands
+
+In the previous chapters, you [defined a link](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) between the [custom Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) and Medusa's [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md), then [extended the create-product flow](https://docs.medusajs.com/learn/customization/extend-features/extend-create-product/index.html.md) to link a product to a brand.
+
+In this chapter, you'll learn how to retrieve a product's brand (and vice-versa) in two ways: Using Medusa's existing API route, or in customizations, such as a custom API route.
+
+### Prerequisites
+
+- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)
+- [Defined link between the Brand and Product data models.](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md)
+
+***
+
+## Approach 1: Retrieve Brands in Existing API Routes
+
+Medusa's existing API routes accept a `fields` query parameter that allows you to specify the fields and relations of a model to retrieve. So, when you send a request to the [List Products](https://docs.medusajs.com/api/admin#products_getproducts), [Get Product](https://docs.medusajs.com/api/admin#products_getproductsid), or any product-related store or admin routes that accept a `fields` query parameter, you can specify in this parameter to return the product's brands.
+
+Learn more about selecting fields and relations in the [API Reference](https://docs.medusajs.com/api/admin#select-fields-and-relations).
+
+For example, send the following request to retrieve the list of products with their brands:
+
+```bash
+curl 'http://localhost:9000/admin/products?fields=+brand.*' \
+--header 'Authorization: Bearer {token}'
+```
+
+Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](https://docs.medusajs.com/api/store#authentication).
+
+Any product that is linked to a brand will have a `brand` property in its object:
+
+```json title="Example Product Object"
+{
+ "id": "prod_123",
+ // ...
+ "brand": {
+ "id": "01JEB44M61BRM3ARM2RRMK7GJF",
+ "name": "Acme",
+ "created_at": "2024-12-05T09:59:08.737Z",
+ "updated_at": "2024-12-05T09:59:08.737Z",
+ "deleted_at": null
+ }
+}
+```
+
+By using the `fields` query parameter, you don't have to re-create existing API routes to get custom data models that you linked to core data models.
+
+### Limitations: Filtering by Brands in Existing API Routes
+
+While you can retrieve linked records using the `fields` query parameter of an existing API route, you can't filter by linked records.
+
+Instead, you'll have to create a custom API route that uses Query to retrieve linked records with filters, as explained in the [Query documentation](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-filters-and-pagination-on-linked-records/index.html.md).
+
+***
+
+## Approach 2: Use Query to Retrieve Linked Records
+
+You can also retrieve linked records using Query. Query allows you to retrieve data across modules with filters, pagination, and more. You can resolve Query from the Medusa container and use it in your API route or workflow.
+
+Learn more about Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md).
+
+For example, you can create an API route that retrieves brands and their products. If you followed the [Create Brands API route chapter](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md), you'll have the file `src/api/admin/brands/route.ts` with a `POST` API route. Add a new `GET` function to the same file:
+
+```ts title="src/api/admin/brands/route.ts" highlights={highlights}
+// other imports...
+import {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ const query = req.scope.resolve("query")
+
+ const { data: brands } = await query.graph({
+ entity: "brand",
+ fields: ["*", "products.*"],
+ })
+
+ res.json({ brands })
+}
+```
+
+This adds a `GET` API route at `/admin/brands`. In the API route, you resolve Query from the Medusa container. Query has a `graph` method that runs a query to retrieve data. It accepts an object having the following properties:
+
+- `entity`: The data model's name as specified in the first parameter of `model.define`.
+- `fields`: An array of properties and relations to retrieve. You can pass:
+ - A property's name, such as `id`, or `*` for all properties.
+ - A relation or linked model's name, such as `products` (use the plural name since brands are linked to list of products). You suffix the name with `.*` to retrieve all its properties.
+
+`graph` returns an object having a `data` property, which is the retrieved brands. You return the brands in the response.
+
+### Test it Out
+
+To test the API route out, send a `GET` request to `/admin/brands`:
+
+```bash
+curl 'http://localhost:9000/admin/brands' \
+-H 'Authorization: Bearer {token}'
+```
+
+Make sure to replace `{token}` with your admin user's authentication token. Learn how to retrieve it in the [API reference](https://docs.medusajs.com/api/store#authentication).
+
+This returns the brands in your store with their linked products. For example:
+
+```json title="Example Response"
+{
+ "brands": [
+ {
+ "id": "123",
+ // ...
+ "products": [
+ {
+ "id": "prod_123",
+ // ...
+ }
+ ]
+ }
+ ]
+}
+```
+
+### Limitations: Filtering by Brand in Query
+
+While you can use Query to retrieve linked records, you can't filter by linked records.
+
+For an alternative approach, refer to the [Query documentation](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-filters-and-pagination-on-linked-records/index.html.md).
+
+***
+
+## Summary
+
+By following the examples of the previous chapters, you:
+
+- Defined a link between the Brand and Product modules's data models, allowing you to associate a product with a brand.
+- Extended the create-product workflow and route to allow setting the product's brand while creating the product.
+- Queried a product's brand, and vice versa.
+
+***
+
+## Next Steps: Customize Medusa Admin
+
+Clients, such as the Medusa Admin dashboard, can now use brand-related features, such as creating a brand or setting the brand of a product.
+
+In the next chapters, you'll learn how to customize the Medusa Admin to show a product's brand on its details page, and to show a new page with the list of brands in your store.
+
+
+# Admin Development Constraints
+
+This chapter lists some constraints of admin widgets and UI routes.
+
+## Arrow Functions
+
+Widget and UI route components must be created as arrow functions.
+
+```ts highlights={arrowHighlights}
+// Don't
+function ProductWidget() {
+ // ...
+}
+
+// Do
+const ProductWidget = () => {
+ // ...
+}
+```
+
+***
+
+## Widget Zone
+
+A widget zone's value must be wrapped in double or single quotes. It can't be a template literal or a variable.
+
+```ts highlights={zoneHighlights}
+// Don't
+export const config = defineWidgetConfig({
+ zone: `product.details.before`,
+})
+
+// Don't
+const ZONE = "product.details.after"
+export const config = defineWidgetConfig({
+ zone: ZONE,
+})
+
+// Do
+export const config = defineWidgetConfig({
+ zone: "product.details.before",
+})
+```
+
+
+# Environment Variables in Admin Customizations
+
+In this chapter, you'll learn how to use environment variables in your admin customizations.
+
+To learn how environment variables are generally loaded in Medusa based on your application's environment, check out [this chapter](https://docs.medusajs.com/learn/fundamentals/environment-variables/index.html.md).
+
+## How to Set Environment Variables
+
+The Medusa Admin is built on top of [Vite](https://vite.dev/). To set an environment variable that you want to use in a widget or UI route, prefix the environment variable with `VITE_`.
For example:
-### Query
-
-```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights}
-import { defineWidgetConfig } from "@medusajs/admin-sdk"
-import { Button, Container } from "@medusajs/ui"
-import { useQuery } from "@tanstack/react-query"
-import { sdk } from "../lib/config"
-import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
-
-const ProductWidget = () => {
- const { data, isLoading } = useQuery({
- queryFn: () => sdk.admin.product.list(),
- queryKey: ["products"],
- })
-
- return (
-
- {isLoading && Loading...}
- {data?.products && (
-
- {data.products.map((product) => (
-
{product.title}
- ))}
-
- )}
-
- )
-}
-
-export const config = defineWidgetConfig({
- zone: "product.list.before",
-})
-
-export default ProductWidget
+```plain
+VITE_MY_API_KEY=sk_123
```
-### Mutation
+***
-```tsx title="src/admin/widgets/product-widget.ts" highlights={mutationHighlights}
+## How to Use Environment Variables
+
+To access or use an environment variable starting with `VITE_`, use the `import.meta.env` object.
+
+For example:
+
+```tsx highlights={[["8"]]}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
-import { Button, Container } from "@medusajs/ui"
-import { useMutation } from "@tanstack/react-query"
-import { sdk } from "../lib/config"
-import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
+import { Container, Heading } from "@medusajs/ui"
-const ProductWidget = ({
- data: productData,
-}: DetailWidgetProps) => {
- const { mutateAsync } = useMutation({
- mutationFn: (payload: HttpTypes.AdminUpdateProduct) =>
- sdk.admin.product.update(productData.id, payload),
- onSuccess: () => alert("updated product"),
- })
-
- const handleUpdate = () => {
- mutateAsync({
- title: "New Product Title",
- })
- }
-
+const ProductWidget = () => {
return (
-
+
+ API Key: {import.meta.env.VITE_MY_API_KEY}
+
)
}
@@ -7320,29 +7259,36 @@ export const config = defineWidgetConfig({
export default ProductWidget
```
-You can also send requests to custom routes as explained in the [JS SDK reference](https://docs.medusajs.com/resources/js-sdk/index.html.md).
+In this example, you display the API key in a widget using `import.meta.env.VITE_MY_API_KEY`.
-### Use Route Loaders for Initial Data
+### Type Error on import.meta.env
-You may need to retrieve data before your component is rendered, or you may need to pass some initial data to your component to be used while data is being fetched. In those cases, you can use a [route loader](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md).
+If you receive a type error on `import.meta.env`, create the file `src/admin/vite-env.d.ts` with the following content:
+
+```ts title="src/admin/vite-env.d.ts"
+///
+```
+
+This file tells TypeScript to recognize the `import.meta.env` object and enhances the types of your custom environment variables.
***
-## Global Variables in Admin Customizations
+## Check Node Environment in Admin Customizations
-In your admin customizations, you can use the following global variables:
+To check the current environment, Vite exposes two variables:
-- `__BASE__`: The base path of the Medusa Admin, as set in the [admin.path](https://docs.medusajs.com/learn/configurations/medusa-config#path/index.html.md) configuration in `medusa-config.ts`.
-- `__BACKEND_URL__`: The URL to the Medusa backend, as set in the [admin.backendUrl](https://docs.medusajs.com/learn/configurations/medusa-config#backendurl/index.html.md) configuration in `medusa-config.ts`.
-- `__STOREFRONT_URL__`: The URL to the storefront, as set in the [admin.storefrontUrl](https://docs.medusajs.com/learn/configurations/medusa-config#storefrontUrl/index.html.md) configuration in `medusa-config.ts`.
+- `import.meta.env.DEV`: Returns `true` if the current environment is development.
+- `import.meta.env.PROD`: Returns `true` if the current environment is production.
+
+Learn more about other Vite environment variables in the [Vite documentation](https://vite.dev/guide/env-and-mode).
***
-## Admin Translations
+## Environment Variables in Production
-The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions.
+When you build the Medusa application, including the Medusa Admin, with the `build` command, the environment variables are inlined into the build. This means that you can't change the environment variables without rebuilding the application.
-Learn how to add a new language translation for the Medusa Admin in [this guide](https://docs.medusajs.com/learn/resources/contribution-guidelines/admin-translations/index.html.md).
+For example, the `VITE_MY_API_KEY` environment variable in the example above will be replaced with the actual value during the build process.
# Admin Routing Customizations
@@ -7498,40 +7444,101 @@ export const handle = {
Refer to [react-router-dom’s documentation](https://reactrouter.com/en/6.29.0) for components and hooks that you can use in your admin customizations.
-# Environment Variables in Admin Customizations
+# Admin Development Tips
-In this chapter, you'll learn how to use environment variables in your admin customizations.
+In this chapter, you'll find some tips for your admin development.
-To learn how environment variables are generally loaded in Medusa based on your application's environment, check out [this chapter](https://docs.medusajs.com/learn/fundamentals/environment-variables/index.html.md).
+## Send Requests to API Routes
-## How to Set Environment Variables
+To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default.
-The Medusa Admin is built on top of [Vite](https://vite.dev/). To set an environment variable that you want to use in a widget or UI route, prefix the environment variable with `VITE_`.
+Do not install Tanstack Query as that will cause unexpected errors in your development. If you prefer installing it for better auto-completion in your code editor, make sure to install `v5.64.2` as a development dependency.
-For example:
+First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations:
-```plain
-VITE_MY_API_KEY=sk_123
+```ts
+import Medusa from "@medusajs/js-sdk"
+
+export const sdk = new Medusa({
+ baseUrl: import.meta.env.VITE_BACKEND_URL || "/",
+ debug: import.meta.env.DEV,
+ auth: {
+ type: "session",
+ },
+})
```
-***
+Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md).
-## How to Use Environment Variables
+Learn more about the JS SDK's configurations [this documentation](https://docs.medusajs.com/resources/js-sdk#js-sdk-configurations/index.html.md).
-To access or use an environment variable starting with `VITE_`, use the `import.meta.env` object.
+Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests.
For example:
-```tsx highlights={[["8"]]}
+### Query
+
+```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights}
import { defineWidgetConfig } from "@medusajs/admin-sdk"
-import { Container, Heading } from "@medusajs/ui"
+import { Button, Container } from "@medusajs/ui"
+import { useQuery } from "@tanstack/react-query"
+import { sdk } from "../lib/config"
+import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
const ProductWidget = () => {
+ const { data, isLoading } = useQuery({
+ queryFn: () => sdk.admin.product.list(),
+ queryKey: ["products"],
+ })
+
return (
-
+ )}
+
+ )
+}
+
+export const config = defineWidgetConfig({
+ zone: "product.list.before",
+})
+
+export default ProductWidget
+```
+
+### Mutation
+
+```tsx title="src/admin/widgets/product-widget.ts" highlights={mutationHighlights}
+import { defineWidgetConfig } from "@medusajs/admin-sdk"
+import { Button, Container } from "@medusajs/ui"
+import { useMutation } from "@tanstack/react-query"
+import { sdk } from "../lib/config"
+import { DetailWidgetProps, HttpTypes } from "@medusajs/framework/types"
+
+const ProductWidget = ({
+ data: productData,
+}: DetailWidgetProps) => {
+ const { mutateAsync } = useMutation({
+ mutationFn: (payload: HttpTypes.AdminUpdateProduct) =>
+ sdk.admin.product.update(productData.id, payload),
+ onSuccess: () => alert("updated product"),
+ })
+
+ const handleUpdate = () => {
+ mutateAsync({
+ title: "New Product Title",
+ })
+ }
+
+ return (
+
+
)
}
@@ -7543,36 +7550,29 @@ export const config = defineWidgetConfig({
export default ProductWidget
```
-In this example, you display the API key in a widget using `import.meta.env.VITE_MY_API_KEY`.
+You can also send requests to custom routes as explained in the [JS SDK reference](https://docs.medusajs.com/resources/js-sdk/index.html.md).
-### Type Error on import.meta.env
+### Use Route Loaders for Initial Data
-If you receive a type error on `import.meta.env`, create the file `src/admin/vite-env.d.ts` with the following content:
-
-```ts title="src/admin/vite-env.d.ts"
-///
-```
-
-This file tells TypeScript to recognize the `import.meta.env` object and enhances the types of your custom environment variables.
+You may need to retrieve data before your component is rendered, or you may need to pass some initial data to your component to be used while data is being fetched. In those cases, you can use a [route loader](https://docs.medusajs.com/learn/fundamentals/admin/routing/index.html.md).
***
-## Check Node Environment in Admin Customizations
+## Global Variables in Admin Customizations
-To check the current environment, Vite exposes two variables:
+In your admin customizations, you can use the following global variables:
-- `import.meta.env.DEV`: Returns `true` if the current environment is development.
-- `import.meta.env.PROD`: Returns `true` if the current environment is production.
-
-Learn more about other Vite environment variables in the [Vite documentation](https://vite.dev/guide/env-and-mode).
+- `__BASE__`: The base path of the Medusa Admin, as set in the [admin.path](https://docs.medusajs.com/learn/configurations/medusa-config#path/index.html.md) configuration in `medusa-config.ts`.
+- `__BACKEND_URL__`: The URL to the Medusa backend, as set in the [admin.backendUrl](https://docs.medusajs.com/learn/configurations/medusa-config#backendurl/index.html.md) configuration in `medusa-config.ts`.
+- `__STOREFRONT_URL__`: The URL to the storefront, as set in the [admin.storefrontUrl](https://docs.medusajs.com/learn/configurations/medusa-config#storefrontUrl/index.html.md) configuration in `medusa-config.ts`.
***
-## Environment Variables in Production
+## Admin Translations
-When you build the Medusa application, including the Medusa Admin, with the `build` command, the environment variables are inlined into the build. This means that you can't change the environment variables without rebuilding the application.
+The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions.
-For example, the `VITE_MY_API_KEY` environment variable in the example above will be replaced with the actual value during the build process.
+Learn how to add a new language translation for the Medusa Admin in [this guide](https://docs.medusajs.com/learn/resources/contribution-guidelines/admin-translations/index.html.md).
# Admin UI Routes
@@ -7930,116 +7930,1379 @@ Refer to [this reference](https://docs.medusajs.com/resources/admin-widget-injec
To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](https://docs.medusajs.com/resources/admin-components/index.html.md) to find common components.
-# Handling CORS in API Routes
+# Seed Data with Custom CLI Script
-In this chapter, you’ll learn about the CORS middleware and how to configure it for custom API routes.
+In this chapter, you'll learn how to seed data using a custom CLI script.
-## CORS Overview
+## How to Seed Data
-Cross-Origin Resource Sharing (CORS) allows only configured origins to access your API Routes.
+To seed dummy data for development or demo purposes, use a custom CLI script.
-For example, if you allow only origins starting with `http://localhost:7001` to access your Admin API Routes, other origins accessing those routes get a CORS error.
+In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md), to seed data.
-### CORS Configurations
+### Example: Seed Dummy Products
-The `storeCors` and `adminCors` properties of Medusa's `http` configuration set the allowed origins for routes starting with `/store` and `/admin` respectively.
+In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products.
-These configurations accept a URL pattern to identify allowed origins.
+First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script:
+
+```bash npm2yarn
+npm install --save-dev @faker-js/faker
+```
+
+Then, create the file `src/scripts/demo-products.ts` with the following content:
+
+```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
+import { ExecArgs } from "@medusajs/framework/types"
+import { faker } from "@faker-js/faker"
+import {
+ ContainerRegistrationKeys,
+ Modules,
+ ProductStatus,
+} from "@medusajs/framework/utils"
+import {
+ createInventoryLevelsWorkflow,
+ createProductsWorkflow,
+} from "@medusajs/medusa/core-flows"
+
+export default async function seedDummyProducts({
+ container,
+}: ExecArgs) {
+ const salesChannelModuleService = container.resolve(
+ Modules.SALES_CHANNEL
+ )
+ const logger = container.resolve(
+ ContainerRegistrationKeys.LOGGER
+ )
+ const query = container.resolve(
+ ContainerRegistrationKeys.QUERY
+ )
+
+ const defaultSalesChannel = await salesChannelModuleService
+ .listSalesChannels({
+ name: "Default Sales Channel",
+ })
+
+ const sizeOptions = ["S", "M", "L", "XL"]
+ const colorOptions = ["Black", "White"]
+ const currency_code = "eur"
+ const productsNum = 50
+
+ // TODO seed products
+}
+```
+
+So far, in the script, you:
+
+- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in.
+- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products.
+- Initialize some default data to use when seeding the products next.
+
+Next, replace the `TODO` with the following:
+
+```ts title="src/scripts/demo-products.ts"
+const productsData = new Array(productsNum).fill(0).map((_, index) => {
+ const title = faker.commerce.product() + "_" + index
+ return {
+ title,
+ is_giftcard: true,
+ description: faker.commerce.productDescription(),
+ status: ProductStatus.PUBLISHED,
+ options: [
+ {
+ title: "Size",
+ values: sizeOptions,
+ },
+ {
+ title: "Color",
+ values: colorOptions,
+ },
+ ],
+ images: [
+ {
+ url: faker.image.urlPlaceholder({
+ text: title,
+ }),
+ },
+ {
+ url: faker.image.urlPlaceholder({
+ text: title,
+ }),
+ },
+ ],
+ variants: new Array(10).fill(0).map((_, variantIndex) => ({
+ title: `${title} ${variantIndex}`,
+ sku: `variant-${variantIndex}${index}`,
+ prices: new Array(10).fill(0).map((_, priceIndex) => ({
+ currency_code,
+ amount: 10 * priceIndex,
+ })),
+ options: {
+ Size: sizeOptions[Math.floor(Math.random() * 3)],
+ },
+ })),
+ shipping_profile_id: "sp_123",
+ sales_channels: [
+ {
+ id: defaultSalesChannel[0].id,
+ },
+ ],
+ }
+})
+
+// TODO seed products
+```
+
+You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images.
+
+Then, replace the new `TODO` with the following:
+
+```ts title="src/scripts/demo-products.ts"
+const { result: products } = await createProductsWorkflow(container).run({
+ input: {
+ products: productsData,
+ },
+})
+
+logger.info(`Seeded ${products.length} products.`)
+
+// TODO add inventory levels
+```
+
+You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products.
+
+Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following:
+
+```ts title="src/scripts/demo-products.ts"
+logger.info("Seeding inventory levels.")
+
+const { data: stockLocations } = await query.graph({
+ entity: "stock_location",
+ fields: ["id"],
+})
+
+const { data: inventoryItems } = await query.graph({
+ entity: "inventory_item",
+ fields: ["id"],
+})
+
+const inventoryLevels = inventoryItems.map((inventoryItem) => ({
+ location_id: stockLocations[0].id,
+ stocked_quantity: 1000000,
+ inventory_item_id: inventoryItem.id,
+}))
+
+await createInventoryLevelsWorkflow(container).run({
+ input: {
+ inventory_levels: inventoryLevels,
+ },
+})
+
+logger.info("Finished seeding inventory levels data.")
+```
+
+You use Query to retrieve the stock location, to use the first location in the application, and the inventory items.
+
+Then, you generate inventory levels for each inventory item, associating it with the first stock location.
+
+Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels.
+
+### Test Script
+
+To test out the script, run the following command in your project's directory:
+
+```bash
+npx medusa exec ./src/scripts/demo-products.ts
+```
+
+This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products.
+
+
+# Data Model Database Index
+
+In this chapter, you’ll learn how to define a database index on a data model.
+
+You can also define an index on a property as explained in the [Properties chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#define-database-index-on-property/index.html.md).
+
+## Define Database Index on Data Model
+
+A data model has an `indexes` method that defines database indices on its properties.
+
+The index can be on multiple columns (composite index). For example:
+
+```ts highlights={dataModelIndexHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const MyCustom = model.define("my_custom", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ age: model.number(),
+}).indexes([
+ {
+ on: ["name", "age"],
+ },
+])
+
+export default MyCustom
+```
+
+The `indexes` method receives an array of indices as a parameter. Each index is an object with a required `on` property indicating the properties to apply the index on.
+
+In the above example, you define a composite index on the `name` and `age` properties.
+
+### Index Conditions
+
+An index can have conditions. For example:
+
+```ts highlights={conditionHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const MyCustom = model.define("my_custom", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ age: model.number(),
+}).indexes([
+ {
+ on: ["name", "age"],
+ where: {
+ age: 30,
+ },
+ },
+])
+
+export default MyCustom
+```
+
+The index object passed to `indexes` accepts a `where` property whose value is an object of conditions. The object's key is a property's name, and its value is the condition on that property.
+
+In the example above, the composite index is created on the `name` and `age` properties when the `age`'s value is `30`.
+
+A property's condition can be a negation. For example:
+
+```ts highlights={negationHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const MyCustom = model.define("my_custom", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ age: model.number().nullable(),
+}).indexes([
+ {
+ on: ["name", "age"],
+ where: {
+ age: {
+ $ne: null,
+ },
+ },
+ },
+])
+
+export default MyCustom
+```
+
+A property's value in `where` can be an object having a `$ne` property. `$ne`'s value indicates what the specified property's value shouldn't be.
+
+In the example above, the composite index is created on the `name` and `age` properties when `age`'s value is not `null`.
+
+### Unique Database Index
+
+The object passed to `indexes` accepts a `unique` property indicating that the created index must be a unique index.
For example:
-```js title="medusa-config.ts"
-module.exports = defineConfig({
- projectConfig: {
- http: {
- storeCors: "http://localhost:8000",
- adminCors: "http://localhost:7001",
- // ...
- },
+```ts highlights={uniqueHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const MyCustom = model.define("my_custom", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+ age: model.number(),
+}).indexes([
+ {
+ on: ["name", "age"],
+ unique: true,
},
+])
+
+export default MyCustom
+```
+
+This creates a unique composite index on the `name` and `age` properties.
+
+
+# Infer Type of Data Model
+
+In this chapter, you'll learn how to infer the type of a data model.
+
+## How to Infer Type of Data Model?
+
+Consider you have a `Post` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable.
+
+Instead, Medusa provides `InferTypeOf` that transforms your data model to a type.
+
+For example:
+
+```ts
+import { InferTypeOf } from "@medusajs/framework/types"
+import { Post } from "../modules/blog/models/post" // relative path to the model
+
+export type Post = InferTypeOf
+```
+
+`InferTypeOf` accepts as a type argument the type of the data model.
+
+Since the `Post` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`.
+
+You can now use the `Post` type to reference a data model in other types, such as in workflow inputs or service method outputs:
+
+```ts title="Example Service"
+// other imports...
+import { InferTypeOf } from "@medusajs/framework/types"
+import { Post } from "../models/post"
+
+type Post = InferTypeOf
+
+class BlogModuleService extends MedusaService({ Post }) {
+ async doSomething(): Promise {
+ // ...
+ }
+}
+```
+
+
+# Add Data Model Check Constraints
+
+In this chapter, you'll learn how to add check constraints to your data model.
+
+## What is a Check Constraint?
+
+A check constraint is a condition that must be satisfied by records inserted into a database table, otherwise an error is thrown.
+
+For example, if you have a data model with a `price` property, you want to only allow positive number values. So, you add a check constraint that fails when inserting a record with a negative price value.
+
+***
+
+## How to Set a Check Constraint?
+
+To set check constraints on a data model, use the `checks` method. This method accepts an array of check constraints to apply on the data model.
+
+For example, to set a check constraint on a `price` property that ensures its value can only be a positive number:
+
+```ts highlights={checks1Highlights}
+import { model } from "@medusajs/framework/utils"
+
+const CustomProduct = model.define("custom_product", {
+ // ...
+ price: model.bigNumber(),
+})
+.checks([
+ (columns) => `${columns.price} >= 0`,
+])
+```
+
+The item passed in the array parameter of `checks` can be a callback function that accepts as a parameter an object whose keys are the names of the properties in the data model schema, and values the respective column name in the database.
+
+The function returns a string indicating the [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). In the expression, use the `columns` parameter to access a property's column name.
+
+You can also pass an object to the `checks` method:
+
+```ts highlights={checks2Highlights}
+import { model } from "@medusajs/framework/utils"
+
+const CustomProduct = model.define("custom_product", {
+ // ...
+ price: model.bigNumber(),
+})
+.checks([
+ {
+ name: "custom_product_price_check",
+ expression: (columns) => `${columns.price} >= 0`,
+ },
+])
+```
+
+The object accepts the following properties:
+
+- `name`: The check constraint's name.
+- `expression`: A function similar to the one that can be passed to the array. It accepts an object of columns and returns an [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS).
+
+***
+
+## Apply in Migrations
+
+After adding the check constraint, make sure to generate and run migrations if you already have the table in the database. Otherwise, the check constraint won't be reflected.
+
+To generate a migration for the data model's module then reflect it on the database, run the following command:
+
+```bash
+npx medusa db:generate custom_module
+npx medusa db:migrate
+```
+
+The first command generates the migration under the `migrations` directory of your module's directory, and the second reflects it on the database.
+
+
+# Manage Relationships
+
+In this chapter, you'll learn how to manage relationships between data models when creating, updating, or retrieving records using the module's main service.
+
+## Manage One-to-One Relationship
+
+### BelongsTo Side of One-to-One
+
+When you create a record of a data model that belongs to another through a one-to-one relation, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
+
+For example, assuming you have the [User and Email data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-one-relationship/index.html.md), set an email's user ID as follows:
+
+```ts highlights={belongsHighlights}
+// when creating an email
+const email = await helloModuleService.createEmails({
+ // other properties...
+ user_id: "123",
+})
+
+// when updating an email
+const email = await helloModuleService.updateEmails({
+ id: "321",
+ // other properties...
+ user_id: "123",
})
```
-This allows the `http://localhost:7001` origin to access the Admin API Routes, and the `http://localhost:8000` origin to access Store API Routes.
+In the example above, you pass the `user_id` property when creating or updating an email to specify the user it belongs to.
-Learn more about the CORS configurations in [this resource guide](https://docs.medusajs.com/learn/configurations/medusa-config#http/index.html.md).
+### HasOne Side
-***
+When you create a record of a data model that has one of another, pass the ID of the other data model's record in the relation property.
-## CORS in Store and Admin Routes
+For example, assuming you have the [User and Email data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-one-relationship/index.html.md), set a user's email ID as follows:
-To disable the CORS middleware for a route, export a `CORS` variable in the route file with its value set to `false`.
+```ts highlights={hasOneHighlights}
+// when creating a user
+const user = await helloModuleService.createUsers({
+ // other properties...
+ email: "123",
+})
-For example:
-
-```ts title="src/api/store/custom/route.ts" highlights={[["15"]]}
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: "[GET] Hello world!",
- })
-}
-
-export const CORS = false
+// when updating a user
+const user = await helloModuleService.updateUsers({
+ id: "321",
+ // other properties...
+ email: "123",
+})
```
-This disables the CORS middleware on API Routes at the path `/store/custom`.
+In the example above, you pass the `email` property when creating or updating a user to specify the email it has.
***
-## CORS in Custom Routes
+## Manage One-to-Many Relationship
-If you create a route that doesn’t start with `/store` or `/admin`, you must apply the CORS middleware manually. Otherwise, all requests to your API route lead to a CORS error.
+In a one-to-many relationship, you can only manage the associations from the `belongsTo` side.
-You can do that in the exported middlewares configurations in `src/api/middlewares.ts`.
+When you create a record of the data model on the `belongsTo` side, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
-For example:
+For example, assuming you have the [Product and Store data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-many-relationship/index.html.md), set a product's store ID as follows:
-```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-10" expandButtonLabel="Show Imports"
-import { defineMiddlewares } from "@medusajs/framework/http"
-import type {
- MedusaNextFunction,
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { ConfigModule } from "@medusajs/framework/types"
-import { parseCorsOrigins } from "@medusajs/framework/utils"
-import cors from "cors"
+```ts highlights={manyBelongsHighlights}
+// when creating a product
+const product = await helloModuleService.createProducts({
+ // other properties...
+ store_id: "123",
+})
-export default defineMiddlewares({
- routes: [
- {
- matcher: "/custom*",
- middlewares: [
- (
- req: MedusaRequest,
- res: MedusaResponse,
- next: MedusaNextFunction
- ) => {
- const configModule: ConfigModule =
- req.scope.resolve("configModule")
+// when updating a product
+const product = await helloModuleService.updateProducts({
+ id: "321",
+ // other properties...
+ store_id: "123",
+})
+```
- return cors({
- origin: parseCorsOrigins(
- configModule.projectConfig.http.storeCors
- ),
- credentials: true,
- })(req, res, next)
- },
- ],
- },
+In the example above, you pass the `store_id` property when creating or updating a product to specify the store it belongs to.
+
+***
+
+## Manage Many-to-Many Relationship
+
+If your many-to-many relation is represented with a [pivotEntity](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), refer to [this section](#manage-many-to-many-relationship-with-pivotentity) instead.
+
+### Create Associations
+
+When you create a record of a data model that has a many-to-many relationship to another data model, pass an array of IDs of the other data model's records in the relation property.
+
+For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), set the association between products and orders as follows:
+
+```ts highlights={manyHighlights}
+// when creating a product
+const product = await helloModuleService.createProducts({
+ // other properties...
+ orders: ["123", "321"],
+})
+
+// when creating an order
+const order = await helloModuleService.createOrders({
+ id: "321",
+ // other properties...
+ products: ["123", "321"],
+})
+```
+
+In the example above, you pass the `orders` property when you create a product, and you pass the `products` property when you create an order.
+
+### Update Associations
+
+When you use the `update` methods generated by the service factory, you also pass an array of IDs as the relation property's value to add new associated records.
+
+However, this removes any existing associations to records whose IDs aren't included in the array.
+
+For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), you update the product's related orders as so:
+
+```ts
+const product = await helloModuleService.updateProducts({
+ id: "123",
+ // other properties...
+ orders: ["321"],
+})
+```
+
+If the product was associated with an order, and you don't include that order's ID in the `orders` array, the association between the product and order is removed.
+
+So, to add a new association without removing existing ones, retrieve the product first to pass its associated orders when updating the product:
+
+```ts highlights={updateAssociationHighlights}
+const product = await helloModuleService.retrieveProduct(
+ "123",
+ {
+ relations: ["orders"],
+ }
+)
+
+const updatedProduct = await helloModuleService.updateProducts({
+ id: product.id,
+ // other properties...
+ orders: [
+ ...product.orders.map((order) => order.id),
+ "321",
],
})
```
-This retrieves the configurations exported from `medusa-config.ts` and applies the `storeCors` to routes starting with `/custom`.
+This keeps existing associations between the product and orders, and adds a new one.
+
+***
+
+## Manage Many-to-Many Relationship with pivotEntity
+
+If your many-to-many relation is represented without a [pivotEntity](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), refer to [this section](#manage-many-to-many-relationship) instead.
+
+If you have a many-to-many relation with a `pivotEntity` specified, make sure to pass the data model representing the pivot table to [MedusaService](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that your module's service extends.
+
+For example, assuming you have the [Order, Product, and OrderProduct models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), add `OrderProduct` to `MedusaService`'s object parameter:
+
+```ts highlights={["4"]}
+class BlogModuleService extends MedusaService({
+ Order,
+ Product,
+ OrderProduct,
+}) {}
+```
+
+This will generate Create, Read, Update and Delete (CRUD) methods for the `OrderProduct` data model, which you can use to create relations between orders and products and manage the extra columns in the pivot table.
+
+For example:
+
+```ts
+// create order-product association
+const orderProduct = await blogModuleService.createOrderProducts({
+ order_id: "123",
+ product_id: "123",
+ metadata: {
+ test: true,
+ },
+})
+
+// update order-product association
+const orderProduct = await blogModuleService.updateOrderProducts({
+ id: "123",
+ metadata: {
+ test: false,
+ },
+})
+
+// delete order-product association
+await blogModuleService.deleteOrderProducts("123")
+```
+
+Since the `OrderProduct` data model belongs to the `Order` and `Product` data models, you can set its order and product as explained in the [one-to-many relationship section](#manage-one-to-many-relationship) using `order_id` and `product_id`.
+
+Refer to the [service factory reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) for a full list of generated methods and their usages.
+
+***
+
+## Retrieve Records of Relation
+
+The `list`, `listAndCount`, and `retrieve` methods of a module's main service accept as a second parameter an object of options.
+
+To retrieve the records associated with a data model's records through a relationship, pass in the second parameter object a `relations` property whose value is an array of relationship names.
+
+For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), you retrieve a product's orders as follows:
+
+```ts highlights={retrieveHighlights}
+const product = await blogModuleService.retrieveProducts(
+ "123",
+ {
+ relations: ["orders"],
+ }
+)
+```
+
+In the example above, the retrieved product has an `orders` property, whose value is an array of orders associated with the product.
+
+
+# Data Model Properties
+
+In this chapter, you'll learn about the different property types you can use in a data model and how to configure a data model's properties.
+
+## Data Model's Default Properties
+
+By default, Medusa creates the following properties for every data model:
+
+- `created_at`: A [dateTime](#dateTime) property that stores when a record of the data model was created.
+- `updated_at`: A [dateTime](#dateTime) property that stores when a record of the data model was updated.
+- `deleted_at`: A [dateTime](#dateTime) property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date.
+
+***
+
+## Property Types
+
+This section covers the different property types you can define in a data model's schema using the `model` methods.
+
+### id
+
+The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers.
+
+For example:
+
+```ts highlights={idHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ id: model.id(),
+ // ...
+})
+
+export default Post
+```
+
+### text
+
+The `text` method defines a string property.
+
+For example:
+
+```ts highlights={textHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ name: model.text(),
+ // ...
+})
+
+export default Post
+```
+
+### number
+
+The `number` method defines a number property.
+
+For example:
+
+```ts highlights={numberHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ age: model.number(),
+ // ...
+})
+
+export default Post
+```
+
+### float
+
+This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2).
+
+The `float` method defines a number property that allows for values with decimal places.
+
+Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber).
+
+For example:
+
+```ts highlights={floatHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ rating: model.float(),
+ // ...
+})
+
+export default Post
+```
+
+### bigNumber
+
+The `bigNumber` method defines a number property that expects large numbers, such as prices.
+
+Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float).
+
+For example:
+
+```ts highlights={bigNumberHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ price: model.bigNumber(),
+ // ...
+})
+
+export default Post
+```
+
+### boolean
+
+The `boolean` method defines a boolean property.
+
+For example:
+
+```ts highlights={booleanHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ hasAccount: model.boolean(),
+ // ...
+})
+
+export default Post
+```
+
+### enum
+
+The `enum` method defines a property whose value can only be one of the specified values.
+
+For example:
+
+```ts highlights={enumHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ color: model.enum(["black", "white"]),
+ // ...
+})
+
+export default Post
+```
+
+The `enum` method accepts an array of possible string values.
+
+### dateTime
+
+The `dateTime` method defines a timestamp property.
+
+For example:
+
+```ts highlights={dateTimeHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ date_of_birth: model.dateTime(),
+ // ...
+})
+
+export default Post
+```
+
+### json
+
+The `json` method defines a property whose value is a stringified JSON object.
+
+For example:
+
+```ts highlights={jsonHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ metadata: model.json(),
+ // ...
+})
+
+export default Post
+```
+
+### array
+
+The `array` method defines an array of strings property.
+
+For example:
+
+```ts highlights={arrHightlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ names: model.array(),
+ // ...
+})
+
+export default Post
+```
+
+### Properties Reference
+
+Refer to the [Data Model Language (DML) reference](https://docs.medusajs.com/resources/references/data-model/index.html.md) for a full reference of the properties.
+
+***
+
+## Set Primary Key Property
+
+To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method.
+
+For example:
+
+```ts highlights={highlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ id: model.id().primaryKey(),
+ // ...
+})
+
+export default Post
+```
+
+In the example above, the `id` property is defined as the data model's primary key.
+
+***
+
+## Property Default Value
+
+Use the `default` method on a property's definition to specify the default value of a property.
+
+For example:
+
+```ts highlights={defaultHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ color: model
+ .enum(["black", "white"])
+ .default("black"),
+ age: model
+ .number()
+ .default(0),
+ // ...
+})
+
+export default Post
+```
+
+In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`.
+
+***
+
+## Make Property Optional
+
+Use the `nullable` method to indicate that a property’s value can be `null`. This is useful when you want a property to be optional.
+
+For example:
+
+```ts highlights={nullableHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ price: model.bigNumber().nullable(),
+ // ...
+})
+
+export default Post
+```
+
+In the example above, the `price` property is configured to allow `null` values, making it optional.
+
+***
+
+## Unique Property
+
+The `unique` method indicates that a property’s value must be unique in the database through a unique index.
+
+For example:
+
+```ts highlights={uniqueHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const User = model.define("user", {
+ email: model.text().unique(),
+ // ...
+})
+
+export default User
+```
+
+In this example, multiple users can’t have the same email.
+
+***
+
+## Define Database Index on Property
+
+Use the `index` method on a property's definition to define a database index.
+
+For example:
+
+```ts highlights={dbIndexHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ id: model.id().primaryKey(),
+ name: model.text().index(
+ "IDX_MY_CUSTOM_NAME"
+ ),
+})
+
+export default Post
+```
+
+The `index` method optionally accepts the name of the index as a parameter.
+
+In this example, you define an index on the `name` property.
+
+***
+
+## Define a Searchable Property
+
+Methods generated by the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters.
+
+When the `q` filter is passed, the data model's searchable properties are queried to find matching records.
+
+Use the `searchable` method on a `text` property to indicate that it's searchable.
+
+For example:
+
+```ts highlights={searchableHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Post = model.define("post", {
+ title: model.text().searchable(),
+ // ...
+})
+
+export default Post
+```
+
+In this example, the `title` property is searchable.
+
+### Search Example
+
+If you pass a `q` filter to the `listPosts` method:
+
+```ts
+const posts = await blogModuleService.listPosts({
+ q: "New Products",
+})
+```
+
+This retrieves records that include `New Products` in their `title` property.
+
+
+# Migrations
+
+In this chapter, you'll learn what a migration is and how to generate a migration or write it manually.
+
+## What is a Migration?
+
+A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
+
+The migration's file has a class with two methods:
+
+- The `up` method reflects changes on the database.
+- The `down` method reverts the changes made in the `up` method.
+
+***
+
+## Generate Migration
+
+Instead of you writing the migration manually, the Medusa CLI tool provides a [db:generate](https://docs.medusajs.com/resources/medusa-cli/commands/db#dbgenerate/index.html.md) command to generate a migration for a modules' data models.
+
+For example, assuming you have a `blog` Module, you can generate a migration for it by running the following command:
+
+```bash
+npx medusa db:generate blog
+```
+
+This generates a migration file under the `migrations` directory of the Blog Module. You can then run it to reflect the changes in the database as mentioned in [this section](#run-the-migration).
+
+***
+
+## Write a Migration Manually
+
+You can also write migrations manually. To do that, create a file in the `migrations` directory of the module and in it, a class that has an `up` and `down` method. The class's name should be of the format `Migration{YEAR}{MONTH}{DAY}{HOUR}{MINUTE}.ts` to ensure migrations are ran in the correct order.
+
+For example:
+
+```ts title="src/modules/blog/migrations/Migration202507021059.ts"
+import { Migration } from "@mikro-orm/migrations"
+
+export class Migration202507021059 extends Migration {
+
+ async up(): Promise {
+ this.addSql("create table if not exists \"author\" (\"id\" text not null, \"name\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"author_pkey\" primary key (\"id\"));")
+ }
+
+ async down(): Promise {
+ this.addSql("drop table if exists \"author\" cascade;")
+ }
+
+}
+```
+
+The migration class in the file extends the `Migration` class imported from `@mikro-orm/migrations`. In the `up` and `down` method of the migration class, you use the `addSql` method provided by MikroORM's `Migration` class to run PostgreSQL syntax.
+
+In the example above, the `up` method creates the table `author`, and the `down` method drops the table if the migration is reverted.
+
+Refer to [MikroORM's documentation](https://mikro-orm.io/docs/migrations#migration-class) for more details on writing migrations.
+
+***
+
+## Run the Migration
+
+To run your migration, run the following command:
+
+This command also syncs module links. If you don't want that, use the `--skip-links` option.
+
+```bash
+npx medusa db:migrate
+```
+
+This reflects the changes in the database as implemented in the migration's `up` method.
+
+***
+
+## Rollback the Migration
+
+To rollback or revert the last migration you ran for a module, run the following command:
+
+```bash
+npx medusa db:rollback blog
+```
+
+This rolls back the last ran migration on the Blog Module.
+
+### Caution: Rollback Migration before Deleting
+
+If you need to delete a migration file, make sure to rollback the migration first. Otherwise, you might encounter issues when generating and running new migrations.
+
+For example, if you delete the migration of the Blog Module, then try to create a new one, Medusa will create a brand new migration that re-creates the tables or indices. If those are still in the database, you might encounter errors.
+
+So, always rollback the migration before deleting it.
+
+***
+
+## More Database Commands
+
+To learn more about the Medusa CLI's database commands, refer to [this CLI reference](https://docs.medusajs.com/resources/medusa-cli/commands/db/index.html.md).
+
+
+# Data Model Relationships
+
+In this chapter, you’ll learn how to define relationships between data models in your module.
+
+## What is a Relationship Property?
+
+A relationship property defines an association in the database between two models. It's created using the Data Model Language (DML) methods, such as `hasOne` or `belongsTo`.
+
+When you generate a migration for these data models, the migrations include foreign key columns or pivot tables, based on the relationship's type.
+
+You want to create a relation between data models in the same module.
+
+You want to create a relationship between data models in different modules. Use module links instead.
+
+***
+
+## One-to-One Relationship
+
+A one-to-one relationship indicates that one record of a data model belongs to or is associated with another.
+
+To define a one-to-one relationship, create relationship properties in the data models using the following methods:
+
+1. `hasOne`: indicates that the model has one record of the specified model.
+2. `belongsTo`: indicates that the model belongs to one record of the specified model.
+
+For example:
+
+```ts highlights={oneToOneHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const User = model.define("user", {
+ id: model.id().primaryKey(),
+ email: model.hasOne(() => Email),
+})
+
+const Email = model.define("email", {
+ id: model.id().primaryKey(),
+ user: model.belongsTo(() => User, {
+ mappedBy: "email",
+ }),
+})
+```
+
+In the example above, a user has one email, and an email belongs to one user.
+
+The `hasOne` and `belongsTo` methods accept a function as the first parameter. The function returns the associated data model.
+
+The `belongsTo` method also requires passing as a second parameter an object with the property `mappedBy`. Its value is the name of the relationship property in the other data model.
+
+### Optional Relationship
+
+To make the relationship optional on the `hasOne` or `belongsTo` side, use the `nullable` method on either property as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#make-property-optional/index.html.md).
+
+### One-sided One-to-One Relationship
+
+If the one-to-one relationship is only defined on one side, pass `undefined` to the `mappedBy` property in the `belongsTo` method.
+
+For example:
+
+```ts highlights={oneToOneUndefinedHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const User = model.define("user", {
+ id: model.id().primaryKey(),
+})
+
+const Email = model.define("email", {
+ id: model.id().primaryKey(),
+ user: model.belongsTo(() => User, {
+ mappedBy: undefined,
+ }),
+})
+```
+
+### One-to-One Relationship in the Database
+
+When you generate the migrations of data models that have a one-to-one relationship, the migration adds to the table of the data model that has the `belongsTo` property:
+
+1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `email` table will have a `user_id` column.
+2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
+
+
+
+***
+
+## One-to-Many Relationship
+
+A one-to-many relationship indicates that one record of a data model has many records of another data model.
+
+To define a one-to-many relationship, create relationship properties in the data models using the following methods:
+
+1. `hasMany`: indicates that the model has more than one record of the specified model.
+2. `belongsTo`: indicates that the model belongs to one record of the specified model.
+
+For example:
+
+```ts highlights={oneToManyHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Store = model.define("store", {
+ id: model.id().primaryKey(),
+ products: model.hasMany(() => Product),
+})
+
+const Product = model.define("product", {
+ id: model.id().primaryKey(),
+ store: model.belongsTo(() => Store, {
+ mappedBy: "products",
+ }),
+})
+```
+
+In this example, a store has many products, but a product belongs to one store.
+
+### Optional Relationship
+
+To make the relationship optional on the `belongsTo` side, use the `nullable` method on the property as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#make-property-optional/index.html.md).
+
+### One-to-Many Relationship in the Database
+
+When you generate the migrations of data models that have a one-to-many relationship, the migration adds to the table of the data model that has the `belongsTo` property:
+
+1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `product` table will have a `store_id` column.
+2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
+
+
+
+***
+
+## Many-to-Many Relationship
+
+A many-to-many relationship indicates that many records of a data model can be associated with many records of another data model.
+
+To define a many-to-many relationship, create relationship properties in the data models using the `manyToMany` method.
+
+For example:
+
+```ts highlights={manyToManyHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const Order = model.define("order", {
+ id: model.id().primaryKey(),
+ products: model.manyToMany(() => Product, {
+ mappedBy: "orders",
+ pivotTable: "order_product",
+ joinColumn: "order_id",
+ inverseJoinColumn: "product_id",
+ }),
+})
+
+const Product = model.define("product", {
+ id: model.id().primaryKey(),
+ orders: model.manyToMany(() => Order, {
+ mappedBy: "products",
+ }),
+})
+```
+
+The `manyToMany` method accepts two parameters:
+
+1. A function that returns the associated data model.
+2. An object of optional configuration. Only one of the data models in the relation can define the `pivotTable`, `joinColumn`, and `inverseJoinColumn` configurations, and it's considered the owner data model. The object can accept the following properties:
+ - `mappedBy`: The name of the relationship property in the other data model. If not set, the property's name is inferred from the associated data model's name.
+ - `pivotTable`: The name of the pivot table created in the database for the many-to-many relation. If not set, the pivot table is inferred by combining the names of the data models' tables in alphabetical order, separating them by `_`, and pluralizing the last name. For example, `order_products`.
+ - `joinColumn`: The name of the column in the pivot table that points to the owner model's primary key.
+ - `inverseJoinColumn`: The name of the column in the pivot table that points to the owned model's primary key.
+
+The `pivotTable`, `joinColumn`, and `inverseJoinColumn` properties are only available after [Medusa v2.0.7](https://github.com/medusajs/medusa/releases/tag/v2.0.7).
+
+Following [Medusa v2.1.0](https://github.com/medusajs/medusa/releases/tag/v2.1.0), if `pivotTable`, `joinColumn`, and `inverseJoinColumn` aren't specified on either model, the owner is decided based on alphabetical order. So, in the example above, the `Order` data model would be the owner.
+
+In this example, an order is associated with many products, and a product is associated with many orders. Since the `pivotTable`, `joinColumn`, and `inverseJoinColumn` configurations are defined on the order, it's considered the owner data model.
+
+### Many-to-Many Relationship in the Database
+
+When you generate the migrations of data models that have a many-to-many relationship, the migration adds a new pivot table. Its name is either the name you specify in the `pivotTable` configuration or the inferred name combining the names of the data models' tables in alphabetical order, separating them by `_`, and pluralizing the last name. For example, `order_products`.
+
+The pivot table has a column with the name `{data_model}_id` for each of the data model's tables. It also has foreign keys on each of these columns to their respective tables.
+
+The pivot table has columns with foreign keys pointing to the primary key of the associated tables. The column's name is either:
+
+- The value of the `joinColumn` configuration for the owner table, and the `inverseJoinColumn` configuration for the owned table;
+- Or the inferred name `{table_name}_id`.
+
+
+
+### Many-To-Many with Custom Columns
+
+To add custom columns to the pivot table between two data models having a many-to-many relationship, you must define a new data model that represents the pivot table.
+
+For example:
+
+```ts highlights={manyToManyColumnHighlights}
+import { model } from "@medusajs/framework/utils"
+
+export const Order = model.define("order_test", {
+ id: model.id().primaryKey(),
+ products: model.manyToMany(() => Product, {
+ pivotEntity: () => OrderProduct,
+ }),
+})
+
+export const Product = model.define("product_test", {
+ id: model.id().primaryKey(),
+ orders: model.manyToMany(() => Order),
+})
+
+export const OrderProduct = model.define("orders_products", {
+ id: model.id().primaryKey(),
+ order: model.belongsTo(() => Order, {
+ mappedBy: "products",
+ }),
+ product: model.belongsTo(() => Product, {
+ mappedBy: "orders",
+ }),
+ metadata: model.json().nullable(),
+})
+```
+
+The `Order` and `Product` data models have a many-to-many relationship. To add extra columns to the created pivot table, you pass a `pivotEntity` option to the `products` relation in `Order` (since `Order` is the owner). The value of `pivotEntity` is a function that returns the data model representing the pivot table.
+
+The `OrderProduct` model defines, aside from the ID, the following properties:
+
+- `order`: A relation that indicates this model belongs to the `Order` data model. You set the `mappedBy` option to the many-to-many relation's name in the `Order` data model.
+- `product`: A relation that indicates this model belongs to the `Product` data model. You set the `mappedBy` option to the many-to-many relation's name in the `Product` data model.
+- `metadata`: An extra column to add to the pivot table of type `json`. You can add other columns as well to the model.
+
+***
+
+## Set Relationship Name in the Other Model
+
+The relationship property methods accept as a second parameter an object of options. The `mappedBy` property defines the name of the relationship in the other data model.
+
+This is useful if the relationship property’s name is different from that of the associated data model.
+
+As seen in previous examples, the `mappedBy` option is required for the `belongsTo` method.
+
+For example:
+
+```ts highlights={relationNameHighlights}
+import { model } from "@medusajs/framework/utils"
+
+const User = model.define("user", {
+ id: model.id().primaryKey(),
+ email: model.hasOne(() => Email, {
+ mappedBy: "owner",
+ }),
+})
+
+const Email = model.define("email", {
+ id: model.id().primaryKey(),
+ owner: model.belongsTo(() => User, {
+ mappedBy: "email",
+ }),
+})
+```
+
+In this example, you specify in the `User` data model’s relationship property that the name of the relationship in the `Email` data model is `owner`.
+
+***
+
+## Cascades
+
+When an operation is performed on a data model, such as record deletion, the relationship cascade specifies what related data model records should be affected by it.
+
+For example, if a store is deleted, its products should also be deleted.
+
+The `cascades` method used on a data model configures which child records an operation is cascaded to.
+
+For example:
+
+```ts highlights={highlights}
+import { model } from "@medusajs/framework/utils"
+
+const Store = model.define("store", {
+ id: model.id().primaryKey(),
+ products: model.hasMany(() => Product),
+})
+.cascades({
+ delete: ["products"],
+})
+
+const Product = model.define("product", {
+ id: model.id().primaryKey(),
+ store: model.belongsTo(() => Store, {
+ mappedBy: "products",
+ }),
+})
+```
+
+The `cascades` method accepts an object. Its key is the operation’s name, such as `delete`. The value is an array of relationship property names that the operation is cascaded to.
+
+In the example above, when a store is deleted, its associated products are also deleted.
# Pass Additional Data to Medusa's API Route
@@ -8050,7 +9313,7 @@ In this chapter, you'll learn how to pass additional data in requests to Medusa'
Some of Medusa's API Routes accept an `additional_data` parameter whose type is an object. The API Route passes the `additional_data` to the workflow, which in turn passes it to its hooks.
-This is useful when you have a link from your custom module to a commerce module, and you want to perform an additional action when a request is sent to an existing API route.
+This is useful when you have a link from your custom module to a Commerce Module, and you want to perform an additional action when a request is sent to an existing API route.
For example, the [Create Product API Route](https://docs.medusajs.com/api/admin#products_postproducts) accepts an `additional_data` parameter. If you have a data model linked to it, you consume the `productsCreated` hook to create a record of the data model using the custom data and link it to the product.
@@ -8241,6 +9504,118 @@ createProductsWorkflow.hooks.productsCreated(
This updates the products to their original state before adding the brand to their `metadata` property.
+# Handling CORS in API Routes
+
+In this chapter, you’ll learn about the CORS middleware and how to configure it for custom API routes.
+
+## CORS Overview
+
+Cross-Origin Resource Sharing (CORS) allows only configured origins to access your API Routes.
+
+For example, if you allow only origins starting with `http://localhost:7001` to access your Admin API Routes, other origins accessing those routes get a CORS error.
+
+### CORS Configurations
+
+The `storeCors` and `adminCors` properties of Medusa's `http` configuration set the allowed origins for routes starting with `/store` and `/admin` respectively.
+
+These configurations accept a URL pattern to identify allowed origins.
+
+For example:
+
+```js title="medusa-config.ts"
+module.exports = defineConfig({
+ projectConfig: {
+ http: {
+ storeCors: "http://localhost:8000",
+ adminCors: "http://localhost:7001",
+ // ...
+ },
+ },
+})
+```
+
+This allows the `http://localhost:7001` origin to access the Admin API Routes, and the `http://localhost:8000` origin to access Store API Routes.
+
+Learn more about the CORS configurations in [this resource guide](https://docs.medusajs.com/learn/configurations/medusa-config#http/index.html.md).
+
+***
+
+## CORS in Store and Admin Routes
+
+To disable the CORS middleware for a route, export a `CORS` variable in the route file with its value set to `false`.
+
+For example:
+
+```ts title="src/api/store/custom/route.ts" highlights={[["15"]]}
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: "[GET] Hello world!",
+ })
+}
+
+export const CORS = false
+```
+
+This disables the CORS middleware on API Routes at the path `/store/custom`.
+
+***
+
+## CORS in Custom Routes
+
+If you create a route that doesn’t start with `/store` or `/admin`, you must apply the CORS middleware manually. Otherwise, all requests to your API route lead to a CORS error.
+
+You can do that in the exported middlewares configurations in `src/api/middlewares.ts`.
+
+For example:
+
+```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-10" expandButtonLabel="Show Imports"
+import { defineMiddlewares } from "@medusajs/framework/http"
+import type {
+ MedusaNextFunction,
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { ConfigModule } from "@medusajs/framework/types"
+import { parseCorsOrigins } from "@medusajs/framework/utils"
+import cors from "cors"
+
+export default defineMiddlewares({
+ routes: [
+ {
+ matcher: "/custom*",
+ middlewares: [
+ (
+ req: MedusaRequest,
+ res: MedusaResponse,
+ next: MedusaNextFunction
+ ) => {
+ const configModule: ConfigModule =
+ req.scope.resolve("configModule")
+
+ return cors({
+ origin: parseCorsOrigins(
+ configModule.projectConfig.http.storeCors
+ ),
+ credentials: true,
+ })(req, res, next)
+ },
+ ],
+ },
+ ],
+})
+```
+
+This retrieves the configurations exported from `medusa-config.ts` and applies the `storeCors` to routes starting with `/custom`.
+
+
# Throwing and Handling Errors
In this guide, you'll learn how to throw errors in your Medusa application, how it affects an API route's response, and how to change the default error handler of your Medusa application.
@@ -8739,6 +10114,322 @@ A middleware can not override an existing middleware. Instead, middlewares are a
For example, if you define a custom validation middleware, such as `validateAndTransformBody`, on an existing route, then both the original and the custom validation middleware will run.
+# Configure Request Body Parser
+
+In this chapter, you'll learn how to configure the request body parser for your API routes.
+
+## Default Body Parser Configuration
+
+The Medusa application configures the body parser by default to parse JSON, URL-encoded, and text request content types. You can parse other data types by adding the relevant [Express middleware](https://expressjs.com/en/guide/using-middleware.html) or preserve the raw body data by configuring the body parser, which is useful for webhook requests.
+
+This chapter shares some examples of configuring the body parser for different data types or use cases.
+
+***
+
+## Preserve Raw Body Data for Webhooks
+
+If your API route receives webhook requests, you might want to preserve the raw body data. To do this, you can configure the body parser to parse the raw body data and store it in the `req.rawBody` property.
+
+To do that, create the file `src/api/middlewares.ts` with the following content:
+
+```ts title="src/api/middlewares.ts" highlights={preserveHighlights}
+import { defineMiddlewares } from "@medusajs/framework/http"
+
+export default defineMiddlewares({
+ routes: [
+ {
+ method: ["POST"],
+ bodyParser: { preserveRawBody: true },
+ matcher: "/custom",
+ },
+ ],
+})
+```
+
+The middleware route object passed to `routes` accepts a `bodyParser` property whose value is an object of configuration for the default body parser. By enabling the `preserveRawBody` property, the raw body data is preserved and stored in the `req.rawBody` property.
+
+Learn more about [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md).
+
+You can then access the raw body data in your API route handler:
+
+```ts title="src/api/custom/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
+
+export async function POST(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ console.log(req.rawBody)
+
+ // TODO use raw body
+}
+```
+
+***
+
+## Configure Request Body Size Limit
+
+By default, the body parser limits the request body size to `100kb`. If a request body exceeds that size, the Medusa application throws an error.
+
+You can configure the body parser to accept larger request bodies by setting the `sizeLimit` property of the `bodyParser` object in a middleware route object. For example:
+
+```ts title="src/api/middlewares.ts" highlights={sizeLimitHighlights}
+import { defineMiddlewares } from "@medusajs/framework/http"
+
+export default defineMiddlewares({
+ routes: [
+ {
+ method: ["POST"],
+ bodyParser: { sizeLimit: "2mb" },
+ matcher: "/custom",
+ },
+ ],
+})
+```
+
+The `sizeLimit` property accepts one of the following types of values:
+
+- A string representing the size limit in bytes (For example, `100kb`, `2mb`, `5gb`). It is passed to the [bytes](https://www.npmjs.com/package/bytes) library to parse the size.
+- A number representing the size limit in bytes. For example, `1024` for 1kb.
+
+***
+
+## Configure File Uploads
+
+To accept file uploads in your API routes, you can configure the [Express Multer middleware](https://expressjs.com/en/resources/middleware/multer.html) on your route.
+
+The `multer` package is available through the `@medusajs/medusa` package, so you don't need to install it. However, for better typing support, install the `@types/multer` package as a development dependency:
+
+```bash npm2yarn
+npm install --save-dev @types/multer
+```
+
+Then, to configure file upload for your route, create the file `src/api/middlewares.ts` with the following content:
+
+```ts title="src/api/middlewares.ts" highlights={uploadHighlights}
+import { defineMiddlewares } from "@medusajs/framework/http"
+import multer from "multer"
+
+const upload = multer({ storage: multer.memoryStorage() })
+
+export default defineMiddlewares({
+ routes: [
+ {
+ method: ["POST"],
+ matcher: "/custom",
+ middlewares: [
+ // @ts-ignore
+ upload.array("files"),
+ ],
+ },
+ ],
+})
+```
+
+In the example above, you configure the `multer` middleware to store the uploaded files in memory. Then, you apply the `upload.array("files")` middleware to the route to accept file uploads. By using the `array` method, you accept multiple file uploads with the same `files` field name.
+
+You can then access the uploaded files in your API route handler:
+
+```ts title="src/api/custom/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
+
+export async function POST(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const files = req.files as Express.Multer.File[]
+
+ // TODO handle files
+}
+```
+
+The uploaded files are stored in the `req.files` property as an array of Multer file objects that have properties like `filename` and `mimetype`.
+
+### Uploading Files using File Module Provider
+
+The recommended way to upload the files to storage using the configured [File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file/index.html.md) is to use the [uploadFilesWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md):
+
+```ts title="src/api/custom/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
+import { MedusaError } from "@medusajs/framework/utils"
+import { uploadFilesWorkflow } from "@medusajs/medusa/core-flows"
+
+export async function POST(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const files = req.files as Express.Multer.File[]
+
+ if (!files?.length) {
+ throw new MedusaError(
+ MedusaError.Types.INVALID_DATA,
+ "No files were uploaded"
+ )
+ }
+
+ const { result } = await uploadFilesWorkflow(req.scope).run({
+ input: {
+ files: files?.map((f) => ({
+ filename: f.originalname,
+ mimeType: f.mimetype,
+ content: f.buffer.toString("binary"),
+ access: "public",
+ })),
+ },
+ })
+
+ res.status(200).json({ files: result })
+}
+```
+
+Check out the [uploadFilesWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md) for details on the expected input and output of the workflow.
+
+
+# API Route Parameters
+
+In this chapter, you’ll learn about path, query, and request body parameters.
+
+## Path Parameters
+
+To create an API route that accepts a path parameter, create a directory within the route file's path whose name is of the format `[param]`.
+
+For example, to create an API Route at the path `/hello-world/:id`, where `:id` is a path parameter, create the file `src/api/hello-world/[id]/route.ts` with the following content:
+
+```ts title="src/api/hello-world/[id]/route.ts" highlights={singlePathHighlights}
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: `[GET] Hello ${req.params.id}!`,
+ })
+}
+```
+
+The `MedusaRequest` object has a `params` property. `params` holds the path parameters in key-value pairs.
+
+### Multiple Path Parameters
+
+To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`.
+
+For example, to create an API route at `/hello-world/:id/name/:name`, create the file `src/api/hello-world/[id]/name/[name]/route.ts` with the following content:
+
+```ts title="src/api/hello-world/[id]/name/[name]/route.ts" highlights={multiplePathHighlights}
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: `[GET] Hello ${
+ req.params.id
+ } - ${req.params.name}!`,
+ })
+}
+```
+
+You access the `id` and `name` path parameters using the `req.params` property.
+
+***
+
+## Query Parameters
+
+You can access all query parameters in the `query` property of the `MedusaRequest` object. `query` is an object of key-value pairs, where the key is a query parameter's name, and the value is its value.
+
+For example:
+
+```ts title="src/api/hello-world/route.ts" highlights={queryHighlights}
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+export const GET = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: `Hello ${req.query.name}`,
+ })
+}
+```
+
+The value of `req.query.name` is the value passed in `?name=John`, for example.
+
+### Validate Query Parameters
+
+You can apply validation rules on received query parameters to ensure they match specified rules and types.
+
+Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-query-paramters/index.html.md).
+
+***
+
+## Request Body Parameters
+
+The Medusa application parses the body of any request having a JSON, URL-encoded, or text request content types. The request body parameters are set in the `MedusaRequest`'s `body` property.
+
+Learn more about configuring body parsing in [this guide](https://docs.medusajs.com/learn/fundamentals/api-routes/parse-body/index.html.md).
+
+For example:
+
+```ts title="src/api/hello-world/route.ts" highlights={bodyHighlights}
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+
+type HelloWorldReq = {
+ name: string
+}
+
+export const POST = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ res.json({
+ message: `[POST] Hello ${req.body.name}!`,
+ })
+}
+```
+
+In this example, you use the `name` request body parameter to create the message in the returned response.
+
+The `MedusaRequest` type accepts a type argument that indicates the type of the request body. This is useful for auto-completion and to avoid typing errors.
+
+To test it out, send the following request to your Medusa application:
+
+```bash
+curl -X POST 'http://localhost:9000/hello-world' \
+-H 'Content-Type: application/json' \
+--data-raw '{
+ "name": "John"
+}'
+```
+
+This returns the following JSON object:
+
+```json
+{
+ "message": "[POST] Hello John!"
+}
+```
+
+### Validate Body Parameters
+
+You can apply validation rules on received body parameters to ensure they match specified rules and types.
+
+Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-body/index.html.md).
+
+
# Protected Routes
In this chapter, you’ll learn how to create protected routes.
@@ -8941,322 +10632,6 @@ export const GET = async (
In the route handler, you resolve the User Module's main service, then use it to retrieve the logged-in admin user.
-# Configure Request Body Parser
-
-In this chapter, you'll learn how to configure the request body parser for your API routes.
-
-## Default Body Parser Configuration
-
-The Medusa application configures the body parser by default to parse JSON, URL-encoded, and text request content types. You can parse other data types by adding the relevant [Express middleware](https://expressjs.com/en/guide/using-middleware.html) or preserve the raw body data by configuring the body parser, which is useful for webhook requests.
-
-This chapter shares some examples of configuring the body parser for different data types or use cases.
-
-***
-
-## Preserve Raw Body Data for Webhooks
-
-If your API route receives webhook requests, you might want to preserve the raw body data. To do this, you can configure the body parser to parse the raw body data and store it in the `req.rawBody` property.
-
-To do that, create the file `src/api/middlewares.ts` with the following content:
-
-```ts title="src/api/middlewares.ts" highlights={preserveHighlights}
-import { defineMiddlewares } from "@medusajs/framework/http"
-
-export default defineMiddlewares({
- routes: [
- {
- method: ["POST"],
- bodyParser: { preserveRawBody: true },
- matcher: "/custom",
- },
- ],
-})
-```
-
-The middleware route object passed to `routes` accepts a `bodyParser` property whose value is an object of configuration for the default body parser. By enabling the `preserveRawBody` property, the raw body data is preserved and stored in the `req.rawBody` property.
-
-Learn more about [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md).
-
-You can then access the raw body data in your API route handler:
-
-```ts title="src/api/custom/route.ts"
-import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
-
-export async function POST(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- console.log(req.rawBody)
-
- // TODO use raw body
-}
-```
-
-***
-
-## Configure Request Body Size Limit
-
-By default, the body parser limits the request body size to `100kb`. If a request body exceeds that size, the Medusa application throws an error.
-
-You can configure the body parser to accept larger request bodies by setting the `sizeLimit` property of the `bodyParser` object in a middleware route object. For example:
-
-```ts title="src/api/middlewares.ts" highlights={sizeLimitHighlights}
-import { defineMiddlewares } from "@medusajs/framework/http"
-
-export default defineMiddlewares({
- routes: [
- {
- method: ["POST"],
- bodyParser: { sizeLimit: "2mb" },
- matcher: "/custom",
- },
- ],
-})
-```
-
-The `sizeLimit` property accepts one of the following types of values:
-
-- A string representing the size limit in bytes (For example, `100kb`, `2mb`, `5gb`). It is passed to the [bytes](https://www.npmjs.com/package/bytes) library to parse the size.
-- A number representing the size limit in bytes. For example, `1024` for 1kb.
-
-***
-
-## Configure File Uploads
-
-To accept file uploads in your API routes, you can configure the [Express Multer middleware](https://expressjs.com/en/resources/middleware/multer.html) on your route.
-
-The `multer` package is available through the `@medusajs/medusa` package, so you don't need to install it. However, for better typing support, install the `@types/multer` package as a development dependency:
-
-```bash npm2yarn
-npm install --save-dev @types/multer
-```
-
-Then, to configure file upload for your route, create the file `src/api/middlewares.ts` with the following content:
-
-```ts title="src/api/middlewares.ts" highlights={uploadHighlights}
-import { defineMiddlewares } from "@medusajs/framework/http"
-import multer from "multer"
-
-const upload = multer({ storage: multer.memoryStorage() })
-
-export default defineMiddlewares({
- routes: [
- {
- method: ["POST"],
- matcher: "/custom",
- middlewares: [
- // @ts-ignore
- upload.array("files"),
- ],
- },
- ],
-})
-```
-
-In the example above, you configure the `multer` middleware to store the uploaded files in memory. Then, you apply the `upload.array("files")` middleware to the route to accept file uploads. By using the `array` method, you accept multiple file uploads with the same `files` field name.
-
-You can then access the uploaded files in your API route handler:
-
-```ts title="src/api/custom/route.ts"
-import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
-
-export async function POST(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const files = req.files as Express.Multer.File[]
-
- // TODO handle files
-}
-```
-
-The uploaded files are stored in the `req.files` property as an array of Multer file objects that have properties like `filename` and `mimetype`.
-
-### Uploading Files using File Module Provider
-
-The recommended way to upload the files to storage using the configured [File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file/index.html.md) is to use the [uploadFilesWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md):
-
-```ts title="src/api/custom/route.ts"
-import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
-import { MedusaError } from "@medusajs/framework/utils"
-import { uploadFilesWorkflow } from "@medusajs/medusa/core-flows"
-
-export async function POST(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const files = req.files as Express.Multer.File[]
-
- if (!files?.length) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- "No files were uploaded"
- )
- }
-
- const { result } = await uploadFilesWorkflow(req.scope).run({
- input: {
- files: files?.map((f) => ({
- filename: f.originalname,
- mimeType: f.mimetype,
- content: f.buffer.toString("binary"),
- access: "public",
- })),
- },
- })
-
- res.status(200).json({ files: result })
-}
-```
-
-Check out the [uploadFilesWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/uploadFilesWorkflow/index.html.md) for details on the expected input and output of the workflow.
-
-
-# API Route Parameters
-
-In this chapter, you’ll learn about path, query, and request body parameters.
-
-## Path Parameters
-
-To create an API route that accepts a path parameter, create a directory within the route file's path whose name is of the format `[param]`.
-
-For example, to create an API Route at the path `/hello-world/:id`, where `:id` is a path parameter, create the file `src/api/hello-world/[id]/route.ts` with the following content:
-
-```ts title="src/api/hello-world/[id]/route.ts" highlights={singlePathHighlights}
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: `[GET] Hello ${req.params.id}!`,
- })
-}
-```
-
-The `MedusaRequest` object has a `params` property. `params` holds the path parameters in key-value pairs.
-
-### Multiple Path Parameters
-
-To create an API route that accepts multiple path parameters, create within the file's path multiple directories whose names are of the format `[param]`.
-
-For example, to create an API route at `/hello-world/:id/name/:name`, create the file `src/api/hello-world/[id]/name/[name]/route.ts` with the following content:
-
-```ts title="src/api/hello-world/[id]/name/[name]/route.ts" highlights={multiplePathHighlights}
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: `[GET] Hello ${
- req.params.id
- } - ${req.params.name}!`,
- })
-}
-```
-
-You access the `id` and `name` path parameters using the `req.params` property.
-
-***
-
-## Query Parameters
-
-You can access all query parameters in the `query` property of the `MedusaRequest` object. `query` is an object of key-value pairs, where the key is a query parameter's name, and the value is its value.
-
-For example:
-
-```ts title="src/api/hello-world/route.ts" highlights={queryHighlights}
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-export const GET = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: `Hello ${req.query.name}`,
- })
-}
-```
-
-The value of `req.query.name` is the value passed in `?name=John`, for example.
-
-### Validate Query Parameters
-
-You can apply validation rules on received query parameters to ensure they match specified rules and types.
-
-Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-query-paramters/index.html.md).
-
-***
-
-## Request Body Parameters
-
-The Medusa application parses the body of any request having a JSON, URL-encoded, or text request content types. The request body parameters are set in the `MedusaRequest`'s `body` property.
-
-Learn more about configuring body parsing in [this guide](https://docs.medusajs.com/learn/fundamentals/api-routes/parse-body/index.html.md).
-
-For example:
-
-```ts title="src/api/hello-world/route.ts" highlights={bodyHighlights}
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-
-type HelloWorldReq = {
- name: string
-}
-
-export const POST = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- res.json({
- message: `[POST] Hello ${req.body.name}!`,
- })
-}
-```
-
-In this example, you use the `name` request body parameter to create the message in the returned response.
-
-The `MedusaRequest` type accepts a type argument that indicates the type of the request body. This is useful for auto-completion and to avoid typing errors.
-
-To test it out, send the following request to your Medusa application:
-
-```bash
-curl -X POST 'http://localhost:9000/hello-world' \
--H 'Content-Type: application/json' \
---data-raw '{
- "name": "John"
-}'
-```
-
-This returns the following JSON object:
-
-```json
-{
- "message": "[POST] Hello John!"
-}
-```
-
-### Validate Body Parameters
-
-You can apply validation rules on received body parameters to ensure they match specified rules and types.
-
-Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation#how-to-validate-request-body/index.html.md).
-
-
# API Route Response
In this chapter, you'll learn how to send a response in your API route.
@@ -9608,194 +10983,6 @@ For example, if you omit the `a` parameter, you'll receive a `400` response code
To see different examples and learn more about creating a validation schema, refer to [Zod's documentation](https://zod.dev).
-# Seed Data with Custom CLI Script
-
-In this chapter, you'll learn how to seed data using a custom CLI script.
-
-## How to Seed Data
-
-To seed dummy data for development or demo purposes, use a custom CLI script.
-
-In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md), to seed data.
-
-### Example: Seed Dummy Products
-
-In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products.
-
-First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script:
-
-```bash npm2yarn
-npm install --save-dev @faker-js/faker
-```
-
-Then, create the file `src/scripts/demo-products.ts` with the following content:
-
-```ts title="src/scripts/demo-products.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
-import { ExecArgs } from "@medusajs/framework/types"
-import { faker } from "@faker-js/faker"
-import {
- ContainerRegistrationKeys,
- Modules,
- ProductStatus,
-} from "@medusajs/framework/utils"
-import {
- createInventoryLevelsWorkflow,
- createProductsWorkflow,
-} from "@medusajs/medusa/core-flows"
-
-export default async function seedDummyProducts({
- container,
-}: ExecArgs) {
- const salesChannelModuleService = container.resolve(
- Modules.SALES_CHANNEL
- )
- const logger = container.resolve(
- ContainerRegistrationKeys.LOGGER
- )
- const query = container.resolve(
- ContainerRegistrationKeys.QUERY
- )
-
- const defaultSalesChannel = await salesChannelModuleService
- .listSalesChannels({
- name: "Default Sales Channel",
- })
-
- const sizeOptions = ["S", "M", "L", "XL"]
- const colorOptions = ["Black", "White"]
- const currency_code = "eur"
- const productsNum = 50
-
- // TODO seed products
-}
-```
-
-So far, in the script, you:
-
-- Resolve the Sales Channel Module's main service to retrieve the application's default sales channel. This is the sales channel the dummy products will be available in.
-- Resolve the Logger to log messages in the terminal, and Query to later retrieve data useful for the seeded products.
-- Initialize some default data to use when seeding the products next.
-
-Next, replace the `TODO` with the following:
-
-```ts title="src/scripts/demo-products.ts"
-const productsData = new Array(productsNum).fill(0).map((_, index) => {
- const title = faker.commerce.product() + "_" + index
- return {
- title,
- is_giftcard: true,
- description: faker.commerce.productDescription(),
- status: ProductStatus.PUBLISHED,
- options: [
- {
- title: "Size",
- values: sizeOptions,
- },
- {
- title: "Color",
- values: colorOptions,
- },
- ],
- images: [
- {
- url: faker.image.urlPlaceholder({
- text: title,
- }),
- },
- {
- url: faker.image.urlPlaceholder({
- text: title,
- }),
- },
- ],
- variants: new Array(10).fill(0).map((_, variantIndex) => ({
- title: `${title} ${variantIndex}`,
- sku: `variant-${variantIndex}${index}`,
- prices: new Array(10).fill(0).map((_, priceIndex) => ({
- currency_code,
- amount: 10 * priceIndex,
- })),
- options: {
- Size: sizeOptions[Math.floor(Math.random() * 3)],
- },
- })),
- shipping_profile_id: "sp_123",
- sales_channels: [
- {
- id: defaultSalesChannel[0].id,
- },
- ],
- }
-})
-
-// TODO seed products
-```
-
-You generate fifty products using the sales channel and variables you initialized, and using Faker for random data, such as the product's title or images.
-
-Then, replace the new `TODO` with the following:
-
-```ts title="src/scripts/demo-products.ts"
-const { result: products } = await createProductsWorkflow(container).run({
- input: {
- products: productsData,
- },
-})
-
-logger.info(`Seeded ${products.length} products.`)
-
-// TODO add inventory levels
-```
-
-You create the generated products using the `createProductsWorkflow` imported previously from `@medusajs/medusa/core-flows`. It accepts the product data as input, and returns the created products.
-
-Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following:
-
-```ts title="src/scripts/demo-products.ts"
-logger.info("Seeding inventory levels.")
-
-const { data: stockLocations } = await query.graph({
- entity: "stock_location",
- fields: ["id"],
-})
-
-const { data: inventoryItems } = await query.graph({
- entity: "inventory_item",
- fields: ["id"],
-})
-
-const inventoryLevels = inventoryItems.map((inventoryItem) => ({
- location_id: stockLocations[0].id,
- stocked_quantity: 1000000,
- inventory_item_id: inventoryItem.id,
-}))
-
-await createInventoryLevelsWorkflow(container).run({
- input: {
- inventory_levels: inventoryLevels,
- },
-})
-
-logger.info("Finished seeding inventory levels data.")
-```
-
-You use Query to retrieve the stock location, to use the first location in the application, and the inventory items.
-
-Then, you generate inventory levels for each inventory item, associating it with the first stock location.
-
-Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels.
-
-### Test Script
-
-To test out the script, run the following command in your project's directory:
-
-```bash
-npx medusa exec ./src/scripts/demo-products.ts
-```
-
-This seeds the products to your database. If you run your Medusa application and view the products in the dashboard, you'll find fifty new products.
-
-
# Event Data Payload
In this chapter, you'll learn how subscribers receive an event's data payload.
@@ -9841,118 +11028,6 @@ This logs the product ID received in the `product.created` event’s data payloa
Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */}
-# Data Model Database Index
-
-In this chapter, you’ll learn how to define a database index on a data model.
-
-You can also define an index on a property as explained in the [Properties chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#define-database-index-on-property/index.html.md).
-
-## Define Database Index on Data Model
-
-A data model has an `indexes` method that defines database indices on its properties.
-
-The index can be on multiple columns (composite index). For example:
-
-```ts highlights={dataModelIndexHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const MyCustom = model.define("my_custom", {
- id: model.id().primaryKey(),
- name: model.text(),
- age: model.number(),
-}).indexes([
- {
- on: ["name", "age"],
- },
-])
-
-export default MyCustom
-```
-
-The `indexes` method receives an array of indices as a parameter. Each index is an object with a required `on` property indicating the properties to apply the index on.
-
-In the above example, you define a composite index on the `name` and `age` properties.
-
-### Index Conditions
-
-An index can have conditions. For example:
-
-```ts highlights={conditionHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const MyCustom = model.define("my_custom", {
- id: model.id().primaryKey(),
- name: model.text(),
- age: model.number(),
-}).indexes([
- {
- on: ["name", "age"],
- where: {
- age: 30,
- },
- },
-])
-
-export default MyCustom
-```
-
-The index object passed to `indexes` accepts a `where` property whose value is an object of conditions. The object's key is a property's name, and its value is the condition on that property.
-
-In the example above, the composite index is created on the `name` and `age` properties when the `age`'s value is `30`.
-
-A property's condition can be a negation. For example:
-
-```ts highlights={negationHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const MyCustom = model.define("my_custom", {
- id: model.id().primaryKey(),
- name: model.text(),
- age: model.number().nullable(),
-}).indexes([
- {
- on: ["name", "age"],
- where: {
- age: {
- $ne: null,
- },
- },
- },
-])
-
-export default MyCustom
-```
-
-A property's value in `where` can be an object having a `$ne` property. `$ne`'s value indicates what the specified property's value shouldn't be.
-
-In the example above, the composite index is created on the `name` and `age` properties when `age`'s value is not `null`.
-
-### Unique Database Index
-
-The object passed to `indexes` accepts a `unique` property indicating that the created index must be a unique index.
-
-For example:
-
-```ts highlights={uniqueHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const MyCustom = model.define("my_custom", {
- id: model.id().primaryKey(),
- name: model.text(),
- age: model.number(),
-}).indexes([
- {
- on: ["name", "age"],
- unique: true,
- },
-])
-
-export default MyCustom
-```
-
-This creates a unique composite index on the `name` and `age` properties.
-
-
# Emit Workflow and Service Events
In this chapter, you'll learn about event types and how to emit an event in a service or workflow.
@@ -10121,1081 +11196,6 @@ If you execute the `performAction` method of your service, the event is emitted
Any subscribers listening to the event are also executed.
-# Add Data Model Check Constraints
-
-In this chapter, you'll learn how to add check constraints to your data model.
-
-## What is a Check Constraint?
-
-A check constraint is a condition that must be satisfied by records inserted into a database table, otherwise an error is thrown.
-
-For example, if you have a data model with a `price` property, you want to only allow positive number values. So, you add a check constraint that fails when inserting a record with a negative price value.
-
-***
-
-## How to Set a Check Constraint?
-
-To set check constraints on a data model, use the `checks` method. This method accepts an array of check constraints to apply on the data model.
-
-For example, to set a check constraint on a `price` property that ensures its value can only be a positive number:
-
-```ts highlights={checks1Highlights}
-import { model } from "@medusajs/framework/utils"
-
-const CustomProduct = model.define("custom_product", {
- // ...
- price: model.bigNumber(),
-})
-.checks([
- (columns) => `${columns.price} >= 0`,
-])
-```
-
-The item passed in the array parameter of `checks` can be a callback function that accepts as a parameter an object whose keys are the names of the properties in the data model schema, and values the respective column name in the database.
-
-The function returns a string indicating the [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS). In the expression, use the `columns` parameter to access a property's column name.
-
-You can also pass an object to the `checks` method:
-
-```ts highlights={checks2Highlights}
-import { model } from "@medusajs/framework/utils"
-
-const CustomProduct = model.define("custom_product", {
- // ...
- price: model.bigNumber(),
-})
-.checks([
- {
- name: "custom_product_price_check",
- expression: (columns) => `${columns.price} >= 0`,
- },
-])
-```
-
-The object accepts the following properties:
-
-- `name`: The check constraint's name.
-- `expression`: A function similar to the one that can be passed to the array. It accepts an object of columns and returns an [SQL check constraint expression](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS).
-
-***
-
-## Apply in Migrations
-
-After adding the check constraint, make sure to generate and run migrations if you already have the table in the database. Otherwise, the check constraint won't be reflected.
-
-To generate a migration for the data model's module then reflect it on the database, run the following command:
-
-```bash
-npx medusa db:generate custom_module
-npx medusa db:migrate
-```
-
-The first command generates the migration under the `migrations` directory of your module's directory, and the second reflects it on the database.
-
-
-# Infer Type of Data Model
-
-In this chapter, you'll learn how to infer the type of a data model.
-
-## How to Infer Type of Data Model?
-
-Consider you have a `Post` data model. You can't reference this data model in a type, such as a workflow input or service method output types, since it's a variable.
-
-Instead, Medusa provides `InferTypeOf` that transforms your data model to a type.
-
-For example:
-
-```ts
-import { InferTypeOf } from "@medusajs/framework/types"
-import { Post } from "../modules/blog/models/post" // relative path to the model
-
-export type Post = InferTypeOf
-```
-
-`InferTypeOf` accepts as a type argument the type of the data model.
-
-Since the `Post` data model is a variable, use the `typeof` operator to pass the data model as a type argument to `InferTypeOf`.
-
-You can now use the `Post` type to reference a data model in other types, such as in workflow inputs or service method outputs:
-
-```ts title="Example Service"
-// other imports...
-import { InferTypeOf } from "@medusajs/framework/types"
-import { Post } from "../models/post"
-
-type Post = InferTypeOf
-
-class BlogModuleService extends MedusaService({ Post }) {
- async doSomething(): Promise {
- // ...
- }
-}
-```
-
-
-# Data Model Relationships
-
-In this chapter, you’ll learn how to define relationships between data models in your module.
-
-## What is a Relationship Property?
-
-A relationship property defines an association in the database between two models. It's created using the Data Model Language (DML) methods, such as `hasOne` or `belongsTo`.
-
-When you generate a migration for these data models, the migrations include foreign key columns or pivot tables, based on the relationship's type.
-
-You want to create a relation between data models in the same module.
-
-You want to create a relationship between data models in different modules. Use module links instead.
-
-***
-
-## One-to-One Relationship
-
-A one-to-one relationship indicates that one record of a data model belongs to or is associated with another.
-
-To define a one-to-one relationship, create relationship properties in the data models using the following methods:
-
-1. `hasOne`: indicates that the model has one record of the specified model.
-2. `belongsTo`: indicates that the model belongs to one record of the specified model.
-
-For example:
-
-```ts highlights={oneToOneHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const User = model.define("user", {
- id: model.id().primaryKey(),
- email: model.hasOne(() => Email),
-})
-
-const Email = model.define("email", {
- id: model.id().primaryKey(),
- user: model.belongsTo(() => User, {
- mappedBy: "email",
- }),
-})
-```
-
-In the example above, a user has one email, and an email belongs to one user.
-
-The `hasOne` and `belongsTo` methods accept a function as the first parameter. The function returns the associated data model.
-
-The `belongsTo` method also requires passing as a second parameter an object with the property `mappedBy`. Its value is the name of the relationship property in the other data model.
-
-### Optional Relationship
-
-To make the relationship optional on the `hasOne` or `belongsTo` side, use the `nullable` method on either property as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#make-property-optional/index.html.md).
-
-### One-sided One-to-One Relationship
-
-If the one-to-one relationship is only defined on one side, pass `undefined` to the `mappedBy` property in the `belongsTo` method.
-
-For example:
-
-```ts highlights={oneToOneUndefinedHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const User = model.define("user", {
- id: model.id().primaryKey(),
-})
-
-const Email = model.define("email", {
- id: model.id().primaryKey(),
- user: model.belongsTo(() => User, {
- mappedBy: undefined,
- }),
-})
-```
-
-### One-to-One Relationship in the Database
-
-When you generate the migrations of data models that have a one-to-one relationship, the migration adds to the table of the data model that has the `belongsTo` property:
-
-1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `email` table will have a `user_id` column.
-2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
-
-
-
-***
-
-## One-to-Many Relationship
-
-A one-to-many relationship indicates that one record of a data model has many records of another data model.
-
-To define a one-to-many relationship, create relationship properties in the data models using the following methods:
-
-1. `hasMany`: indicates that the model has more than one record of the specified model.
-2. `belongsTo`: indicates that the model belongs to one record of the specified model.
-
-For example:
-
-```ts highlights={oneToManyHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Store = model.define("store", {
- id: model.id().primaryKey(),
- products: model.hasMany(() => Product),
-})
-
-const Product = model.define("product", {
- id: model.id().primaryKey(),
- store: model.belongsTo(() => Store, {
- mappedBy: "products",
- }),
-})
-```
-
-In this example, a store has many products, but a product belongs to one store.
-
-### Optional Relationship
-
-To make the relationship optional on the `belongsTo` side, use the `nullable` method on the property as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/properties#make-property-optional/index.html.md).
-
-### One-to-Many Relationship in the Database
-
-When you generate the migrations of data models that have a one-to-many relationship, the migration adds to the table of the data model that has the `belongsTo` property:
-
-1. A column of the format `{relation_name}_id` to store the ID of the record of the related data model. For example, the `product` table will have a `store_id` column.
-2. A foreign key on the `{relation_name}_id` column to the table of the related data model.
-
-
-
-***
-
-## Many-to-Many Relationship
-
-A many-to-many relationship indicates that many records of a data model can be associated with many records of another data model.
-
-To define a many-to-many relationship, create relationship properties in the data models using the `manyToMany` method.
-
-For example:
-
-```ts highlights={manyToManyHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Order = model.define("order", {
- id: model.id().primaryKey(),
- products: model.manyToMany(() => Product, {
- mappedBy: "orders",
- pivotTable: "order_product",
- joinColumn: "order_id",
- inverseJoinColumn: "product_id",
- }),
-})
-
-const Product = model.define("product", {
- id: model.id().primaryKey(),
- orders: model.manyToMany(() => Order, {
- mappedBy: "products",
- }),
-})
-```
-
-The `manyToMany` method accepts two parameters:
-
-1. A function that returns the associated data model.
-2. An object of optional configuration. Only one of the data models in the relation can define the `pivotTable`, `joinColumn`, and `inverseJoinColumn` configurations, and it's considered the owner data model. The object can accept the following properties:
- - `mappedBy`: The name of the relationship property in the other data model. If not set, the property's name is inferred from the associated data model's name.
- - `pivotTable`: The name of the pivot table created in the database for the many-to-many relation. If not set, the pivot table is inferred by combining the names of the data models' tables in alphabetical order, separating them by `_`, and pluralizing the last name. For example, `order_products`.
- - `joinColumn`: The name of the column in the pivot table that points to the owner model's primary key.
- - `inverseJoinColumn`: The name of the column in the pivot table that points to the owned model's primary key.
-
-The `pivotTable`, `joinColumn`, and `inverseJoinColumn` properties are only available after [Medusa v2.0.7](https://github.com/medusajs/medusa/releases/tag/v2.0.7).
-
-Following [Medusa v2.1.0](https://github.com/medusajs/medusa/releases/tag/v2.1.0), if `pivotTable`, `joinColumn`, and `inverseJoinColumn` aren't specified on either model, the owner is decided based on alphabetical order. So, in the example above, the `Order` data model would be the owner.
-
-In this example, an order is associated with many products, and a product is associated with many orders. Since the `pivotTable`, `joinColumn`, and `inverseJoinColumn` configurations are defined on the order, it's considered the owner data model.
-
-### Many-to-Many Relationship in the Database
-
-When you generate the migrations of data models that have a many-to-many relationship, the migration adds a new pivot table. Its name is either the name you specify in the `pivotTable` configuration or the inferred name combining the names of the data models' tables in alphabetical order, separating them by `_`, and pluralizing the last name. For example, `order_products`.
-
-The pivot table has a column with the name `{data_model}_id` for each of the data model's tables. It also has foreign keys on each of these columns to their respective tables.
-
-The pivot table has columns with foreign keys pointing to the primary key of the associated tables. The column's name is either:
-
-- The value of the `joinColumn` configuration for the owner table, and the `inverseJoinColumn` configuration for the owned table;
-- Or the inferred name `{table_name}_id`.
-
-
-
-### Many-To-Many with Custom Columns
-
-To add custom columns to the pivot table between two data models having a many-to-many relationship, you must define a new data model that represents the pivot table.
-
-For example:
-
-```ts highlights={manyToManyColumnHighlights}
-import { model } from "@medusajs/framework/utils"
-
-export const Order = model.define("order_test", {
- id: model.id().primaryKey(),
- products: model.manyToMany(() => Product, {
- pivotEntity: () => OrderProduct,
- }),
-})
-
-export const Product = model.define("product_test", {
- id: model.id().primaryKey(),
- orders: model.manyToMany(() => Order),
-})
-
-export const OrderProduct = model.define("orders_products", {
- id: model.id().primaryKey(),
- order: model.belongsTo(() => Order, {
- mappedBy: "products",
- }),
- product: model.belongsTo(() => Product, {
- mappedBy: "orders",
- }),
- metadata: model.json().nullable(),
-})
-```
-
-The `Order` and `Product` data models have a many-to-many relationship. To add extra columns to the created pivot table, you pass a `pivotEntity` option to the `products` relation in `Order` (since `Order` is the owner). The value of `pivotEntity` is a function that returns the data model representing the pivot table.
-
-The `OrderProduct` model defines, aside from the ID, the following properties:
-
-- `order`: A relation that indicates this model belongs to the `Order` data model. You set the `mappedBy` option to the many-to-many relation's name in the `Order` data model.
-- `product`: A relation that indicates this model belongs to the `Product` data model. You set the `mappedBy` option to the many-to-many relation's name in the `Product` data model.
-- `metadata`: An extra column to add to the pivot table of type `json`. You can add other columns as well to the model.
-
-***
-
-## Set Relationship Name in the Other Model
-
-The relationship property methods accept as a second parameter an object of options. The `mappedBy` property defines the name of the relationship in the other data model.
-
-This is useful if the relationship property’s name is different from that of the associated data model.
-
-As seen in previous examples, the `mappedBy` option is required for the `belongsTo` method.
-
-For example:
-
-```ts highlights={relationNameHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const User = model.define("user", {
- id: model.id().primaryKey(),
- email: model.hasOne(() => Email, {
- mappedBy: "owner",
- }),
-})
-
-const Email = model.define("email", {
- id: model.id().primaryKey(),
- owner: model.belongsTo(() => User, {
- mappedBy: "email",
- }),
-})
-```
-
-In this example, you specify in the `User` data model’s relationship property that the name of the relationship in the `Email` data model is `owner`.
-
-***
-
-## Cascades
-
-When an operation is performed on a data model, such as record deletion, the relationship cascade specifies what related data model records should be affected by it.
-
-For example, if a store is deleted, its products should also be deleted.
-
-The `cascades` method used on a data model configures which child records an operation is cascaded to.
-
-For example:
-
-```ts highlights={highlights}
-import { model } from "@medusajs/framework/utils"
-
-const Store = model.define("store", {
- id: model.id().primaryKey(),
- products: model.hasMany(() => Product),
-})
-.cascades({
- delete: ["products"],
-})
-
-const Product = model.define("product", {
- id: model.id().primaryKey(),
- store: model.belongsTo(() => Store, {
- mappedBy: "products",
- }),
-})
-```
-
-The `cascades` method accepts an object. Its key is the operation’s name, such as `delete`. The value is an array of relationship property names that the operation is cascaded to.
-
-In the example above, when a store is deleted, its associated products are also deleted.
-
-
-# Data Model Properties
-
-In this chapter, you'll learn about the different property types you can use in a data model and how to configure a data model's properties.
-
-## Data Model's Default Properties
-
-By default, Medusa creates the following properties for every data model:
-
-- `created_at`: A [dateTime](#dateTime) property that stores when a record of the data model was created.
-- `updated_at`: A [dateTime](#dateTime) property that stores when a record of the data model was updated.
-- `deleted_at`: A [dateTime](#dateTime) property that stores when a record of the data model was deleted. When you soft-delete a record, Medusa sets the `deleted_at` property to the current date.
-
-***
-
-## Property Types
-
-This section covers the different property types you can define in a data model's schema using the `model` methods.
-
-### id
-
-The `id` method defines an automatically generated string ID property. The generated ID is a unique string that has a mix of letters and numbers.
-
-For example:
-
-```ts highlights={idHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- id: model.id(),
- // ...
-})
-
-export default Post
-```
-
-### text
-
-The `text` method defines a string property.
-
-For example:
-
-```ts highlights={textHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- name: model.text(),
- // ...
-})
-
-export default Post
-```
-
-### number
-
-The `number` method defines a number property.
-
-For example:
-
-```ts highlights={numberHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- age: model.number(),
- // ...
-})
-
-export default Post
-```
-
-### float
-
-This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2).
-
-The `float` method defines a number property that allows for values with decimal places.
-
-Use this property type when it's less important to have high precision for numbers with large decimal places. Alternatively, for higher percision, use the [bigNumber property](#bignumber).
-
-For example:
-
-```ts highlights={floatHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- rating: model.float(),
- // ...
-})
-
-export default Post
-```
-
-### bigNumber
-
-The `bigNumber` method defines a number property that expects large numbers, such as prices.
-
-Use this property type when it's important to have high precision for numbers with large decimal places. Alternatively, for less percision, use the [float property](#float).
-
-For example:
-
-```ts highlights={bigNumberHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- price: model.bigNumber(),
- // ...
-})
-
-export default Post
-```
-
-### boolean
-
-The `boolean` method defines a boolean property.
-
-For example:
-
-```ts highlights={booleanHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- hasAccount: model.boolean(),
- // ...
-})
-
-export default Post
-```
-
-### enum
-
-The `enum` method defines a property whose value can only be one of the specified values.
-
-For example:
-
-```ts highlights={enumHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- color: model.enum(["black", "white"]),
- // ...
-})
-
-export default Post
-```
-
-The `enum` method accepts an array of possible string values.
-
-### dateTime
-
-The `dateTime` method defines a timestamp property.
-
-For example:
-
-```ts highlights={dateTimeHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- date_of_birth: model.dateTime(),
- // ...
-})
-
-export default Post
-```
-
-### json
-
-The `json` method defines a property whose value is a stringified JSON object.
-
-For example:
-
-```ts highlights={jsonHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- metadata: model.json(),
- // ...
-})
-
-export default Post
-```
-
-### array
-
-The `array` method defines an array of strings property.
-
-For example:
-
-```ts highlights={arrHightlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- names: model.array(),
- // ...
-})
-
-export default Post
-```
-
-### Properties Reference
-
-Refer to the [Data Model Language (DML) reference](https://docs.medusajs.com/resources/references/data-model/index.html.md) for a full reference of the properties.
-
-***
-
-## Set Primary Key Property
-
-To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method.
-
-For example:
-
-```ts highlights={highlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- id: model.id().primaryKey(),
- // ...
-})
-
-export default Post
-```
-
-In the example above, the `id` property is defined as the data model's primary key.
-
-***
-
-## Property Default Value
-
-Use the `default` method on a property's definition to specify the default value of a property.
-
-For example:
-
-```ts highlights={defaultHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- color: model
- .enum(["black", "white"])
- .default("black"),
- age: model
- .number()
- .default(0),
- // ...
-})
-
-export default Post
-```
-
-In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`.
-
-***
-
-## Make Property Optional
-
-Use the `nullable` method to indicate that a property’s value can be `null`. This is useful when you want a property to be optional.
-
-For example:
-
-```ts highlights={nullableHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- price: model.bigNumber().nullable(),
- // ...
-})
-
-export default Post
-```
-
-In the example above, the `price` property is configured to allow `null` values, making it optional.
-
-***
-
-## Unique Property
-
-The `unique` method indicates that a property’s value must be unique in the database through a unique index.
-
-For example:
-
-```ts highlights={uniqueHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const User = model.define("user", {
- email: model.text().unique(),
- // ...
-})
-
-export default User
-```
-
-In this example, multiple users can’t have the same email.
-
-***
-
-## Define Database Index on Property
-
-Use the `index` method on a property's definition to define a database index.
-
-For example:
-
-```ts highlights={dbIndexHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- id: model.id().primaryKey(),
- name: model.text().index(
- "IDX_MY_CUSTOM_NAME"
- ),
-})
-
-export default Post
-```
-
-The `index` method optionally accepts the name of the index as a parameter.
-
-In this example, you define an index on the `name` property.
-
-***
-
-## Define a Searchable Property
-
-Methods generated by the [service factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters.
-
-When the `q` filter is passed, the data model's searchable properties are queried to find matching records.
-
-Use the `searchable` method on a `text` property to indicate that it's searchable.
-
-For example:
-
-```ts highlights={searchableHighlights}
-import { model } from "@medusajs/framework/utils"
-
-const Post = model.define("post", {
- title: model.text().searchable(),
- // ...
-})
-
-export default Post
-```
-
-In this example, the `title` property is searchable.
-
-### Search Example
-
-If you pass a `q` filter to the `listPosts` method:
-
-```ts
-const posts = await blogModuleService.listPosts({
- q: "New Products",
-})
-```
-
-This retrieves records that include `New Products` in their `title` property.
-
-
-# Manage Relationships
-
-In this chapter, you'll learn how to manage relationships between data models when creating, updating, or retrieving records using the module's main service.
-
-## Manage One-to-One Relationship
-
-### BelongsTo Side of One-to-One
-
-When you create a record of a data model that belongs to another through a one-to-one relation, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
-
-For example, assuming you have the [User and Email data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-one-relationship/index.html.md), set an email's user ID as follows:
-
-```ts highlights={belongsHighlights}
-// when creating an email
-const email = await helloModuleService.createEmails({
- // other properties...
- user_id: "123",
-})
-
-// when updating an email
-const email = await helloModuleService.updateEmails({
- id: "321",
- // other properties...
- user_id: "123",
-})
-```
-
-In the example above, you pass the `user_id` property when creating or updating an email to specify the user it belongs to.
-
-### HasOne Side
-
-When you create a record of a data model that has one of another, pass the ID of the other data model's record in the relation property.
-
-For example, assuming you have the [User and Email data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-one-relationship/index.html.md), set a user's email ID as follows:
-
-```ts highlights={hasOneHighlights}
-// when creating a user
-const user = await helloModuleService.createUsers({
- // other properties...
- email: "123",
-})
-
-// when updating a user
-const user = await helloModuleService.updateUsers({
- id: "321",
- // other properties...
- email: "123",
-})
-```
-
-In the example above, you pass the `email` property when creating or updating a user to specify the email it has.
-
-***
-
-## Manage One-to-Many Relationship
-
-In a one-to-many relationship, you can only manage the associations from the `belongsTo` side.
-
-When you create a record of the data model on the `belongsTo` side, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
-
-For example, assuming you have the [Product and Store data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#one-to-many-relationship/index.html.md), set a product's store ID as follows:
-
-```ts highlights={manyBelongsHighlights}
-// when creating a product
-const product = await helloModuleService.createProducts({
- // other properties...
- store_id: "123",
-})
-
-// when updating a product
-const product = await helloModuleService.updateProducts({
- id: "321",
- // other properties...
- store_id: "123",
-})
-```
-
-In the example above, you pass the `store_id` property when creating or updating a product to specify the store it belongs to.
-
-***
-
-## Manage Many-to-Many Relationship
-
-If your many-to-many relation is represented with a [pivotEntity](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), refer to [this section](#manage-many-to-many-relationship-with-pivotentity) instead.
-
-### Create Associations
-
-When you create a record of a data model that has a many-to-many relationship to another data model, pass an array of IDs of the other data model's records in the relation property.
-
-For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), set the association between products and orders as follows:
-
-```ts highlights={manyHighlights}
-// when creating a product
-const product = await helloModuleService.createProducts({
- // other properties...
- orders: ["123", "321"],
-})
-
-// when creating an order
-const order = await helloModuleService.createOrders({
- id: "321",
- // other properties...
- products: ["123", "321"],
-})
-```
-
-In the example above, you pass the `orders` property when you create a product, and you pass the `products` property when you create an order.
-
-### Update Associations
-
-When you use the `update` methods generated by the service factory, you also pass an array of IDs as the relation property's value to add new associated records.
-
-However, this removes any existing associations to records whose IDs aren't included in the array.
-
-For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), you update the product's related orders as so:
-
-```ts
-const product = await helloModuleService.updateProducts({
- id: "123",
- // other properties...
- orders: ["321"],
-})
-```
-
-If the product was associated with an order, and you don't include that order's ID in the `orders` array, the association between the product and order is removed.
-
-So, to add a new association without removing existing ones, retrieve the product first to pass its associated orders when updating the product:
-
-```ts highlights={updateAssociationHighlights}
-const product = await helloModuleService.retrieveProduct(
- "123",
- {
- relations: ["orders"],
- }
-)
-
-const updatedProduct = await helloModuleService.updateProducts({
- id: product.id,
- // other properties...
- orders: [
- ...product.orders.map((order) => order.id),
- "321",
- ],
-})
-```
-
-This keeps existing associations between the product and orders, and adds a new one.
-
-***
-
-## Manage Many-to-Many Relationship with pivotEntity
-
-If your many-to-many relation is represented without a [pivotEntity](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), refer to [this section](#manage-many-to-many-relationship) instead.
-
-If you have a many-to-many relation with a `pivotEntity` specified, make sure to pass the data model representing the pivot table to [MedusaService](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) that your module's service extends.
-
-For example, assuming you have the [Order, Product, and OrderProduct models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns/index.html.md), add `OrderProduct` to `MedusaService`'s object parameter:
-
-```ts highlights={["4"]}
-class BlogModuleService extends MedusaService({
- Order,
- Product,
- OrderProduct,
-}) {}
-```
-
-This will generate Create, Read, Update and Delete (CRUD) methods for the `OrderProduct` data model, which you can use to create relations between orders and products and manage the extra columns in the pivot table.
-
-For example:
-
-```ts
-// create order-product association
-const orderProduct = await blogModuleService.createOrderProducts({
- order_id: "123",
- product_id: "123",
- metadata: {
- test: true,
- },
-})
-
-// update order-product association
-const orderProduct = await blogModuleService.updateOrderProducts({
- id: "123",
- metadata: {
- test: false,
- },
-})
-
-// delete order-product association
-await blogModuleService.deleteOrderProducts("123")
-```
-
-Since the `OrderProduct` data model belongs to the `Order` and `Product` data models, you can set its order and product as explained in the [one-to-many relationship section](#manage-one-to-many-relationship) using `order_id` and `product_id`.
-
-Refer to the [service factory reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) for a full list of generated methods and their usages.
-
-***
-
-## Retrieve Records of Relation
-
-The `list`, `listAndCount`, and `retrieve` methods of a module's main service accept as a second parameter an object of options.
-
-To retrieve the records associated with a data model's records through a relationship, pass in the second parameter object a `relations` property whose value is an array of relationship names.
-
-For example, assuming you have the [Order and Product data models from the previous chapter](https://docs.medusajs.com/learn/fundamentals/data-models/relationships#many-to-many-relationship/index.html.md), you retrieve a product's orders as follows:
-
-```ts highlights={retrieveHighlights}
-const product = await blogModuleService.retrieveProducts(
- "123",
- {
- relations: ["orders"],
- }
-)
-```
-
-In the example above, the retrieved product has an `orders` property, whose value is an array of orders associated with the product.
-
-
-# Migrations
-
-In this chapter, you'll learn what a migration is and how to generate a migration or write it manually.
-
-## What is a Migration?
-
-A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations are useful when you re-use a module or you're working in a team, so that when one member of a team makes a database change, everyone else can reflect it on their side by running the migrations.
-
-The migration's file has a class with two methods:
-
-- The `up` method reflects changes on the database.
-- The `down` method reverts the changes made in the `up` method.
-
-***
-
-## Generate Migration
-
-Instead of you writing the migration manually, the Medusa CLI tool provides a [db:generate](https://docs.medusajs.com/resources/medusa-cli/commands/db#dbgenerate/index.html.md) command to generate a migration for a modules' data models.
-
-For example, assuming you have a `blog` Module, you can generate a migration for it by running the following command:
-
-```bash
-npx medusa db:generate blog
-```
-
-This generates a migration file under the `migrations` directory of the Blog Module. You can then run it to reflect the changes in the database as mentioned in [this section](#run-the-migration).
-
-***
-
-## Write a Migration Manually
-
-You can also write migrations manually. To do that, create a file in the `migrations` directory of the module and in it, a class that has an `up` and `down` method. The class's name should be of the format `Migration{YEAR}{MONTH}{DAY}{HOUR}{MINUTE}.ts` to ensure migrations are ran in the correct order.
-
-For example:
-
-```ts title="src/modules/blog/migrations/Migration202507021059.ts"
-import { Migration } from "@mikro-orm/migrations"
-
-export class Migration202507021059 extends Migration {
-
- async up(): Promise {
- this.addSql("create table if not exists \"author\" (\"id\" text not null, \"name\" text not null, \"created_at\" timestamptz not null default now(), \"updated_at\" timestamptz not null default now(), \"deleted_at\" timestamptz null, constraint \"author_pkey\" primary key (\"id\"));")
- }
-
- async down(): Promise {
- this.addSql("drop table if exists \"author\" cascade;")
- }
-
-}
-```
-
-The migration class in the file extends the `Migration` class imported from `@mikro-orm/migrations`. In the `up` and `down` method of the migration class, you use the `addSql` method provided by MikroORM's `Migration` class to run PostgreSQL syntax.
-
-In the example above, the `up` method creates the table `author`, and the `down` method drops the table if the migration is reverted.
-
-Refer to [MikroORM's documentation](https://mikro-orm.io/docs/migrations#migration-class) for more details on writing migrations.
-
-***
-
-## Run the Migration
-
-To run your migration, run the following command:
-
-This command also syncs module links. If you don't want that, use the `--skip-links` option.
-
-```bash
-npx medusa db:migrate
-```
-
-This reflects the changes in the database as implemented in the migration's `up` method.
-
-***
-
-## Rollback the Migration
-
-To rollback or revert the last migration you ran for a module, run the following command:
-
-```bash
-npx medusa db:rollback blog
-```
-
-This rolls back the last ran migration on the Blog Module.
-
-### Caution: Rollback Migration before Deleting
-
-If you need to delete a migration file, make sure to rollback the migration first. Otherwise, you might encounter issues when generating and running new migrations.
-
-For example, if you delete the migration of the Blog Module, then try to create a new one, Medusa will create a brand new migration that re-creates the tables or indices. If those are still in the database, you might encounter errors.
-
-So, always rollback the migration before deleting it.
-
-***
-
-## More Database Commands
-
-To learn more about the Medusa CLI's database commands, refer to [this CLI reference](https://docs.medusajs.com/resources/medusa-cli/commands/db/index.html.md).
-
-
# Add Columns to a Link Table
In this chapter, you'll learn how to add custom columns to a link definition's table and manage them.
@@ -11354,6 +11354,67 @@ await link.create({
```
+# Module Link Direction
+
+In this chapter, you'll learn about the difference in module link directions, and which to use based on your use case.
+
+The details in this chapter don't apply to [Read-Only Module Links](https://docs.medusajs.com/learn/fundamentals/module-links/read-only/index.html.md). Refer to the [Read-Only Module Links chapter](https://docs.medusajs.com/learn/fundamentals/module-links/read-only/index.html.md) for more information on read-only links and their direction.
+
+## Link Direction
+
+The module link's direction depends on the order you pass the data model configuration parameters to `defineLink`.
+
+For example, the following defines a link from the Blog Module's `post` data model to the Product Module's `product` data model:
+
+```ts
+export default defineLink(
+ BlogModule.linkable.post,
+ ProductModule.linkable.product
+)
+```
+
+Whereas the following defines a link from the Product Module's `product` data model to the Blog Module's `post` data model:
+
+```ts
+export default defineLink(
+ ProductModule.linkable.product,
+ BlogModule.linkable.post
+)
+```
+
+The above links are two different links that serve different purposes.
+
+***
+
+## Which Link Direction to Use?
+
+### Extend Data Models
+
+If you're adding a link to a data model to extend it and add new fields, define the link from the main data model to the custom data model.
+
+For example, consider you want to add a `subtitle` custom field to the `product` data model. To do that, you define a `Subtitle` data model in your module, then define a link from the `Product` data model to it:
+
+```ts
+export default defineLink(
+ ProductModule.linkable.product,
+ BlogModule.linkable.subtitle
+)
+```
+
+### Associate Data Models
+
+If you're linking data models to indicate an association between them, define the link from the custom data model to the main data model.
+
+For example, consider you have `Post` data model representing a blog post, and you want to associate a blog post with a product. To do that, define a link from the `Post` data model to `Product`:
+
+```ts
+export default defineLink(
+ BlogModule.linkable.post,
+ ProductModule.linkable.product
+)
+```
+
+
# Query
In this chapter, you’ll learn about Query and how to use it to fetch data from modules.
@@ -11362,7 +11423,7 @@ In this chapter, you’ll learn about Query and how to use it to fetch data from
Query fetches data across modules. It’s a set of methods registered in the Medusa container under the `query` key.
-In all resources that can access the [Medusa Container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md), such as API routes or workflows, you can resolve Query to fetch data across custom modules and Medusa’s commerce modules.
+In all resources that can access the [Medusa Container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md), such as API routes or workflows, you can resolve Query to fetch data across custom modules and Medusa’s Commerce Modules.
***
@@ -11905,67 +11966,6 @@ Try passing one of the Query configuration parameters, like `fields` or `limit`,
Learn more about [specifing fields and relations](https://docs.medusajs.com/api/store#select-fields-and-relations) and [pagination](https://docs.medusajs.com/api/store#pagination) in the API reference.
-# Module Link Direction
-
-In this chapter, you'll learn about the difference in module link directions, and which to use based on your use case.
-
-The details in this chapter don't apply to [Read-Only Module Links](https://docs.medusajs.com/learn/fundamentals/module-links/read-only/index.html.md). Refer to the [Read-Only Module Links chapter](https://docs.medusajs.com/learn/fundamentals/module-links/read-only/index.html.md) for more information on read-only links and their direction.
-
-## Link Direction
-
-The module link's direction depends on the order you pass the data model configuration parameters to `defineLink`.
-
-For example, the following defines a link from the Blog Module's `post` data model to the Product Module's `product` data model:
-
-```ts
-export default defineLink(
- BlogModule.linkable.post,
- ProductModule.linkable.product
-)
-```
-
-Whereas the following defines a link from the Product Module's `product` data model to the Blog Module's `post` data model:
-
-```ts
-export default defineLink(
- ProductModule.linkable.product,
- BlogModule.linkable.post
-)
-```
-
-The above links are two different links that serve different purposes.
-
-***
-
-## Which Link Direction to Use?
-
-### Extend Data Models
-
-If you're adding a link to a data model to extend it and add new fields, define the link from the main data model to the custom data model.
-
-For example, consider you want to add a `subtitle` custom field to the `product` data model. To do that, you define a `Subtitle` data model in your module, then define a link from the `Product` data model to it:
-
-```ts
-export default defineLink(
- ProductModule.linkable.product,
- BlogModule.linkable.subtitle
-)
-```
-
-### Associate Data Models
-
-If you're linking data models to indicate an association between them, define the link from the custom data model to the main data model.
-
-For example, consider you have `Post` data model representing a blog post, and you want to associate a blog post with a product. To do that, define a link from the `Post` data model to `Product`:
-
-```ts
-export default defineLink(
- BlogModule.linkable.post,
- ProductModule.linkable.product
-)
-```
-
-
# Link
In this chapter, you’ll learn what Link is and how to use it to manage links.
@@ -12867,499 +12867,48 @@ If multiple posts have their `product_id` set to a product's ID, an array of pos
[Sanity Integration Tutorial](https://docs.medusajs.com/resources/integrations/guides/sanity/index.html.md).
-# Architectural Modules
+# Commerce Modules
-In this chapter, you’ll learn about architectural modules.
+In this chapter, you'll learn about Medusa's Commerce Modules.
-## What is an Architectural Module?
+## What is a Commerce Module?
-An architectural module implements features and mechanisms related to the Medusa application’s architecture and infrastructure.
+Commerce Modules are built-in [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more.
-Since modules are interchangeable, you have more control over Medusa’s architecture. For example, you can choose to use Memcached for event handling instead of Redis.
+Medusa's Commerce Modules are used to form Medusa's default [workflows](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md) and [APIs](https://docs.medusajs.com/api/store). For example, when you call the add to cart endpoint. the add to cart workflow runs which uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart.
+
+You'll find the details and steps of the add-to-cart workflow in [this workflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/addToCartWorkflow/index.html.md)
+
+The core commerce logic contained in Commerce Modules is also available directly when you are building customizations. This granular access to commerce functionality is unique and expands what's possible to build with Medusa drastically.
+
+### List of Medusa's Commerce Modules
+
+Refer to [this reference](https://docs.medusajs.com/resources/commerce-modules/index.html.md) for a full list of Commerce Modules in Medusa.
***
-## Architectural Module Types
+## Use Commerce Modules in Custom Flows
-There are different architectural module types including:
+Similar to your [custom modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), the Medusa application registers a Commerce Module's service in the [container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md). So, you can resolve it in your custom flows. This is useful as you build unique requirements extending core commerce features.
-
-
-- Cache Module: Defines the caching mechanism or logic to cache computational results.
-- Event Module: Integrates a pub/sub service to handle subscribing to and emitting events.
-- Workflow Engine Module: Integrates a service to store and track workflow executions and steps.
-- File Module: Integrates a storage service to handle uploading and managing files.
-- Notification Module: Integrates a third-party service or defines custom logic to send notifications to users and customers.
-
-***
-
-## Architectural Modules List
-
-Refer to the [Architectural Modules reference](https://docs.medusajs.com/resources/architectural-modules/index.html.md) for a list of Medusa’s architectural modules, available modules to install, and how to create an architectural module.
-
-
-# Create a Plugin
-
-In this chapter, you'll learn how to create a Medusa plugin and publish it.
-
-A [plugin](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md) is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community.
-
-Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
-
-## 1. Create a Plugin Project
-
-Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it.
-
-Medusa's `create-medusa-app` CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project:
-
-```bash
-npx create-medusa-app my-plugin --plugin
-```
-
-This will create a new Medusa plugin project in the `my-plugin` directory.
-
-### Plugin Directory Structure
-
-After the installation is done, the plugin structure will look like this:
-
-
-
-- `src/`: Contains the Medusa customizations.
-- `src/admin`: Contains [admin extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md).
-- `src/api`: Contains [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) and [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). You can add store, admin, or any custom API routes.
-- `src/jobs`: Contains [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md).
-- `src/links`: Contains [module links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
-- `src/modules`: Contains [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
-- `src/provider`: Contains [module providers](#create-module-providers).
-- `src/subscribers`: Contains [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md).
-- `src/workflows`: Contains [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). You can also add [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) under `src/workflows/hooks`.
-- `package.json`: Contains the plugin's package information, including general information and dependencies.
-- `tsconfig.json`: Contains the TypeScript configuration for the plugin.
-
-***
-
-## 2. Prepare Plugin
-
-### Package Name
-
-Before developing, testing, and publishing your plugin, make sure its name in `package.json` is correct. This is the name you'll use to install the plugin in your Medusa application.
-
-For example:
-
-```json title="package.json"
-{
- "name": "@myorg/plugin-name",
- // ...
-}
-```
-
-### Package Keywords
-
-Medusa scrapes NPM for a list of plugins that integrate third-party services, to later showcase them on the Medusa website. If you want your plugin to appear in that listing, make sure to add the `medusa-v2` and `medusa-plugin-integration` keywords to the `keywords` field in `package.json`.
-
-Only plugins that integrate third-party services are listed in the Medusa integrations page. If your plugin doesn't integrate a third-party service, you can skip this step.
-
-```json title="package.json"
-{
- "keywords": [
- "medusa-plugin-integration",
- "medusa-v2"
- ],
- // ...
-}
-```
-
-In addition, make sure to use one of the following keywords based on your integration type:
-
-|Keyword|Description|Example|
-|---|---|---|
-|\`medusa-plugin-analytics\`|Analytics service integration|Google Analytics|
-|\`medusa-plugin-auth\`|Authentication service integration|Auth0|
-|\`medusa-plugin-cms\`|CMS service integration|Contentful|
-|\`medusa-plugin-notification\`|Notification service integration|Twilio SMS|
-|\`medusa-plugin-payment\`|Payment service integration|PayPal|
-|\`medusa-plugin-search\`|Search service integration|MeiliSearch|
-|\`medusa-plugin-shipping\`|Shipping service integration|DHL|
-|\`medusa-plugin-other\`|Other third-party integrations|Sentry|
-
-### Package Dependencies
-
-Your plugin project will already have the dependencies mentioned in this section. Unless you made changes to the dependencies, you can skip this section.
-
-In the `package.json` file you must have the Medusa dependencies as `devDependencies` and `peerDependencies`. In addition, you must have `@swc/core` as a `devDependency`, as it's used by the plugin CLI tools.
-
-For example, assuming `2.5.0` is the latest Medusa version:
-
-```json title="package.json"
-{
- "devDependencies": {
- "@medusajs/admin-sdk": "2.5.0",
- "@medusajs/cli": "2.5.0",
- "@medusajs/framework": "2.5.0",
- "@medusajs/medusa": "2.5.0",
- "@medusajs/test-utils": "2.5.0",
- "@medusajs/ui": "4.0.4",
- "@medusajs/icons": "2.5.0",
- "@swc/core": "1.5.7",
- },
- "peerDependencies": {
- "@medusajs/admin-sdk": "2.5.0",
- "@medusajs/cli": "2.5.0",
- "@medusajs/framework": "2.5.0",
- "@medusajs/test-utils": "2.5.0",
- "@medusajs/medusa": "2.5.0",
- "@medusajs/ui": "4.0.3",
- "@medusajs/icons": "2.5.0",
- }
-}
-```
-
-### Package Exports
-
-Your plugin project will already have the exports mentioned in this section. Unless you made changes to the exports or you created your plugin before [Medusa v2.7.0](https://github.com/medusajs/medusa/releases/tag/v2.7.0), you can skip this section.
-
-In the `package.json` file, make sure your plugin has the following exports:
-
-```json title="package.json"
-{
- "exports": {
- "./package.json": "./package.json",
- "./workflows": "./.medusa/server/src/workflows/index.js",
- "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
- "./providers/*": "./.medusa/server/src/providers/*/index.js",
- "./admin": {
- "import": "./.medusa/server/src/admin/index.mjs",
- "require": "./.medusa/server/src/admin/index.js",
- "default": "./.medusa/server/src/admin/index.js"
- },
- "./*": "./.medusa/server/src/*.js"
- }
-}
-```
-
-Aside from the `./package.json`, `./providers`, and `./admin`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export.
-
-The plugin exports the following files and directories:
-
-- `./package.json`: The `package.json` file. Medusa needs to access the `package.json` when registering the plugin.
-- `./workflows`: The workflows exported in `./src/workflows/index.ts`.
-- `./.medusa/server/src/modules/*`: The definition file of modules. This is useful if you create links to the plugin's modules in the Medusa application.
-- `./providers/*`: The definition file of module providers. This is useful if your plugin includes a module provider, allowing you to register the plugin's providers in Medusa applications. Learn more in the [Create Module Providers](#create-module-providers) section.
-- `./admin`: The admin extensions exported in `./src/admin/index.ts`.
-- `./*`: Any other files in the plugin's `src` directory.
-
-***
-
-## 3. Publish Plugin Locally for Development and Testing
-
-Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it.
-
-### Publish and Install Local Package
-
-### Prerequisites
-
-- [Medusa application installed.](https://docs.medusajs.com/learn/installation/index.html.md)
-
-The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process.
-
-To publish the plugin to the local registry, run the following command in your plugin project:
-
-```bash title="Plugin project"
-npx medusa plugin:publish
-```
-
-This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`.
-
-Next, navigate to your Medusa application:
-
-```bash title="Medusa application"
-cd ~/path/to/medusa-app
-```
-
-Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application.
-
-Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency:
-
-```bash npm2yarn title="Medusa application"
-npm install --save-dev yalc
-```
-
-After that, run the following Medusa CLI command to install the plugin:
-
-```bash title="Medusa application"
-npx medusa plugin:add @myorg/plugin-name
-```
-
-Make sure to replace `@myorg/plugin-name` with the name of your plugin as specified in `package.json`. Your plugin will be installed from the local package registry into your Medusa application.
-
-### Register Plugin in Medusa Application
-
-After installing the plugin, you need to register it in your Medusa application in the configurations defined in `medusa-config.ts`.
-
-Add the plugin to the `plugins` array in the `medusa-config.ts` file:
-
-```ts title="medusa-config.ts" highlights={pluginHighlights}
-module.exports = defineConfig({
- // ...
- plugins: [
- {
- resolve: "@myorg/plugin-name",
- options: {},
- },
- ],
-})
-```
-
-The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package.
-
-#### Pass Module Options through Plugin
-
-Each plugin configuration also accepts an `options` property, whose value is an object of options to pass to the plugin's modules.
-
-For example:
-
-```ts title="medusa-config.ts" highlights={pluginOptionsHighlight}
-module.exports = defineConfig({
- // ...
- plugins: [
- {
- resolve: "@myorg/plugin-name",
- options: {
- apiKey: true,
- },
- },
- ],
-})
-```
-
-The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/options/index.html.md).
-
-### Watch Plugin Changes During Development
-
-While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development.
-
-To do that, run the following command in your plugin project:
-
-```bash title="Plugin project"
-npx medusa plugin:develop
-```
-
-This command will:
-
-- Watch for changes in the plugin. Whenever a file is changed, the plugin is automatically built.
-- Publish the plugin changes to the local package registry. This will automatically update the plugin in the Medusa application using it. You can also benefit from real-time HMR updates of admin extensions.
-
-### Start Medusa Application
-
-You can start your Medusa application's development server to test out your plugin:
-
-```bash npm2yarn title="Medusa application"
-npm run dev
-```
-
-While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application.
-
-***
-
-## 4. Create Customizations in the Plugin
-
-You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin.
-
-- [Create a module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md)
-- [Create a module link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md)
-- [Create a workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md)
-- [Add a workflow hook](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md)
-- [Create an API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md)
-- [Add a subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md)
-- [Add a scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md)
-- [Add an admin widget](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md)
-- [Add an admin UI route](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md)
-
-While building those customizations, you can test them in your Medusa application by [watching the plugin changes](#watch-plugin-changes-during-development) and [starting the Medusa application](#start-medusa-application).
-
-### Generating Migrations for Modules
-
-During your development, you may need to generate migrations for modules in your plugin. To do that, first, add the following environment variables in your plugin project:
-
-```plain title="Plugin project"
-DB_USERNAME=postgres
-DB_PASSWORD=123...
-DB_HOST=localhost
-DB_PORT=5432
-DB_NAME=db_name
-```
-
-You can add these environment variables in a `.env` file in your plugin project. The variables are:
-
-- `DB_USERNAME`: The username of the PostgreSQL user to connect to the database.
-- `DB_PASSWORD`: The password of the PostgreSQL user to connect to the database.
-- `DB_HOST`: The host of the PostgreSQL database. Typically, it's `localhost` if you're running the database locally.
-- `DB_PORT`: The port of the PostgreSQL database. Typically, it's `5432` if you're running the database locally.
-- `DB_NAME`: The name of the PostgreSQL database to connect to.
-
-Then, run the following command in your plugin project to generate migrations for the modules in your plugin:
-
-```bash title="Plugin project"
-npx medusa plugin:db:generate
-```
-
-This command generates migrations for all modules in the plugin.
-
-Finally, run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command:
-
-```bash title="Medusa application"
-npx medusa db:migrate
-```
-
-The migrations in your application, including your plugin, will run and update the database.
-
-### Importing Module Resources
-
-In the [Prepare Plugin](#2-prepare-plugin) section, you learned about [exported resources](#package-exports) in your plugin.
-
-These exports allow you to import your plugin resources in your Medusa application, including workflows, links and modules.
-
-For example, to import the plugin's workflow in your Medusa application:
-
-`@myorg/plugin-name` is the plugin package's name.
-
-```ts
-import { Workflow1, Workflow2 } from "@myorg/plugin-name/workflows"
-import BlogModule from "@myorg/plugin-name/modules/blog"
-// import other files created in plugin like ./src/types/blog.ts
-import BlogType from "@myorg/plugin-name/types/blog"
-```
-
-### Create Module Providers
-
-The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or architectural module.
-
-For example, assuming your plugin has a [Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification/index.html.md) called `my-notification`, you can register it in your Medusa application's configuration like this:
-
-`@myorg/plugin-name` is the plugin package's name.
-
-```ts highlights={[["9"]]} title="medusa-config.ts"
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/medusa/notification",
- options: {
- providers: [
- {
- resolve: "@myorg/plugin-name/providers/my-notification",
- id: "my-notification",
- options: {
- channels: ["email"],
- // provider options...
- },
- },
- ],
- },
- },
- ],
-})
-```
-
-You pass to `resolve` the path to the provider relative to the plugin package. So, in this example, the `my-notification` provider is located in `./src/providers/my-notification/index.ts` of the plugin.
-
-To learn how to create module providers, refer to the following guides:
-
-- [File Module Provider](https://docs.medusajs.com/resources/references/file-provider-module/index.html.md)
-- [Notification Module Provider](https://docs.medusajs.com/resources/references/notification-provider-module/index.html.md)
-- [Auth Module Provider](https://docs.medusajs.com/resources/references/auth/provider/index.html.md)
-- [Payment Module Provider](https://docs.medusajs.com/resources/references/payment/provider/index.html.md)
-- [Fulfillment Module Provider](https://docs.medusajs.com/resources/references/fulfillment/provider/index.html.md)
-- [Tax Module Provider](https://docs.medusajs.com/resources/references/tax/provider/index.html.md)
-
-***
-
-## 5. Publish Plugin to NPM
-
-Make sure to add the keywords mentioned in the [Package Keywords](#package-keywords) section in your plugin's `package.json` file.
-
-Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project:
-
-```bash
-npx medusa plugin:build
-```
-
-The command will compile an output in the `.medusa/server` directory.
-
-You can now publish the plugin to npm using the [NPM CLI tool](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Run the following command to publish the plugin to npm:
-
-```bash
-npm publish
-```
-
-If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application.
-
-### Install Public Plugin in Medusa Application
-
-You install a plugin that's published publicly using your package manager. For example:
-
-```bash npm2yarn
-npm install @myorg/plugin-name
-```
-
-Where `@myorg/plugin-name` is the name of your plugin as published on NPM.
-
-Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application).
-
-***
-
-## Update a Published Plugin
-
-To update the Medusa dependencies in a plugin, refer to [this documentation](https://docs.medusajs.com/learn/update#update-plugin-project/index.html.md).
-
-If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again.
-
-First, run the following command to change the version of the plugin:
-
-```bash
-npm version
-```
-
-Where `` indicates the type of version update you’re publishing. For example, it can be `major` or `minor`. Refer to the [npm version documentation](https://docs.npmjs.com/cli/v10/commands/npm-version) for more information.
-
-Then, re-run the same commands for publishing a plugin:
-
-```bash
-npx medusa plugin:build
-npm publish
-```
-
-This will publish an updated version of your plugin under a new version.
-
-
-# Scheduled Jobs Number of Executions
-
-In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed.
-
-## numberOfExecutions Option
-
-The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime.
-
-For example:
+For example, consider you have a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) (a special function that performs a task in a series of steps with rollback mechanism) that needs a step to retrieve the total number of products. You can create a step in the workflow that resolves the Product Module's service from the container to use its methods:
```ts highlights={highlights}
-export default async function myCustomJob() {
- console.log("I'll be executed three times only.")
-}
+import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
-export const config = {
- name: "hello-world",
- // execute every minute
- schedule: "* * * * *",
- numberOfExecutions: 3,
-}
+export const countProductsStep = createStep(
+ "count-products",
+ async ({ }, { container }) => {
+ const productModuleService = container.resolve("product")
+
+ const [,count] = await productModuleService.listAndCountProducts()
+
+ return new StepResponse(count)
+ }
+)
```
-The above scheduled job has the `numberOfExecutions` configuration set to `3`.
-
-So, it'll only execute 3 times, each every minute, then it won't be executed anymore.
-
-If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified.
+Your workflow can use services of both custom and Commerce Modules, supporting you in building custom flows without having to re-build core commerce features.
# Module Container
@@ -13428,50 +12977,6 @@ export default async function helloWorldLoader({
```
-# Commerce Modules
-
-In this chapter, you'll learn about Medusa's commerce modules.
-
-## What is a Commerce Module?
-
-Commerce modules are built-in [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more.
-
-Medusa's commerce modules are used to form Medusa's default [workflows](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md) and [APIs](https://docs.medusajs.com/api/store). For example, when you call the add to cart endpoint. the add to cart workflow runs which uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart.
-
-You'll find the details and steps of the add-to-cart workflow in [this workflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/addToCartWorkflow/index.html.md)
-
-The core commerce logic contained in Commerce Modules is also available directly when you are building customizations. This granular access to commerce functionality is unique and expands what's possible to build with Medusa drastically.
-
-### List of Medusa's Commerce Modules
-
-Refer to [this reference](https://docs.medusajs.com/resources/commerce-modules/index.html.md) for a full list of commerce modules in Medusa.
-
-***
-
-## Use Commerce Modules in Custom Flows
-
-Similar to your [custom modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), the Medusa application registers a commerce module's service in the [container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md). So, you can resolve it in your custom flows. This is useful as you build unique requirements extending core commerce features.
-
-For example, consider you have a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) (a special function that performs a task in a series of steps with rollback mechanism) that needs a step to retrieve the total number of products. You can create a step in the workflow that resolves the Product Module's service from the container to use its methods:
-
-```ts highlights={highlights}
-import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
-
-export const countProductsStep = createStep(
- "count-products",
- async ({ }, { container }) => {
- const productModuleService = container.resolve("product")
-
- const [,count] = await productModuleService.listAndCountProducts()
-
- return new StepResponse(count)
- }
-)
-```
-
-Your workflow can use services of both custom and commerce modules, supporting you in building custom flows without having to re-build core commerce features.
-
-
# Perform Database Operations in a Service
In this chapter, you'll learn how to perform database operations in a module's service.
@@ -13932,105 +13437,36 @@ class BlogModuleService {
```
-# Module Isolation
+# Infrastructure Modules
-In this chapter, you'll learn how modules are isolated, and what that means for your custom development.
+In this chapter, you’ll learn about Infrastructure Modules.
-- Modules can't access resources, such as services or data models, from other modules.
-- Use Medusa's linking concepts, as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), to extend a module's data models and retrieve data across modules.
+## What is an Infrastructure Module?
-## How are Modules Isolated?
+An Infrastructure Module implements features and mechanisms related to the Medusa application’s architecture and infrastructure.
-A module is unaware of any resources other than its own, such as services or data models. This means it can't access these resources if they're implemented in another module.
-
-For example, your custom module can't resolve the Product Module's main service or have direct relationships from its data model to the Product Module's data models.
+Since modules are interchangeable, you have more control over Medusa’s architecture. For example, you can choose to use Memcached for event handling instead of Redis.
***
-## Why are Modules Isolated
+## Infrastructure Module Types
-Some of the module isolation's benefits include:
+There are different Infrastructure Module types including:
-- Integrate your module into any Medusa application without side-effects to your setup.
-- Replace existing modules with your custom implementation, if your use case is drastically different.
-- Use modules in other environments, such as Edge functions and Next.js apps.
+
+
+- Cache Module: Defines the caching mechanism or logic to cache computational results.
+- Event Module: Integrates a pub/sub service to handle subscribing to and emitting events.
+- Workflow Engine Module: Integrates a service to store and track workflow executions and steps.
+- File Module: Integrates a storage service to handle uploading and managing files.
+- Notification Module: Integrates a third-party service or defines custom logic to send notifications to users and customers.
+- Locking Module: Integrates a service that manages access to shared resources by multiple processes or threads.
***
-## How to Extend Data Model of Another Module?
+## Infrastructure Modules List
-To extend the data model of another module, such as the `product` data model of the Product Module, use Medusa's linking concepts as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
-
-***
-
-## How to Use Services of Other Modules?
-
-If you're building a feature that uses functionalities from different modules, use a workflow whose steps resolve the modules' services to perform these functionalities.
-
-Workflows ensure data consistency through their roll-back mechanism and tracking of each execution's status, steps, input, and output.
-
-### Example
-
-For example, consider you have two modules:
-
-1. A module that stores and manages brands in your application.
-2. A module that integrates a third-party Content Management System (CMS).
-
-To sync brands from your application to the third-party system, create the following steps:
-
-```ts title="Example Steps" highlights={stepsHighlights}
-const retrieveBrandsStep = createStep(
- "retrieve-brands",
- async (_, { container }) => {
- const brandModuleService = container.resolve(
- "brandModuleService"
- )
-
- const brands = await brandModuleService.listBrands()
-
- return new StepResponse(brands)
- }
-)
-
-const createBrandsInCmsStep = createStep(
- "create-brands-in-cms",
- async ({ brands }, { container }) => {
- const cmsModuleService = container.resolve(
- "cmsModuleService"
- )
-
- const cmsBrands = await cmsModuleService.createBrands(brands)
-
- return new StepResponse(cmsBrands, cmsBrands)
- },
- async (brands, { container }) => {
- const cmsModuleService = container.resolve(
- "cmsModuleService"
- )
-
- await cmsModuleService.deleteBrands(
- brands.map((brand) => brand.id)
- )
- }
-)
-```
-
-The `retrieveBrandsStep` retrieves the brands from a brand module, and the `createBrandsInCmsStep` creates the brands in a third-party system using a CMS module.
-
-Then, create the following workflow that uses these steps:
-
-```ts title="Example Workflow"
-export const syncBrandsWorkflow = createWorkflow(
- "sync-brands",
- () => {
- const brands = retrieveBrandsStep()
-
- createBrandsInCmsStep({ brands })
- }
-)
-```
-
-You can then use this workflow in an API route, scheduled job, or other resources that use this functionality.
+Refer to the [Infrastructure Modules reference](https://docs.medusajs.com/resources/infrastructure-modules/index.html.md) for a list of Medusa’s Infrastructure Modules, available modules to install, and how to create an Infrastructure Module.
# Loaders
@@ -14276,6 +13712,107 @@ info: Connected to MongoDB
You can now resolve the MongoDB Module's main service in your customizations to perform operations on the MongoDB database.
+# Module Isolation
+
+In this chapter, you'll learn how modules are isolated, and what that means for your custom development.
+
+- Modules can't access resources, such as services or data models, from other modules.
+- Use Medusa's linking concepts, as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md), to extend a module's data models and retrieve data across modules.
+
+## How are Modules Isolated?
+
+A module is unaware of any resources other than its own, such as services or data models. This means it can't access these resources if they're implemented in another module.
+
+For example, your custom module can't resolve the Product Module's main service or have direct relationships from its data model to the Product Module's data models.
+
+***
+
+## Why are Modules Isolated
+
+Some of the module isolation's benefits include:
+
+- Integrate your module into any Medusa application without side-effects to your setup.
+- Replace existing modules with your custom implementation, if your use case is drastically different.
+- Use modules in other environments, such as Edge functions and Next.js apps.
+
+***
+
+## How to Extend Data Model of Another Module?
+
+To extend the data model of another module, such as the `product` data model of the Product Module, use Medusa's linking concepts as explained in the [Module Links chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
+
+***
+
+## How to Use Services of Other Modules?
+
+If you're building a feature that uses functionalities from different modules, use a workflow whose steps resolve the modules' services to perform these functionalities.
+
+Workflows ensure data consistency through their roll-back mechanism and tracking of each execution's status, steps, input, and output.
+
+### Example
+
+For example, consider you have two modules:
+
+1. A module that stores and manages brands in your application.
+2. A module that integrates a third-party Content Management System (CMS).
+
+To sync brands from your application to the third-party system, create the following steps:
+
+```ts title="Example Steps" highlights={stepsHighlights}
+const retrieveBrandsStep = createStep(
+ "retrieve-brands",
+ async (_, { container }) => {
+ const brandModuleService = container.resolve(
+ "brandModuleService"
+ )
+
+ const brands = await brandModuleService.listBrands()
+
+ return new StepResponse(brands)
+ }
+)
+
+const createBrandsInCmsStep = createStep(
+ "create-brands-in-cms",
+ async ({ brands }, { container }) => {
+ const cmsModuleService = container.resolve(
+ "cmsModuleService"
+ )
+
+ const cmsBrands = await cmsModuleService.createBrands(brands)
+
+ return new StepResponse(cmsBrands, cmsBrands)
+ },
+ async (brands, { container }) => {
+ const cmsModuleService = container.resolve(
+ "cmsModuleService"
+ )
+
+ await cmsModuleService.deleteBrands(
+ brands.map((brand) => brand.id)
+ )
+ }
+)
+```
+
+The `retrieveBrandsStep` retrieves the brands from a brand module, and the `createBrandsInCmsStep` creates the brands in a third-party system using a CMS module.
+
+Then, create the following workflow that uses these steps:
+
+```ts title="Example Workflow"
+export const syncBrandsWorkflow = createWorkflow(
+ "sync-brands",
+ () => {
+ const brands = retrieveBrandsStep()
+
+ createBrandsInCmsStep({ brands })
+ }
+)
+```
+
+You can then use this workflow in an API route, scheduled job, or other resources that use this functionality.
+
+
# Modules Directory Structure
In this document, you'll learn about the expected files and directories in your module.
@@ -14809,6 +14346,828 @@ export default BlogModuleService
```
+# Create a Plugin
+
+In this chapter, you'll learn how to create a Medusa plugin and publish it.
+
+A [plugin](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md) is a package of reusable Medusa customizations that you can install in any Medusa application. By creating and publishing a plugin, you can reuse your Medusa customizations across multiple projects or share them with the community.
+
+Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
+
+## 1. Create a Plugin Project
+
+Plugins are created in a separate Medusa project. This makes the development and publishing of the plugin easier. Later, you'll install that plugin in your Medusa application to test it out and use it.
+
+Medusa's `create-medusa-app` CLI tool provides the option to create a plugin project. Run the following command to create a new plugin project:
+
+```bash
+npx create-medusa-app my-plugin --plugin
+```
+
+This will create a new Medusa plugin project in the `my-plugin` directory.
+
+### Plugin Directory Structure
+
+After the installation is done, the plugin structure will look like this:
+
+
+
+- `src/`: Contains the Medusa customizations.
+- `src/admin`: Contains [admin extensions](https://docs.medusajs.com/learn/fundamentals/admin/index.html.md).
+- `src/api`: Contains [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) and [middlewares](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). You can add store, admin, or any custom API routes.
+- `src/jobs`: Contains [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md).
+- `src/links`: Contains [module links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md).
+- `src/modules`: Contains [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md).
+- `src/provider`: Contains [module providers](#create-module-providers).
+- `src/subscribers`: Contains [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md).
+- `src/workflows`: Contains [workflows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). You can also add [hooks](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md) under `src/workflows/hooks`.
+- `package.json`: Contains the plugin's package information, including general information and dependencies.
+- `tsconfig.json`: Contains the TypeScript configuration for the plugin.
+
+***
+
+## 2. Prepare Plugin
+
+### Package Name
+
+Before developing, testing, and publishing your plugin, make sure its name in `package.json` is correct. This is the name you'll use to install the plugin in your Medusa application.
+
+For example:
+
+```json title="package.json"
+{
+ "name": "@myorg/plugin-name",
+ // ...
+}
+```
+
+### Package Keywords
+
+Medusa scrapes NPM for a list of plugins that integrate third-party services, to later showcase them on the Medusa website. If you want your plugin to appear in that listing, make sure to add the `medusa-v2` and `medusa-plugin-integration` keywords to the `keywords` field in `package.json`.
+
+Only plugins that integrate third-party services are listed in the Medusa integrations page. If your plugin doesn't integrate a third-party service, you can skip this step.
+
+```json title="package.json"
+{
+ "keywords": [
+ "medusa-plugin-integration",
+ "medusa-v2"
+ ],
+ // ...
+}
+```
+
+In addition, make sure to use one of the following keywords based on your integration type:
+
+|Keyword|Description|Example|
+|---|---|---|
+|\`medusa-plugin-analytics\`|Analytics service integration|Google Analytics|
+|\`medusa-plugin-auth\`|Authentication service integration|Auth0|
+|\`medusa-plugin-cms\`|CMS service integration|Contentful|
+|\`medusa-plugin-notification\`|Notification service integration|Twilio SMS|
+|\`medusa-plugin-payment\`|Payment service integration|PayPal|
+|\`medusa-plugin-search\`|Search service integration|MeiliSearch|
+|\`medusa-plugin-shipping\`|Shipping service integration|DHL|
+|\`medusa-plugin-other\`|Other third-party integrations|Sentry|
+
+### Package Dependencies
+
+Your plugin project will already have the dependencies mentioned in this section. Unless you made changes to the dependencies, you can skip this section.
+
+In the `package.json` file you must have the Medusa dependencies as `devDependencies` and `peerDependencies`. In addition, you must have `@swc/core` as a `devDependency`, as it's used by the plugin CLI tools.
+
+For example, assuming `2.5.0` is the latest Medusa version:
+
+```json title="package.json"
+{
+ "devDependencies": {
+ "@medusajs/admin-sdk": "2.5.0",
+ "@medusajs/cli": "2.5.0",
+ "@medusajs/framework": "2.5.0",
+ "@medusajs/medusa": "2.5.0",
+ "@medusajs/test-utils": "2.5.0",
+ "@medusajs/ui": "4.0.4",
+ "@medusajs/icons": "2.5.0",
+ "@swc/core": "1.5.7",
+ },
+ "peerDependencies": {
+ "@medusajs/admin-sdk": "2.5.0",
+ "@medusajs/cli": "2.5.0",
+ "@medusajs/framework": "2.5.0",
+ "@medusajs/test-utils": "2.5.0",
+ "@medusajs/medusa": "2.5.0",
+ "@medusajs/ui": "4.0.3",
+ "@medusajs/icons": "2.5.0",
+ }
+}
+```
+
+### Package Exports
+
+Your plugin project will already have the exports mentioned in this section. Unless you made changes to the exports or you created your plugin before [Medusa v2.7.0](https://github.com/medusajs/medusa/releases/tag/v2.7.0), you can skip this section.
+
+In the `package.json` file, make sure your plugin has the following exports:
+
+```json title="package.json"
+{
+ "exports": {
+ "./package.json": "./package.json",
+ "./workflows": "./.medusa/server/src/workflows/index.js",
+ "./.medusa/server/src/modules/*": "./.medusa/server/src/modules/*/index.js",
+ "./providers/*": "./.medusa/server/src/providers/*/index.js",
+ "./admin": {
+ "import": "./.medusa/server/src/admin/index.mjs",
+ "require": "./.medusa/server/src/admin/index.js",
+ "default": "./.medusa/server/src/admin/index.js"
+ },
+ "./*": "./.medusa/server/src/*.js"
+ }
+}
+```
+
+Aside from the `./package.json`, `./providers`, and `./admin`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export.
+
+The plugin exports the following files and directories:
+
+- `./package.json`: The `package.json` file. Medusa needs to access the `package.json` when registering the plugin.
+- `./workflows`: The workflows exported in `./src/workflows/index.ts`.
+- `./.medusa/server/src/modules/*`: The definition file of modules. This is useful if you create links to the plugin's modules in the Medusa application.
+- `./providers/*`: The definition file of module providers. This is useful if your plugin includes a module provider, allowing you to register the plugin's providers in Medusa applications. Learn more in the [Create Module Providers](#create-module-providers) section.
+- `./admin`: The admin extensions exported in `./src/admin/index.ts`.
+- `./*`: Any other files in the plugin's `src` directory.
+
+***
+
+## 3. Publish Plugin Locally for Development and Testing
+
+Medusa's CLI tool provides commands to simplify developing and testing your plugin in a local Medusa application. You start by publishing your plugin in the local package registry, then install it in your Medusa application. You can then watch for changes in the plugin as you develop it.
+
+### Publish and Install Local Package
+
+### Prerequisites
+
+- [Medusa application installed.](https://docs.medusajs.com/learn/installation/index.html.md)
+
+The first time you create your plugin, you need to publish the package into a local package registry, then install it in your Medusa application. This is a one-time only process.
+
+To publish the plugin to the local registry, run the following command in your plugin project:
+
+```bash title="Plugin project"
+npx medusa plugin:publish
+```
+
+This command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. The plugin is published locally under the name you specified in `package.json`.
+
+Next, navigate to your Medusa application:
+
+```bash title="Medusa application"
+cd ~/path/to/medusa-app
+```
+
+Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application.
+
+Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency:
+
+```bash npm2yarn title="Medusa application"
+npm install --save-dev yalc
+```
+
+After that, run the following Medusa CLI command to install the plugin:
+
+```bash title="Medusa application"
+npx medusa plugin:add @myorg/plugin-name
+```
+
+Make sure to replace `@myorg/plugin-name` with the name of your plugin as specified in `package.json`. Your plugin will be installed from the local package registry into your Medusa application.
+
+### Register Plugin in Medusa Application
+
+After installing the plugin, you need to register it in your Medusa application in the configurations defined in `medusa-config.ts`.
+
+Add the plugin to the `plugins` array in the `medusa-config.ts` file:
+
+```ts title="medusa-config.ts" highlights={pluginHighlights}
+module.exports = defineConfig({
+ // ...
+ plugins: [
+ {
+ resolve: "@myorg/plugin-name",
+ options: {},
+ },
+ ],
+})
+```
+
+The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package.
+
+#### Pass Module Options through Plugin
+
+Each plugin configuration also accepts an `options` property, whose value is an object of options to pass to the plugin's modules.
+
+For example:
+
+```ts title="medusa-config.ts" highlights={pluginOptionsHighlight}
+module.exports = defineConfig({
+ // ...
+ plugins: [
+ {
+ resolve: "@myorg/plugin-name",
+ options: {
+ apiKey: true,
+ },
+ },
+ ],
+})
+```
+
+The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/options/index.html.md).
+
+### Watch Plugin Changes During Development
+
+While developing your plugin, you can watch for changes in the plugin and automatically update the plugin in the Medusa application using it. This is the only command you'll continuously need during your plugin development.
+
+To do that, run the following command in your plugin project:
+
+```bash title="Plugin project"
+npx medusa plugin:develop
+```
+
+This command will:
+
+- Watch for changes in the plugin. Whenever a file is changed, the plugin is automatically built.
+- Publish the plugin changes to the local package registry. This will automatically update the plugin in the Medusa application using it. You can also benefit from real-time HMR updates of admin extensions.
+
+### Start Medusa Application
+
+You can start your Medusa application's development server to test out your plugin:
+
+```bash npm2yarn title="Medusa application"
+npm run dev
+```
+
+While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application.
+
+***
+
+## 4. Create Customizations in the Plugin
+
+You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin.
+
+- [Create a module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md)
+- [Create a module link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md)
+- [Create a workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md)
+- [Add a workflow hook](https://docs.medusajs.com/learn/fundamentals/workflows/add-workflow-hook/index.html.md)
+- [Create an API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md)
+- [Add a subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md)
+- [Add a scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md)
+- [Add an admin widget](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md)
+- [Add an admin UI route](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md)
+
+While building those customizations, you can test them in your Medusa application by [watching the plugin changes](#watch-plugin-changes-during-development) and [starting the Medusa application](#start-medusa-application).
+
+### Generating Migrations for Modules
+
+During your development, you may need to generate migrations for modules in your plugin. To do that, first, add the following environment variables in your plugin project:
+
+```plain title="Plugin project"
+DB_USERNAME=postgres
+DB_PASSWORD=123...
+DB_HOST=localhost
+DB_PORT=5432
+DB_NAME=db_name
+```
+
+You can add these environment variables in a `.env` file in your plugin project. The variables are:
+
+- `DB_USERNAME`: The username of the PostgreSQL user to connect to the database.
+- `DB_PASSWORD`: The password of the PostgreSQL user to connect to the database.
+- `DB_HOST`: The host of the PostgreSQL database. Typically, it's `localhost` if you're running the database locally.
+- `DB_PORT`: The port of the PostgreSQL database. Typically, it's `5432` if you're running the database locally.
+- `DB_NAME`: The name of the PostgreSQL database to connect to.
+
+Then, run the following command in your plugin project to generate migrations for the modules in your plugin:
+
+```bash title="Plugin project"
+npx medusa plugin:db:generate
+```
+
+This command generates migrations for all modules in the plugin.
+
+Finally, run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command:
+
+```bash title="Medusa application"
+npx medusa db:migrate
+```
+
+The migrations in your application, including your plugin, will run and update the database.
+
+### Importing Module Resources
+
+In the [Prepare Plugin](#2-prepare-plugin) section, you learned about [exported resources](#package-exports) in your plugin.
+
+These exports allow you to import your plugin resources in your Medusa application, including workflows, links and modules.
+
+For example, to import the plugin's workflow in your Medusa application:
+
+`@myorg/plugin-name` is the plugin package's name.
+
+```ts
+import { Workflow1, Workflow2 } from "@myorg/plugin-name/workflows"
+import BlogModule from "@myorg/plugin-name/modules/blog"
+// import other files created in plugin like ./src/types/blog.ts
+import BlogType from "@myorg/plugin-name/types/blog"
+```
+
+### Create Module Providers
+
+The [exported resources](#package-exports) also allow you to import module providers in your plugin and register them in the Medusa application's configuration. A module provider is a module that provides the underlying logic or integration related to a commerce or Infrastructure Module.
+
+For example, assuming your plugin has a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification/index.html.md) called `my-notification`, you can register it in your Medusa application's configuration like this:
+
+`@myorg/plugin-name` is the plugin package's name.
+
+```ts highlights={[["9"]]} title="medusa-config.ts"
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/medusa/notification",
+ options: {
+ providers: [
+ {
+ resolve: "@myorg/plugin-name/providers/my-notification",
+ id: "my-notification",
+ options: {
+ channels: ["email"],
+ // provider options...
+ },
+ },
+ ],
+ },
+ },
+ ],
+})
+```
+
+You pass to `resolve` the path to the provider relative to the plugin package. So, in this example, the `my-notification` provider is located in `./src/providers/my-notification/index.ts` of the plugin.
+
+To learn how to create module providers, refer to the following guides:
+
+- [File Module Provider](https://docs.medusajs.com/resources/references/file-provider-module/index.html.md)
+- [Notification Module Provider](https://docs.medusajs.com/resources/references/notification-provider-module/index.html.md)
+- [Auth Module Provider](https://docs.medusajs.com/resources/references/auth/provider/index.html.md)
+- [Payment Module Provider](https://docs.medusajs.com/resources/references/payment/provider/index.html.md)
+- [Fulfillment Module Provider](https://docs.medusajs.com/resources/references/fulfillment/provider/index.html.md)
+- [Tax Module Provider](https://docs.medusajs.com/resources/references/tax/provider/index.html.md)
+
+***
+
+## 5. Publish Plugin to NPM
+
+Make sure to add the keywords mentioned in the [Package Keywords](#package-keywords) section in your plugin's `package.json` file.
+
+Medusa's CLI tool provides a command that bundles your plugin to be published to npm. Once you're ready to publish your plugin publicly, run the following command in your plugin project:
+
+```bash
+npx medusa plugin:build
+```
+
+The command will compile an output in the `.medusa/server` directory.
+
+You can now publish the plugin to npm using the [NPM CLI tool](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm). Run the following command to publish the plugin to npm:
+
+```bash
+npm publish
+```
+
+If you haven't logged in before with your NPM account, you'll be asked to log in first. Then, your package is published publicly to be used in any Medusa application.
+
+### Install Public Plugin in Medusa Application
+
+You install a plugin that's published publicly using your package manager. For example:
+
+```bash npm2yarn
+npm install @myorg/plugin-name
+```
+
+Where `@myorg/plugin-name` is the name of your plugin as published on NPM.
+
+Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application).
+
+***
+
+## Update a Published Plugin
+
+To update the Medusa dependencies in a plugin, refer to [this documentation](https://docs.medusajs.com/learn/update#update-plugin-project/index.html.md).
+
+If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again.
+
+First, run the following command to change the version of the plugin:
+
+```bash
+npm version
+```
+
+Where `` indicates the type of version update you’re publishing. For example, it can be `major` or `minor`. Refer to the [npm version documentation](https://docs.npmjs.com/cli/v10/commands/npm-version) for more information.
+
+Then, re-run the same commands for publishing a plugin:
+
+```bash
+npx medusa plugin:build
+npm publish
+```
+
+This will publish an updated version of your plugin under a new version.
+
+
+# Scheduled Jobs Number of Executions
+
+In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed.
+
+## numberOfExecutions Option
+
+The export configuration object of the scheduled job accepts an optional property `numberOfExecutions`. Its value is a number indicating how many times the scheduled job can be executed during the Medusa application's runtime.
+
+For example:
+
+```ts highlights={highlights}
+export default async function myCustomJob() {
+ console.log("I'll be executed three times only.")
+}
+
+export const config = {
+ name: "hello-world",
+ // execute every minute
+ schedule: "* * * * *",
+ numberOfExecutions: 3,
+}
+```
+
+The above scheduled job has the `numberOfExecutions` configuration set to `3`.
+
+So, it'll only execute 3 times, each every minute, then it won't be executed anymore.
+
+If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified.
+
+
+# Translate Medusa Admin
+
+The Medusa Admin supports multiple languages, with the default being English. In this documentation, you'll learn how to contribute to the community by translating the Medusa Admin to a language you're fluent in.
+
+{/* vale docs.We = NO */}
+
+You can contribute either by translating the admin to a new language, or fixing translations for existing languages. As we can't validate every language's translations, some translations may be incorrect. Your contribution is welcome to fix any translation errors you find.
+
+{/* vale docs.We = YES */}
+
+Check out the translated languages either in the admin dashboard's settings or on [GitHub](https://github.com/medusajs/medusa/blob/develop/packages/admin/dashboard/src/i18n/languages.ts).
+
+***
+
+## How to Contribute Translation
+
+1. Clone the [Medusa monorepository](https://github.com/medusajs/medusa) to your local machine:
+
+```bash
+git clone https://github.com/medusajs/medusa.git
+```
+
+If you already have it cloned, make sure to pull the latest changes from the `develop` branch.
+
+2. Install the monorepository's dependencies. Since it's a Yarn workspace, it's highly recommended to use yarn:
+
+```bash
+yarn install
+```
+
+3. Create a branch that you'll use to open the pull request later:
+
+```bash
+git checkout -b feat/translate-
+```
+
+Where `` is your language name. For example, `feat/translate-da`.
+
+4. Translation files are under `packages/admin/dashboard/src/i18n/translations` as JSON files whose names are the ISO-2 name of the language.
+ - If you're adding a new language, copy the file `packages/admin/dashboard/src/i18n/translations/en.json` and paste it with the ISO-2 name for your language. For example, if you're adding Danish translations, copy the `en.json` file and paste it as `packages/admin/dashboard/src/i18n/translations/de.json`.
+ - If you're fixing a translation, find the JSON file of the language under `packages/admin/dashboard/src/i18n/translations`.
+
+5. Start translating the keys in the JSON file (or updating the targeted ones). All keys in the JSON file must be translated, and your PR tests will fail otherwise.
+ - You can check whether the JSON file is valid by running the following command in `packages/admin/dashboard`, replacing `da.json` with the JSON file's name:
+
+```bash title="packages/admin/dashboard"
+yarn i18n:validate da.json
+```
+
+6. After finishing the translation, if you're adding a new language, import its JSON file in `packages/admin/dashboard/src/i18n/translations/index.ts` and add it to the exported object:
+
+```ts title="packages/admin/dashboard/src/i18n/translations/index.ts" highlights={[["2"], ["6"], ["7"], ["8"]]}
+// other imports...
+import da from "./da.json"
+
+export default {
+ // other languages...
+ da: {
+ translation: da,
+ },
+}
+```
+
+The language's key in the object is the ISO-2 name of the language.
+
+7. If you're adding a new language, add it to the file `packages/admin/dashboard/src/i18n/languages.ts`:
+
+```ts title="packages/admin/dashboard/src/i18n/languages.ts" highlights={languageHighlights}
+import { da } from "date-fns/locale"
+// other imports...
+
+export const languages: Language[] = [
+ // other languages...
+ {
+ code: "da",
+ display_name: "Danish",
+ ltr: true,
+ date_locale: da,
+ },
+]
+```
+
+`languages` is an array having the following properties:
+
+- `code`: The ISO-2 name of the language. For example, `da` for Danish.
+- `display_name`: The language's name to be displayed in the admin.
+- `ltr`: Whether the language supports a left-to-right layout. For example, set this to `false` for languages like Arabic.
+- `date_locale`: An instance of the locale imported from the [date-fns/locale](https://date-fns.org/) package.
+
+8. Once you're done, push the changes into your branch and open a pull request on GitHub.
+
+Our team will perform a general review on your PR and merge it if no issues are found. The translation will be available in the admin after the next release.
+
+
+# Docs Contribution Guidelines
+
+Thank you for your interest in contributing to the documentation! You will be helping the open source community and other developers interested in learning more about Medusa and using it.
+
+This guide is specific to contributing to the documentation. If you’re interested in contributing to Medusa’s codebase, check out the [contributing guidelines in the Medusa GitHub repository](https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md).
+
+## What Can You Contribute?
+
+You can contribute to the Medusa documentation in the following ways:
+
+- Fixes to existing content. This includes small fixes like typos, or adding missing information.
+- Additions to the documentation. If you think a documentation page can be useful to other developers, you can contribute by adding it.
+ - Make sure to open an issue first in the [medusa repository](https://github.com/medusajs/medusa) to confirm that you can add that documentation page.
+- Fixes to UI components and tooling. If you find a bug while browsing the documentation, you can contribute by fixing it.
+
+***
+
+## Documentation Workspace
+
+Medusa's documentation projects are all part of the documentation yarn workspace, which you can find in the [medusa repository](https://github.com/medusajs/medusa) under the `www` directory.
+
+The workspace has the following two directories:
+
+- `apps`: this directory holds the different documentation websites and projects.
+ - `book`: includes the codebase for the [main Medusa documentation](https://docs.medusajs.com//index.html.md). It's built with [Next.js 15](https://nextjs.org/).
+ - `resources`: includes the codebase for the resources documentation, which powers different sections of the docs such as the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) or [How-to & Tutorials](https://docs.medusajs.com/resources/how-to-tutorials/index.html.md) sections. It's built with [Next.js 15](https://nextjs.org/).
+ - `api-reference`: includes the codebase for the API reference website. It's built with [Next.js 15](https://nextjs.org/).
+ - `ui`: includes the codebase for the Medusa UI documentation website. It's built with [Next.js 15](https://nextjs.org/).
+- `packages`: this directory holds the shared packages and components necessary for the development of the projects in the `apps` directory.
+ - `docs-ui` includes the shared React components between the different apps.
+ - `remark-rehype-plugins` includes Remark and Rehype plugins used by the documentation projects.
+
+***
+
+## Documentation Content
+
+All documentation projects are built with Next.js. The content is writtin in MDX files.
+
+### Medusa Main Docs Content
+
+The content of the Medusa main docs are under the `www/apps/book/app` directory.
+
+### Medusa Resources Content
+
+The content of all pages under the `/resources` path are under the `www/apps/resources/app` directory.
+
+Documentation pages under the `www/apps/resources/references` directory are generated automatically from the source code under the `packages/medusa` directory. So, you can't directly make changes to them. Instead, you'll have to make changes to the comments in the original source code.
+
+### API Reference
+
+The API reference's content is split into two types:
+
+1. Static content, which are the content related to getting started, expanding fields, and more. These are located in the `www/apps/api-reference/markdown` directory. They are MDX files.
+2. OpenAPI specs that are shown to developers when checking the reference of an API Route. These are generated from OpenApi Spec comments, which are under the `www/utils/generated/oas-output` directory.
+
+### Medusa UI Documentation
+
+The content of the Medusa UI documentation are located under the `www/apps/ui/src/content/docs` directory. They are MDX files.
+
+The UI documentation also shows code examples, which are under the `www/apps/ui/src/examples` directory.
+
+The UI component props are generated from the source code and placed into the `www/apps/ui/src/specs` directory. To contribute to these props and their comments, check the comments in the source code under the `packages/design-system/ui` directory.
+
+***
+
+## Style Guide
+
+When you contribute to the documentation content, make sure to follow the [documentation style guide](https://www.notion.so/Style-Guide-Docs-fad86dd1c5f84b48b145e959f36628e0).
+
+***
+
+## How to Contribute
+
+If you’re fixing errors in an existing documentation page, you can scroll down to the end of the page and click on the “Edit this page” link. You’ll be redirected to the GitHub edit form of that page and you can make edits directly and submit a pull request (PR).
+
+If you’re adding a new page or contributing to the codebase, fork the repository, create a new branch, and make all changes necessary in your repository. Then, once you’re done, create a PR in the Medusa repository.
+
+### Base Branch
+
+When you make an edit to an existing documentation page or fork the repository to make changes to the documentation, create a new branch.
+
+Documentation contributions always use `develop` as the base branch. Make sure to also open your PR against the `develop` branch.
+
+### Branch Name
+
+Make sure that the branch name starts with `docs/`. For example, `docs/fix-services`. Vercel deployed previews are only triggered for branches starting with `docs/`.
+
+### Pull Request Conventions
+
+When you create a pull request, prefix the title with `docs:` or `docs(PROJECT_NAME):`, where `PROJECT_NAME` is the name of the documentation project this pull request pertains to. For example, `docs(ui): fix titles`.
+
+In the body of the PR, explain clearly what the PR does. If the PR solves an issue, use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) with the issue number. For example, “Closes #1333”.
+
+***
+
+## Images
+
+If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free to include it in the PR. Our team will later upload it to our image hosting.
+
+***
+
+## NPM and Yarn Code Blocks
+
+If you’re adding code blocks that use NPM and Yarn, you must add the `npm2yarn` meta field.
+
+For example:
+
+````md
+```bash npm2yarn
+npm run start
+```
+````
+
+The code snippet must be written using NPM.
+
+### Global Option
+
+When a command uses the global option `-g`, add it at the end of the NPM command to ensure that it’s transformed to a Yarn command properly. For example:
+
+```bash npm2yarn
+npm install @medusajs/cli -g
+```
+
+***
+
+## Linting with Vale
+
+Medusa uses [Vale](https://vale.sh/) to lint documentation pages and perform checks on incoming PRs into the repository.
+
+### Result of Vale PR Checks
+
+You can check the result of running the "lint" action on your PR by clicking the Details link next to it. You can find there all errors that you need to fix.
+
+### Run Vale Locally
+
+If you want to check your work locally, you can do that by:
+
+1. [Installing Vale](https://vale.sh/docs/vale-cli/installation/) on your machine.
+2. Changing to the `www/vale` directory:
+
+```bash
+cd www/vale
+```
+
+3\. Running the `run-vale` script:
+
+```bash
+# to lint content for the main documentation
+./run-vale.sh book/app/learn error resources
+# to lint content for the resources documentation
+./run-vale.sh resources/app error
+# to lint content for the API reference
+./run-vale.sh api-reference/markdown error
+# to lint content for the Medusa UI documentation
+./run-vale.sh ui/src/content/docs error
+# to lint content for the user guide
+./run-vale.sh user-guide/app error
+```
+
+{/* TODO need to enable MDX v1 comments first. */}
+
+{/* ### Linter Exceptions
+
+If it's needed to break some style guide rules in a document, you can wrap the parts that the linter shouldn't scan with the following comments in the `md` or `mdx` files:
+
+```md
+
+
+content that shouldn't be scanned for errors here...
+
+
+```
+
+You can also disable specific rules. For example:
+
+```md
+
+
+Medusa supports Node versions 14 and 16.
+
+
+```
+
+If you use this in your PR, you must justify its usage. */}
+
+***
+
+## Linting with ESLint
+
+Medusa uses ESlint to lint code blocks both in the content and the code base of the documentation apps.
+
+### Linting Content with ESLint
+
+Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `content-eslint`.
+
+If you want to check content ESLint errors locally and fix them, you can do that by:
+
+1\. Install the dependencies in the `www` directory:
+
+```bash
+yarn install
+```
+
+2\. Run the turbo command in the `www` directory:
+
+```bash
+turbo run lint:content
+```
+
+This will fix any fixable errors, and show errors that require your action.
+
+### Linting Code with ESLint
+
+Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `code-docs-eslint`.
+
+If you want to check code ESLint errors locally and fix them, you can do that by:
+
+1\. Install the dependencies in the `www` directory:
+
+```bash
+yarn install
+```
+
+2\. Run the turbo command in the `www` directory:
+
+```bash
+yarn lint
+```
+
+This will fix any fixable errors, and show errors that require your action.
+
+{/* TODO need to enable MDX v1 comments first. */}
+
+{/* ### ESLint Exceptions
+
+If some code blocks have errors that can't or shouldn't be fixed, you can add the following command before the code block:
+
+~~~md
+
+
+```js
+console.log("This block isn't linted")
+```
+
+```js
+console.log("This block is linted")
+```
+~~~
+
+You can also disable specific rules. For example:
+
+~~~md
+
+
+```js
+console.log("This block can use semicolons");
+```
+
+```js
+console.log("This block can't use semi colons")
+```
+~~~ */}
+
+
# Access Workflow Errors
In this chapter, you’ll learn how to access errors that occur during a workflow’s execution.
@@ -14923,6 +15282,260 @@ The hook is available on the workflow's `hooks` property using its name `product
You invoke the hook, passing a step function (the hook handler) as a parameter.
+# Compensation Function
+
+In this chapter, you'll learn what a compensation function is and how to add it to a step.
+
+## What is a Compensation Function
+
+A compensation function rolls back or undoes changes made by a step when an error occurs in the workflow.
+
+For example, if a step creates a record, the compensation function deletes the record when an error occurs later in the workflow.
+
+By using compensation functions, you provide a mechanism that guarantees data consistency in your application and across systems.
+
+***
+
+## How to add a Compensation Function?
+
+A compensation function is passed as a second parameter to the `createStep` function.
+
+For example, create the file `src/workflows/hello-world.ts` with the following content:
+
+```ts title="src/workflows/hello-world.ts" highlights={[["15"], ["16"], ["17"]]} collapsibleLines="1-5" expandButtonLabel="Show Imports"
+import {
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+
+const step1 = createStep(
+ "step-1",
+ async () => {
+ const message = `Hello from step one!`
+
+ console.log(message)
+
+ return new StepResponse(message)
+ },
+ async () => {
+ console.log("Oops! Rolling back my changes...")
+ }
+)
+```
+
+Each step can have a compensation function. The compensation function only runs if an error occurs throughout the workflow.
+
+***
+
+## Test the Compensation Function
+
+Create a step in the same `src/workflows/hello-world.ts` file that throws an error:
+
+```ts title="src/workflows/hello-world.ts"
+const step2 = createStep(
+ "step-2",
+ async () => {
+ throw new Error("Throwing an error...")
+ }
+)
+```
+
+Then, create a workflow that uses the steps:
+
+```ts title="src/workflows/hello-world.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
+import {
+ createWorkflow,
+ WorkflowResponse,
+} from "@medusajs/framework/workflows-sdk"
+// other imports...
+
+// steps...
+
+const myWorkflow = createWorkflow(
+ "hello-world",
+ function (input) {
+ const str1 = step1()
+ step2()
+
+ return new WorkflowResponse({
+ message: str1,
+ })
+})
+
+export default myWorkflow
+```
+
+Finally, execute the workflow from an API route:
+
+```ts title="src/api/workflow/route.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import myWorkflow from "../../../workflows/hello-world"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await myWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+Run the Medusa application and send a `GET` request to `/workflow`:
+
+```bash
+curl http://localhost:9000/workflow
+```
+
+In the console, you'll see:
+
+- `Hello from step one!` logged in the terminal, indicating that the first step ran successfully.
+- `Oops! Rolling back my changes...` logged in the terminal, indicating that the second step failed and the compensation function of the first step ran consequently.
+
+***
+
+## Pass Input to Compensation Function
+
+If a step creates a record, the compensation function must receive the ID of the record to remove it.
+
+To pass input to the compensation function, pass a second parameter in the `StepResponse` returned by the step.
+
+For example:
+
+```ts highlights={inputHighlights}
+import {
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+
+const step1 = createStep(
+ "step-1",
+ async () => {
+ return new StepResponse(
+ `Hello from step one!`,
+ { message: "Oops! Rolling back my changes..." }
+ )
+ },
+ async ({ message }) => {
+ console.log(message)
+ }
+)
+```
+
+In this example, the step passes an object as a second parameter to `StepResponse`.
+
+The compensation function receives the object and uses its `message` property to log a message.
+
+***
+
+## Resolve Resources from the Medusa Container
+
+The compensation function receives an object second parameter. The object has a `container` property that you use to resolve resources from the Medusa container.
+
+For example:
+
+```ts
+import {
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
+
+const step1 = createStep(
+ "step-1",
+ async () => {
+ return new StepResponse(
+ `Hello from step one!`,
+ { message: "Oops! Rolling back my changes..." }
+ )
+ },
+ async ({ message }, { container }) => {
+ const logger = container.resolve(
+ ContainerRegistrationKeys.LOGGER
+ )
+
+ logger.info(message)
+ }
+)
+```
+
+In this example, you use the `container` property in the second object parameter of the compensation function to resolve the logger.
+
+You then use the logger to log a message.
+
+***
+
+## Handle Errors in Loops
+
+This feature is only available after [Medusa v2.0.5](https://github.com/medusajs/medusa/releases/tag/v2.0.5).
+
+Consider you have a module that integrates a third-party ERP system, and you're creating a workflow that deletes items in that ERP. You may have the following step:
+
+```ts
+// other imports...
+import { promiseAll } from "@medusajs/framework/utils"
+
+type StepInput = {
+ ids: string[]
+}
+
+const step1 = createStep(
+ "step-1",
+ async ({ ids }: StepInput, { container }) => {
+ const erpModuleService = container.resolve(
+ ERP_MODULE
+ )
+ const prevData: unknown[] = []
+
+ await promiseAll(
+ ids.map(async (id) => {
+ const data = await erpModuleService.retrieve(id)
+
+ await erpModuleService.delete(id)
+
+ prevData.push(id)
+ })
+ )
+
+ return new StepResponse(ids, prevData)
+ }
+)
+```
+
+In the step, you loop over the IDs to retrieve the item's data, store them in a `prevData` variable, then delete them using the ERP Module's service. You then pass the `prevData` variable to the compensation function.
+
+However, if an error occurs in the loop, the `prevData` variable won't be passed to the compensation function as the execution never reached the return statement.
+
+To handle errors in the loop so that the compensation function receives the last version of `prevData` before the error occurred, you wrap the loop in a try-catch block. Then, in the catch block, you invoke and return the `StepResponse.permanentFailure` function:
+
+```ts highlights={highlights}
+try {
+ await promiseAll(
+ ids.map(async (id) => {
+ const data = await erpModuleService.retrieve(id)
+
+ await erpModuleService.delete(id)
+
+ prevData.push(id)
+ })
+ )
+} catch (e) {
+ return StepResponse.permanentFailure(
+ `An error occurred: ${e}`,
+ prevData
+ )
+}
+```
+
+The `StepResponse.permanentFailure` fails the step and its workflow, triggering current and previous steps' compensation functions. The `permanentFailure` function accepts as a first parameter the error message, which is saved in the workflow's error details, and as a second parameter the data to pass to the compensation function.
+
+So, if an error occurs during the loop, the compensation function will still receive the `prevData` variable to undo the changes made before the step failed.
+
+
# Conditions in Workflows with When-Then
In this chapter, you'll learn how to execute an action based on a condition in a workflow using when-then from the Workflows SDK.
@@ -15430,260 +16043,6 @@ const step1 = createStep(
```
-# Compensation Function
-
-In this chapter, you'll learn what a compensation function is and how to add it to a step.
-
-## What is a Compensation Function
-
-A compensation function rolls back or undoes changes made by a step when an error occurs in the workflow.
-
-For example, if a step creates a record, the compensation function deletes the record when an error occurs later in the workflow.
-
-By using compensation functions, you provide a mechanism that guarantees data consistency in your application and across systems.
-
-***
-
-## How to add a Compensation Function?
-
-A compensation function is passed as a second parameter to the `createStep` function.
-
-For example, create the file `src/workflows/hello-world.ts` with the following content:
-
-```ts title="src/workflows/hello-world.ts" highlights={[["15"], ["16"], ["17"]]} collapsibleLines="1-5" expandButtonLabel="Show Imports"
-import {
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-
-const step1 = createStep(
- "step-1",
- async () => {
- const message = `Hello from step one!`
-
- console.log(message)
-
- return new StepResponse(message)
- },
- async () => {
- console.log("Oops! Rolling back my changes...")
- }
-)
-```
-
-Each step can have a compensation function. The compensation function only runs if an error occurs throughout the workflow.
-
-***
-
-## Test the Compensation Function
-
-Create a step in the same `src/workflows/hello-world.ts` file that throws an error:
-
-```ts title="src/workflows/hello-world.ts"
-const step2 = createStep(
- "step-2",
- async () => {
- throw new Error("Throwing an error...")
- }
-)
-```
-
-Then, create a workflow that uses the steps:
-
-```ts title="src/workflows/hello-world.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
-import {
- createWorkflow,
- WorkflowResponse,
-} from "@medusajs/framework/workflows-sdk"
-// other imports...
-
-// steps...
-
-const myWorkflow = createWorkflow(
- "hello-world",
- function (input) {
- const str1 = step1()
- step2()
-
- return new WorkflowResponse({
- message: str1,
- })
-})
-
-export default myWorkflow
-```
-
-Finally, execute the workflow from an API route:
-
-```ts title="src/api/workflow/route.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import myWorkflow from "../../../workflows/hello-world"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await myWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-Run the Medusa application and send a `GET` request to `/workflow`:
-
-```bash
-curl http://localhost:9000/workflow
-```
-
-In the console, you'll see:
-
-- `Hello from step one!` logged in the terminal, indicating that the first step ran successfully.
-- `Oops! Rolling back my changes...` logged in the terminal, indicating that the second step failed and the compensation function of the first step ran consequently.
-
-***
-
-## Pass Input to Compensation Function
-
-If a step creates a record, the compensation function must receive the ID of the record to remove it.
-
-To pass input to the compensation function, pass a second parameter in the `StepResponse` returned by the step.
-
-For example:
-
-```ts highlights={inputHighlights}
-import {
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-
-const step1 = createStep(
- "step-1",
- async () => {
- return new StepResponse(
- `Hello from step one!`,
- { message: "Oops! Rolling back my changes..." }
- )
- },
- async ({ message }) => {
- console.log(message)
- }
-)
-```
-
-In this example, the step passes an object as a second parameter to `StepResponse`.
-
-The compensation function receives the object and uses its `message` property to log a message.
-
-***
-
-## Resolve Resources from the Medusa Container
-
-The compensation function receives an object second parameter. The object has a `container` property that you use to resolve resources from the Medusa container.
-
-For example:
-
-```ts
-import {
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
-
-const step1 = createStep(
- "step-1",
- async () => {
- return new StepResponse(
- `Hello from step one!`,
- { message: "Oops! Rolling back my changes..." }
- )
- },
- async ({ message }, { container }) => {
- const logger = container.resolve(
- ContainerRegistrationKeys.LOGGER
- )
-
- logger.info(message)
- }
-)
-```
-
-In this example, you use the `container` property in the second object parameter of the compensation function to resolve the logger.
-
-You then use the logger to log a message.
-
-***
-
-## Handle Errors in Loops
-
-This feature is only available after [Medusa v2.0.5](https://github.com/medusajs/medusa/releases/tag/v2.0.5).
-
-Consider you have a module that integrates a third-party ERP system, and you're creating a workflow that deletes items in that ERP. You may have the following step:
-
-```ts
-// other imports...
-import { promiseAll } from "@medusajs/framework/utils"
-
-type StepInput = {
- ids: string[]
-}
-
-const step1 = createStep(
- "step-1",
- async ({ ids }: StepInput, { container }) => {
- const erpModuleService = container.resolve(
- ERP_MODULE
- )
- const prevData: unknown[] = []
-
- await promiseAll(
- ids.map(async (id) => {
- const data = await erpModuleService.retrieve(id)
-
- await erpModuleService.delete(id)
-
- prevData.push(id)
- })
- )
-
- return new StepResponse(ids, prevData)
- }
-)
-```
-
-In the step, you loop over the IDs to retrieve the item's data, store them in a `prevData` variable, then delete them using the ERP Module's service. You then pass the `prevData` variable to the compensation function.
-
-However, if an error occurs in the loop, the `prevData` variable won't be passed to the compensation function as the execution never reached the return statement.
-
-To handle errors in the loop so that the compensation function receives the last version of `prevData` before the error occurred, you wrap the loop in a try-catch block. Then, in the catch block, you invoke and return the `StepResponse.permanentFailure` function:
-
-```ts highlights={highlights}
-try {
- await promiseAll(
- ids.map(async (id) => {
- const data = await erpModuleService.retrieve(id)
-
- await erpModuleService.delete(id)
-
- prevData.push(id)
- })
- )
-} catch (e) {
- return StepResponse.permanentFailure(
- `An error occurred: ${e}`,
- prevData
- )
-}
-```
-
-The `StepResponse.permanentFailure` fails the step and its workflow, triggering current and previous steps' compensation functions. The `permanentFailure` function accepts as a first parameter the error message, which is saved in the workflow's error details, and as a second parameter the data to pass to the compensation function.
-
-So, if an error occurs during the loop, the compensation function will still receive the `prevData` variable to undo the changes made before the step failed.
-
-
# Execute Another Workflow
In this chapter, you'll learn how to execute a workflow in another.
@@ -16377,7 +16736,7 @@ You can view stored workflow executions from the Medusa Admin dashboard by going
### Prerequisites
-- [Redis Workflow Engine must be installed and configured.](https://docs.medusajs.com/resources/architectural-modules/workflow-engine/redis/index.html.md)
+- [Redis Workflow Engine must be installed and configured.](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/redis/index.html.md)
`createWorkflow` from the Workflows SDK can accept an object as a first parameter to set the workflow's configuration. To enable storing a workflow's executions:
@@ -16919,364 +17278,6 @@ This step's executions fail if they run longer than two seconds.
A step’s timeout error is returned in the `errors` property of the workflow’s execution, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/access-workflow-errors/index.html.md). The error’s name is `TransactionStepTimeoutError`.
-# Docs Contribution Guidelines
-
-Thank you for your interest in contributing to the documentation! You will be helping the open source community and other developers interested in learning more about Medusa and using it.
-
-This guide is specific to contributing to the documentation. If you’re interested in contributing to Medusa’s codebase, check out the [contributing guidelines in the Medusa GitHub repository](https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md).
-
-## What Can You Contribute?
-
-You can contribute to the Medusa documentation in the following ways:
-
-- Fixes to existing content. This includes small fixes like typos, or adding missing information.
-- Additions to the documentation. If you think a documentation page can be useful to other developers, you can contribute by adding it.
- - Make sure to open an issue first in the [medusa repository](https://github.com/medusajs/medusa) to confirm that you can add that documentation page.
-- Fixes to UI components and tooling. If you find a bug while browsing the documentation, you can contribute by fixing it.
-
-***
-
-## Documentation Workspace
-
-Medusa's documentation projects are all part of the documentation yarn workspace, which you can find in the [medusa repository](https://github.com/medusajs/medusa) under the `www` directory.
-
-The workspace has the following two directories:
-
-- `apps`: this directory holds the different documentation websites and projects.
- - `book`: includes the codebase for the [main Medusa documentation](https://docs.medusajs.com//index.html.md). It's built with [Next.js 15](https://nextjs.org/).
- - `resources`: includes the codebase for the resources documentation, which powers different sections of the docs such as the [Integrations](https://docs.medusajs.com/resources/integrations/index.html.md) or [How-to & Tutorials](https://docs.medusajs.com/resources/how-to-tutorials/index.html.md) sections. It's built with [Next.js 15](https://nextjs.org/).
- - `api-reference`: includes the codebase for the API reference website. It's built with [Next.js 15](https://nextjs.org/).
- - `ui`: includes the codebase for the Medusa UI documentation website. It's built with [Next.js 15](https://nextjs.org/).
-- `packages`: this directory holds the shared packages and components necessary for the development of the projects in the `apps` directory.
- - `docs-ui` includes the shared React components between the different apps.
- - `remark-rehype-plugins` includes Remark and Rehype plugins used by the documentation projects.
-
-***
-
-## Documentation Content
-
-All documentation projects are built with Next.js. The content is writtin in MDX files.
-
-### Medusa Main Docs Content
-
-The content of the Medusa main docs are under the `www/apps/book/app` directory.
-
-### Medusa Resources Content
-
-The content of all pages under the `/resources` path are under the `www/apps/resources/app` directory.
-
-Documentation pages under the `www/apps/resources/references` directory are generated automatically from the source code under the `packages/medusa` directory. So, you can't directly make changes to them. Instead, you'll have to make changes to the comments in the original source code.
-
-### API Reference
-
-The API reference's content is split into two types:
-
-1. Static content, which are the content related to getting started, expanding fields, and more. These are located in the `www/apps/api-reference/markdown` directory. They are MDX files.
-2. OpenAPI specs that are shown to developers when checking the reference of an API Route. These are generated from OpenApi Spec comments, which are under the `www/utils/generated/oas-output` directory.
-
-### Medusa UI Documentation
-
-The content of the Medusa UI documentation are located under the `www/apps/ui/src/content/docs` directory. They are MDX files.
-
-The UI documentation also shows code examples, which are under the `www/apps/ui/src/examples` directory.
-
-The UI component props are generated from the source code and placed into the `www/apps/ui/src/specs` directory. To contribute to these props and their comments, check the comments in the source code under the `packages/design-system/ui` directory.
-
-***
-
-## Style Guide
-
-When you contribute to the documentation content, make sure to follow the [documentation style guide](https://www.notion.so/Style-Guide-Docs-fad86dd1c5f84b48b145e959f36628e0).
-
-***
-
-## How to Contribute
-
-If you’re fixing errors in an existing documentation page, you can scroll down to the end of the page and click on the “Edit this page” link. You’ll be redirected to the GitHub edit form of that page and you can make edits directly and submit a pull request (PR).
-
-If you’re adding a new page or contributing to the codebase, fork the repository, create a new branch, and make all changes necessary in your repository. Then, once you’re done, create a PR in the Medusa repository.
-
-### Base Branch
-
-When you make an edit to an existing documentation page or fork the repository to make changes to the documentation, create a new branch.
-
-Documentation contributions always use `develop` as the base branch. Make sure to also open your PR against the `develop` branch.
-
-### Branch Name
-
-Make sure that the branch name starts with `docs/`. For example, `docs/fix-services`. Vercel deployed previews are only triggered for branches starting with `docs/`.
-
-### Pull Request Conventions
-
-When you create a pull request, prefix the title with `docs:` or `docs(PROJECT_NAME):`, where `PROJECT_NAME` is the name of the documentation project this pull request pertains to. For example, `docs(ui): fix titles`.
-
-In the body of the PR, explain clearly what the PR does. If the PR solves an issue, use [closing keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) with the issue number. For example, “Closes #1333”.
-
-***
-
-## Images
-
-If you are adding images to a documentation page, you can host the image on [Imgur](https://imgur.com) for free to include it in the PR. Our team will later upload it to our image hosting.
-
-***
-
-## NPM and Yarn Code Blocks
-
-If you’re adding code blocks that use NPM and Yarn, you must add the `npm2yarn` meta field.
-
-For example:
-
-````md
-```bash npm2yarn
-npm run start
-```
-````
-
-The code snippet must be written using NPM.
-
-### Global Option
-
-When a command uses the global option `-g`, add it at the end of the NPM command to ensure that it’s transformed to a Yarn command properly. For example:
-
-```bash npm2yarn
-npm install @medusajs/cli -g
-```
-
-***
-
-## Linting with Vale
-
-Medusa uses [Vale](https://vale.sh/) to lint documentation pages and perform checks on incoming PRs into the repository.
-
-### Result of Vale PR Checks
-
-You can check the result of running the "lint" action on your PR by clicking the Details link next to it. You can find there all errors that you need to fix.
-
-### Run Vale Locally
-
-If you want to check your work locally, you can do that by:
-
-1. [Installing Vale](https://vale.sh/docs/vale-cli/installation/) on your machine.
-2. Changing to the `www/vale` directory:
-
-```bash
-cd www/vale
-```
-
-3\. Running the `run-vale` script:
-
-```bash
-# to lint content for the main documentation
-./run-vale.sh book/app/learn error resources
-# to lint content for the resources documentation
-./run-vale.sh resources/app error
-# to lint content for the API reference
-./run-vale.sh api-reference/markdown error
-# to lint content for the Medusa UI documentation
-./run-vale.sh ui/src/content/docs error
-# to lint content for the user guide
-./run-vale.sh user-guide/app error
-```
-
-{/* TODO need to enable MDX v1 comments first. */}
-
-{/* ### Linter Exceptions
-
-If it's needed to break some style guide rules in a document, you can wrap the parts that the linter shouldn't scan with the following comments in the `md` or `mdx` files:
-
-```md
-
-
-content that shouldn't be scanned for errors here...
-
-
-```
-
-You can also disable specific rules. For example:
-
-```md
-
-
-Medusa supports Node versions 14 and 16.
-
-
-```
-
-If you use this in your PR, you must justify its usage. */}
-
-***
-
-## Linting with ESLint
-
-Medusa uses ESlint to lint code blocks both in the content and the code base of the documentation apps.
-
-### Linting Content with ESLint
-
-Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `content-eslint`.
-
-If you want to check content ESLint errors locally and fix them, you can do that by:
-
-1\. Install the dependencies in the `www` directory:
-
-```bash
-yarn install
-```
-
-2\. Run the turbo command in the `www` directory:
-
-```bash
-turbo run lint:content
-```
-
-This will fix any fixable errors, and show errors that require your action.
-
-### Linting Code with ESLint
-
-Each PR runs through a check that lints the code in the content files using ESLint. The action's name is `code-docs-eslint`.
-
-If you want to check code ESLint errors locally and fix them, you can do that by:
-
-1\. Install the dependencies in the `www` directory:
-
-```bash
-yarn install
-```
-
-2\. Run the turbo command in the `www` directory:
-
-```bash
-yarn lint
-```
-
-This will fix any fixable errors, and show errors that require your action.
-
-{/* TODO need to enable MDX v1 comments first. */}
-
-{/* ### ESLint Exceptions
-
-If some code blocks have errors that can't or shouldn't be fixed, you can add the following command before the code block:
-
-~~~md
-
-
-```js
-console.log("This block isn't linted")
-```
-
-```js
-console.log("This block is linted")
-```
-~~~
-
-You can also disable specific rules. For example:
-
-~~~md
-
-
-```js
-console.log("This block can use semicolons");
-```
-
-```js
-console.log("This block can't use semi colons")
-```
-~~~ */}
-
-
-# Translate Medusa Admin
-
-The Medusa Admin supports multiple languages, with the default being English. In this documentation, you'll learn how to contribute to the community by translating the Medusa Admin to a language you're fluent in.
-
-{/* vale docs.We = NO */}
-
-You can contribute either by translating the admin to a new language, or fixing translations for existing languages. As we can't validate every language's translations, some translations may be incorrect. Your contribution is welcome to fix any translation errors you find.
-
-{/* vale docs.We = YES */}
-
-Check out the translated languages either in the admin dashboard's settings or on [GitHub](https://github.com/medusajs/medusa/blob/develop/packages/admin/dashboard/src/i18n/languages.ts).
-
-***
-
-## How to Contribute Translation
-
-1. Clone the [Medusa monorepository](https://github.com/medusajs/medusa) to your local machine:
-
-```bash
-git clone https://github.com/medusajs/medusa.git
-```
-
-If you already have it cloned, make sure to pull the latest changes from the `develop` branch.
-
-2. Install the monorepository's dependencies. Since it's a Yarn workspace, it's highly recommended to use yarn:
-
-```bash
-yarn install
-```
-
-3. Create a branch that you'll use to open the pull request later:
-
-```bash
-git checkout -b feat/translate-
-```
-
-Where `` is your language name. For example, `feat/translate-da`.
-
-4. Translation files are under `packages/admin/dashboard/src/i18n/translations` as JSON files whose names are the ISO-2 name of the language.
- - If you're adding a new language, copy the file `packages/admin/dashboard/src/i18n/translations/en.json` and paste it with the ISO-2 name for your language. For example, if you're adding Danish translations, copy the `en.json` file and paste it as `packages/admin/dashboard/src/i18n/translations/de.json`.
- - If you're fixing a translation, find the JSON file of the language under `packages/admin/dashboard/src/i18n/translations`.
-
-5. Start translating the keys in the JSON file (or updating the targeted ones). All keys in the JSON file must be translated, and your PR tests will fail otherwise.
- - You can check whether the JSON file is valid by running the following command in `packages/admin/dashboard`, replacing `da.json` with the JSON file's name:
-
-```bash title="packages/admin/dashboard"
-yarn i18n:validate da.json
-```
-
-6. After finishing the translation, if you're adding a new language, import its JSON file in `packages/admin/dashboard/src/i18n/translations/index.ts` and add it to the exported object:
-
-```ts title="packages/admin/dashboard/src/i18n/translations/index.ts" highlights={[["2"], ["6"], ["7"], ["8"]]}
-// other imports...
-import da from "./da.json"
-
-export default {
- // other languages...
- da: {
- translation: da,
- },
-}
-```
-
-The language's key in the object is the ISO-2 name of the language.
-
-7. If you're adding a new language, add it to the file `packages/admin/dashboard/src/i18n/languages.ts`:
-
-```ts title="packages/admin/dashboard/src/i18n/languages.ts" highlights={languageHighlights}
-import { da } from "date-fns/locale"
-// other imports...
-
-export const languages: Language[] = [
- // other languages...
- {
- code: "da",
- display_name: "Danish",
- ltr: true,
- date_locale: da,
- },
-]
-```
-
-`languages` is an array having the following properties:
-
-- `code`: The ISO-2 name of the language. For example, `da` for Danish.
-- `display_name`: The language's name to be displayed in the admin.
-- `ltr`: Whether the language supports a left-to-right layout. For example, set this to `false` for languages like Arabic.
-- `date_locale`: An instance of the locale imported from the [date-fns/locale](https://date-fns.org/) package.
-
-8. Once you're done, push the changes into your branch and open a pull request on GitHub.
-
-Our team will perform a general review on your PR and merge it if no issues are found. The translation will be available in the admin after the next release.
-
-
# Example: Write Integration Tests for API Routes
In this chapter, you'll learn how to write integration tests for API routes using [medusaIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests/index.html.md) from Medusa's Testing Framework.
@@ -18049,11 +18050,11 @@ This runs your Medusa application and runs the tests available in any `__tests__
# Commerce Modules
-In this section of the documentation, you'll find guides and references related to Medusa's commerce modules.
+In this section of the documentation, you'll find guides and references related to Medusa's Commerce Modules.
-A commerce module provides features for a commerce domain within its service. The Medusa application exposes these features in its API routes to clients.
+A Commerce Module provides features for a commerce domain within its service. The Medusa application exposes these features in its API routes to clients.
-A commerce module also defines data models, representing tables in the database. The Medusa Framework and tools allow you to extend these data models to add custom fields.
+A Commerce Module also defines data models, representing tables in the database. The Medusa Framework and tools allow you to extend these data models to add custom fields.
## Commerce Modules List
@@ -18093,7 +18094,7 @@ In this section of the documentation, you will find resources to learn more abou
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/developer/index.html.md) to learn how to manage publishable and secret API keys using the dashboard.
-Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this API Key Module.
+Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this API Key Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -18108,7 +18109,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the API Key Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -18227,11 +18228,300 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
***
+# Customer Module
+
+In this section of the documentation, you will find resources to learn more about the Customer Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers and groups using the dashboard.
+
+Medusa has customer related features available out-of-the-box through the Customer Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Customer Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Customer Features
+
+- [Customer Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/customer-accounts/index.html.md): Store and manage guest and registered customers in your store.
+- [Customer Organization](https://docs.medusajs.com/references/customer/models/index.html.md): Organize customers into groups. This has a lot of benefits and supports many use cases, such as provide discounts for specific customer groups using the [Promotion Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/index.html.md).
+
+***
+
+## How to Use the Customer Module
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/create-customer.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const createCustomerStep = createStep(
+ "create-customer",
+ async ({}, { container }) => {
+ const customerModuleService = container.resolve(Modules.CUSTOMER)
+
+ const customer = await customerModuleService.createCustomers({
+ first_name: "Peter",
+ last_name: "Hayes",
+ email: "peter.hayes@example.com",
+ })
+
+ return new StepResponse({ customer }, customer.id)
+ },
+ async (customerId, { container }) => {
+ if (!customerId) {
+ return
+ }
+ const customerModuleService = container.resolve(Modules.CUSTOMER)
+
+ await customerModuleService.deleteCustomers([customerId])
+ }
+)
+
+export const createCustomerWorkflow = createWorkflow(
+ "create-customer",
+ () => {
+ const { customer } = createCustomerStep()
+
+ return new WorkflowResponse({
+ customer,
+ })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { createCustomerWorkflow } from "../../workflows/create-customer"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await createCustomerWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { createCustomerWorkflow } from "../workflows/create-customer"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await createCustomerWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { createCustomerWorkflow } from "../workflows/create-customer"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await createCustomerWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+
+# Currency Module
+
+In this section of the documentation, you will find resources to learn more about the Currency Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store's currencies using the dashboard.
+
+Medusa has currency related features available out-of-the-box through the Currency Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Currency Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Currency Features
+
+- [Currency Management and Retrieval](https://docs.medusajs.com/references/currency/listAndCountCurrencies/index.html.md): This module adds all common currencies to your application and allows you to retrieve them.
+- [Support Currencies in Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/index.html.md): Other Commerce Modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
+
+***
+
+## How to Use the Currency Module
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+ transform,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const retrieveCurrencyStep = createStep(
+ "retrieve-currency",
+ async ({}, { container }) => {
+ const currencyModuleService = container.resolve(Modules.CURRENCY)
+
+ const currency = await currencyModuleService
+ .retrieveCurrency("usd")
+
+ return new StepResponse({ currency })
+ }
+)
+
+type Input = {
+ price: number
+}
+
+export const retrievePriceWithCurrency = createWorkflow(
+ "create-currency",
+ (input: Input) => {
+ const { currency } = retrieveCurrencyStep()
+
+ const formattedPrice = transform({
+ input,
+ currency,
+ }, (data) => {
+ return `${data.currency.symbol}${data.input.price}`
+ })
+
+ return new WorkflowResponse({
+ formattedPrice,
+ })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await retrievePriceWithCurrency(req.scope)
+ .run({
+ price: 10,
+ })
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await retrievePriceWithCurrency(container)
+ .run({
+ price: 10,
+ })
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await retrievePriceWithCurrency(container)
+ .run({
+ price: 10,
+ })
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+
# Cart Module
In this section of the documentation, you will find resources to learn more about the Cart Module and how to use it in your application.
-Medusa has cart related features available out-of-the-box through the Cart Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Cart Module.
+Medusa has cart related features available out-of-the-box through the Cart Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Cart Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -18240,13 +18530,13 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
- [Cart Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/concepts/index.html.md): Store and manage carts, including their addresses, line items, shipping methods, and more.
- [Apply Promotion Adjustments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/promotions/index.html.md): Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals.
- [Apply Tax Lines](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/tax-lines/index.html.md): Apply tax lines to line items and shipping methods.
-- [Cart Scoping](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/index.html.md): When used in the Medusa application, Medusa creates links to other commerce modules, scoping a cart to a sales channel, region, and a customer.
+- [Cart Scoping](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/index.html.md): When used in the Medusa application, Medusa creates links to other Commerce Modules, scoping a cart to a sales channel, region, and a customer.
***
## How to Use the Cart Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -18381,7 +18671,7 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
In this section of the documentation, you will find resources to learn more about the Auth Module and how to use it in your application.
-Medusa has auth related features available out-of-the-box through the Auth Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Auth Module.
+Medusa has auth related features available out-of-the-box through the Auth Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Auth Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -18396,7 +18686,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Auth Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -18507,302 +18797,13 @@ Medusa provides the following authentication providers out-of-the-box. You can u
***
-# Customer Module
-
-In this section of the documentation, you will find resources to learn more about the Customer Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers and groups using the dashboard.
-
-Medusa has customer related features available out-of-the-box through the Customer Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Customer Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Customer Features
-
-- [Customer Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/customer-accounts/index.html.md): Store and manage guest and registered customers in your store.
-- [Customer Organization](https://docs.medusajs.com/references/customer/models/index.html.md): Organize customers into groups. This has a lot of benefits and supports many use cases, such as provide discounts for specific customer groups using the [Promotion Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/index.html.md).
-
-***
-
-## How to Use the Customer Module
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/create-customer.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const createCustomerStep = createStep(
- "create-customer",
- async ({}, { container }) => {
- const customerModuleService = container.resolve(Modules.CUSTOMER)
-
- const customer = await customerModuleService.createCustomers({
- first_name: "Peter",
- last_name: "Hayes",
- email: "peter.hayes@example.com",
- })
-
- return new StepResponse({ customer }, customer.id)
- },
- async (customerId, { container }) => {
- if (!customerId) {
- return
- }
- const customerModuleService = container.resolve(Modules.CUSTOMER)
-
- await customerModuleService.deleteCustomers([customerId])
- }
-)
-
-export const createCustomerWorkflow = createWorkflow(
- "create-customer",
- () => {
- const { customer } = createCustomerStep()
-
- return new WorkflowResponse({
- customer,
- })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { createCustomerWorkflow } from "../../workflows/create-customer"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await createCustomerWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { createCustomerWorkflow } from "../workflows/create-customer"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await createCustomerWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { createCustomerWorkflow } from "../workflows/create-customer"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await createCustomerWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-
-# Currency Module
-
-In this section of the documentation, you will find resources to learn more about the Currency Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store's currencies using the dashboard.
-
-Medusa has currency related features available out-of-the-box through the Currency Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Currency Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Currency Features
-
-- [Currency Management and Retrieval](https://docs.medusajs.com/references/currency/listAndCountCurrencies/index.html.md): This module adds all common currencies to your application and allows you to retrieve them.
-- [Support Currencies in Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/index.html.md): Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
-
-***
-
-## How to Use the Currency Module
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
- transform,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const retrieveCurrencyStep = createStep(
- "retrieve-currency",
- async ({}, { container }) => {
- const currencyModuleService = container.resolve(Modules.CURRENCY)
-
- const currency = await currencyModuleService
- .retrieveCurrency("usd")
-
- return new StepResponse({ currency })
- }
-)
-
-type Input = {
- price: number
-}
-
-export const retrievePriceWithCurrency = createWorkflow(
- "create-currency",
- (input: Input) => {
- const { currency } = retrieveCurrencyStep()
-
- const formattedPrice = transform({
- input,
- currency,
- }, (data) => {
- return `${data.currency.symbol}${data.input.price}`
- })
-
- return new WorkflowResponse({
- formattedPrice,
- })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await retrievePriceWithCurrency(req.scope)
- .run({
- price: 10,
- })
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await retrievePriceWithCurrency(container)
- .run({
- price: 10,
- })
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await retrievePriceWithCurrency(container)
- .run({
- price: 10,
- })
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-
# Inventory Module
In this section of the documentation, you will find resources to learn more about the Inventory Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/inventory/index.html.md) to learn how to manage inventory and related features using the dashboard.
-Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Inventory Module.
+Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Inventory Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -18818,7 +18819,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Inventory Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -18949,7 +18950,7 @@ Refer to the Medusa Admin User Guide to learn how to use the dashboard to:
- [Manage order fulfillments](https://docs.medusajs.com/user-guide/orders/fulfillments/index.html.md).
- [Manage shipping options and profiles](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/index.html.md).
-Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Fulfillment Module.
+Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Fulfillment Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -18964,7 +18965,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Fulfillment Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -19112,7 +19113,7 @@ In this section of the documentation, you will find resources to learn more abou
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/index.html.md) to learn how to manage orders using the dashboard.
-Medusa has order related features available out-of-the-box through the Order Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Order Module.
+Medusa has order related features available out-of-the-box through the Order Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Order Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -19128,7 +19129,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Order Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -19268,7 +19269,7 @@ In this section of the documentation, you will find resources to learn more abou
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/orders/payments/index.html.md) to learn how to manage order payments using the dashboard.
-Medusa has payment related features available out-of-the-box through the Payment Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Payment Module.
+Medusa has payment related features available out-of-the-box through the Payment Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Payment Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -19284,7 +19285,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Payment Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -19417,167 +19418,13 @@ Medusa provides the following payment providers out-of-the-box. You can use them
***
-# Pricing Module
-
-In this section of the documentation, you will find resources to learn more about the Pricing Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/price-lists/index.html.md) to learn how to manage price lists using the dashboard.
-
-Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Pricing Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Pricing Features
-
-- [Price Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts/index.html.md): Store and manage prices of a resource, such as a product or a variant.
-- [Advanced Rule Engine](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-rules/index.html.md): Create prices with custom rules to condition prices based on different contexts.
-- [Price Lists](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts#price-list/index.html.md): Group prices and apply them only in specific conditions with price lists.
-- [Price Calculation Strategy](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md): Retrieve the best price in a given context and for the specified rule values.
-- [Tax-Inclusive Pricing](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/tax-inclusive-pricing/index.html.md): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically.
-
-***
-
-## How to Use the Pricing Module
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/create-price-set.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const createPriceSetStep = createStep(
- "create-price-set",
- async ({}, { container }) => {
- const pricingModuleService = container.resolve(Modules.PRICING)
-
- const priceSet = await pricingModuleService.createPriceSets({
- prices: [
- {
- amount: 500,
- currency_code: "USD",
- },
- {
- amount: 400,
- currency_code: "EUR",
- min_quantity: 0,
- max_quantity: 4,
- rules: {},
- },
- ],
- })
-
- return new StepResponse({ priceSet }, priceSet.id)
- },
- async (priceSetId, { container }) => {
- if (!priceSetId) {
- return
- }
- const pricingModuleService = container.resolve(Modules.PRICING)
-
- await pricingModuleService.deletePriceSets([priceSetId])
- }
-)
-
-export const createPriceSetWorkflow = createWorkflow(
- "create-price-set",
- () => {
- const { priceSet } = createPriceSetStep()
-
- return new WorkflowResponse({
- priceSet,
- })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { createPriceSetWorkflow } from "../../workflows/create-price-set"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await createPriceSetWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { createPriceSetWorkflow } from "../workflows/create-price-set"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await createPriceSetWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { createPriceSetWorkflow } from "../workflows/create-price-set"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await createPriceSetWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-
# Product Module
In this section of the documentation, you will find resources to learn more about the Product Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/products/index.html.md) to learn how to manage products using the dashboard.
-Medusa has product related features available out-of-the-box through the Product Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Product Module.
+Medusa has product related features available out-of-the-box through the Product Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Product Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -19591,7 +19438,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Product Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -19725,13 +19572,167 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
***
+# Pricing Module
+
+In this section of the documentation, you will find resources to learn more about the Pricing Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/price-lists/index.html.md) to learn how to manage price lists using the dashboard.
+
+Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Pricing Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Pricing Features
+
+- [Price Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts/index.html.md): Store and manage prices of a resource, such as a product or a variant.
+- [Advanced Rule Engine](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-rules/index.html.md): Create prices with custom rules to condition prices based on different contexts.
+- [Price Lists](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/concepts#price-list/index.html.md): Group prices and apply them only in specific conditions with price lists.
+- [Price Calculation Strategy](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md): Retrieve the best price in a given context and for the specified rule values.
+- [Tax-Inclusive Pricing](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/tax-inclusive-pricing/index.html.md): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically.
+
+***
+
+## How to Use the Pricing Module
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/create-price-set.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const createPriceSetStep = createStep(
+ "create-price-set",
+ async ({}, { container }) => {
+ const pricingModuleService = container.resolve(Modules.PRICING)
+
+ const priceSet = await pricingModuleService.createPriceSets({
+ prices: [
+ {
+ amount: 500,
+ currency_code: "USD",
+ },
+ {
+ amount: 400,
+ currency_code: "EUR",
+ min_quantity: 0,
+ max_quantity: 4,
+ rules: {},
+ },
+ ],
+ })
+
+ return new StepResponse({ priceSet }, priceSet.id)
+ },
+ async (priceSetId, { container }) => {
+ if (!priceSetId) {
+ return
+ }
+ const pricingModuleService = container.resolve(Modules.PRICING)
+
+ await pricingModuleService.deletePriceSets([priceSetId])
+ }
+)
+
+export const createPriceSetWorkflow = createWorkflow(
+ "create-price-set",
+ () => {
+ const { priceSet } = createPriceSetStep()
+
+ return new WorkflowResponse({
+ priceSet,
+ })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { createPriceSetWorkflow } from "../../workflows/create-price-set"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await createPriceSetWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { createPriceSetWorkflow } from "../workflows/create-price-set"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await createPriceSetWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { createPriceSetWorkflow } from "../workflows/create-price-set"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await createPriceSetWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+
# Promotion Module
In this section of the documentation, you will find resources to learn more about the Promotion Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/index.html.md) to learn how to manage promotions using the dashboard.
-Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Promotion Module.
+Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Promotion Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -19746,7 +19747,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use the Promotion Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -19873,150 +19874,13 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
***
-# Stock Location Module
-
-In this section of the documentation, you will find resources to learn more about the Stock Location Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/index.html.md) to learn how to manage stock locations using the dashboard.
-
-Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Stock Location Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Stock Location Features
-
-- [Stock Location Management](https://docs.medusajs.com/references/stock-location-next/models/index.html.md): Store and manage stock locations. Medusa links stock locations with data models of other modules that require a location, such as the [Inventory Module's InventoryLevel](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/index.html.md).
-- [Address Management](https://docs.medusajs.com/references/stock-location-next/models/StockLocationAddress/index.html.md): Manage the address of each stock location.
-
-***
-
-## How to Use Stock Location Module's Service
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/create-stock-location.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const createStockLocationStep = createStep(
- "create-stock-location",
- async ({}, { container }) => {
- const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
-
- const stockLocation = await stockLocationModuleService.createStockLocations({
- name: "Warehouse 1",
- })
-
- return new StepResponse({ stockLocation }, stockLocation.id)
- },
- async (stockLocationId, { container }) => {
- if (!stockLocationId) {
- return
- }
- const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
-
- await stockLocationModuleService.deleteStockLocations([stockLocationId])
- }
-)
-
-export const createStockLocationWorkflow = createWorkflow(
- "create-stock-location",
- () => {
- const { stockLocation } = createStockLocationStep()
-
- return new WorkflowResponse({ stockLocation })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { createStockLocationWorkflow } from "../../workflows/create-stock-location"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await createStockLocationWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { createStockLocationWorkflow } from "../workflows/create-stock-location"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await createStockLocationWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { createStockLocationWorkflow } from "../workflows/create-stock-location"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await createStockLocationWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-
# Region Module
In this section of the documentation, you will find resources to learn more about the Region Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage regions using the dashboard.
-Medusa has region related features available out-of-the-box through the Region Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Region Module.
+Medusa has region related features available out-of-the-box through the Region Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Region Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -20032,7 +19896,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use Region Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -20153,13 +20017,435 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
***
+# Store Module
+
+In this section of the documentation, you will find resources to learn more about the Store Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store using the dashboard.
+
+Medusa has store related features available out-of-the-box through the Store Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Store Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Store Features
+
+- [Store Management](https://docs.medusajs.com/references/store/models/Store/index.html.md): Create and manage stores in your application.
+- [Multi-Tenancy Support](https://docs.medusajs.com/references/store/models/Store/index.html.md): Create multiple stores, each having its own configurations.
+
+***
+
+## How to Use Store Module's Service
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/create-store.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const createStoreStep = createStep(
+ "create-store",
+ async ({}, { container }) => {
+ const storeModuleService = container.resolve(Modules.STORE)
+
+ const store = await storeModuleService.createStores({
+ name: "My Store",
+ supported_currencies: [{
+ currency_code: "usd",
+ is_default: true,
+ }],
+ })
+
+ return new StepResponse({ store }, store.id)
+ },
+ async (storeId, { container }) => {
+ if(!storeId) {
+ return
+ }
+ const storeModuleService = container.resolve(Modules.STORE)
+
+ await storeModuleService.deleteStores([storeId])
+ }
+)
+
+export const createStoreWorkflow = createWorkflow(
+ "create-store",
+ () => {
+ const { store } = createStoreStep()
+
+ return new WorkflowResponse({ store })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { createStoreWorkflow } from "../../workflows/create-store"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await createStoreWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { createStoreWorkflow } from "../workflows/create-store"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await createStoreWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { createStoreWorkflow } from "../workflows/create-store"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await createStoreWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+
+# Tax Module
+
+In this section of the documentation, you will find resources to learn more about the Tax Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard.
+
+Medusa has tax related features available out-of-the-box through the Tax Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Tax Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Tax Features
+
+- [Tax Settings Per Region](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-region/index.html.md): Set different tax settings for each tax region.
+- [Tax Rates and Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-rates-and-rules/index.html.md): Manage each region's default tax rates and override them with conditioned tax rates.
+- [Retrieve Tax Lines for carts and orders](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-calculation-with-provider/index.html.md): Calculate and retrieve the tax lines of a cart or order's line items and shipping methods with tax providers.
+
+***
+
+## How to Use Tax Module's Service
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/create-tax-region.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const createTaxRegionStep = createStep(
+ "create-tax-region",
+ async ({}, { container }) => {
+ const taxModuleService = container.resolve(Modules.TAX)
+
+ const taxRegion = await taxModuleService.createTaxRegions({
+ country_code: "us",
+ })
+
+ return new StepResponse({ taxRegion }, taxRegion.id)
+ },
+ async (taxRegionId, { container }) => {
+ if (!taxRegionId) {
+ return
+ }
+ const taxModuleService = container.resolve(Modules.TAX)
+
+ await taxModuleService.deleteTaxRegions([taxRegionId])
+ }
+)
+
+export const createTaxRegionWorkflow = createWorkflow(
+ "create-tax-region",
+ () => {
+ const { taxRegion } = createTaxRegionStep()
+
+ return new WorkflowResponse({ taxRegion })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { createTaxRegionWorkflow } from "../../workflows/create-tax-region"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await createTaxRegionWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await createTaxRegionWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await createTaxRegionWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+## Configure Tax Module
+
+The Tax Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/module-options/index.html.md) for details on the module's options.
+
+***
+
+
+# Stock Location Module
+
+In this section of the documentation, you will find resources to learn more about the Stock Location Module and how to use it in your application.
+
+Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/index.html.md) to learn how to manage stock locations using the dashboard.
+
+Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Stock Location Module.
+
+Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
+
+## Stock Location Features
+
+- [Stock Location Management](https://docs.medusajs.com/references/stock-location-next/models/index.html.md): Store and manage stock locations. Medusa links stock locations with data models of other modules that require a location, such as the [Inventory Module's InventoryLevel](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/index.html.md).
+- [Address Management](https://docs.medusajs.com/references/stock-location-next/models/StockLocationAddress/index.html.md): Manage the address of each stock location.
+
+***
+
+## How to Use Stock Location Module's Service
+
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+
+You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
+
+For example:
+
+```ts title="src/workflows/create-stock-location.ts" highlights={highlights}
+import {
+ createWorkflow,
+ WorkflowResponse,
+ createStep,
+ StepResponse,
+} from "@medusajs/framework/workflows-sdk"
+import { Modules } from "@medusajs/framework/utils"
+
+const createStockLocationStep = createStep(
+ "create-stock-location",
+ async ({}, { container }) => {
+ const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
+
+ const stockLocation = await stockLocationModuleService.createStockLocations({
+ name: "Warehouse 1",
+ })
+
+ return new StepResponse({ stockLocation }, stockLocation.id)
+ },
+ async (stockLocationId, { container }) => {
+ if (!stockLocationId) {
+ return
+ }
+ const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
+
+ await stockLocationModuleService.deleteStockLocations([stockLocationId])
+ }
+)
+
+export const createStockLocationWorkflow = createWorkflow(
+ "create-stock-location",
+ () => {
+ const { stockLocation } = createStockLocationStep()
+
+ return new WorkflowResponse({ stockLocation })
+ }
+)
+```
+
+You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
+
+### API Route
+
+```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import type {
+ MedusaRequest,
+ MedusaResponse,
+} from "@medusajs/framework/http"
+import { createStockLocationWorkflow } from "../../workflows/create-stock-location"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const { result } = await createStockLocationWorkflow(req.scope)
+ .run()
+
+ res.send(result)
+}
+```
+
+### Subscriber
+
+```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
+import {
+ type SubscriberConfig,
+ type SubscriberArgs,
+} from "@medusajs/framework"
+import { createStockLocationWorkflow } from "../workflows/create-stock-location"
+
+export default async function handleUserCreated({
+ event: { data },
+ container,
+}: SubscriberArgs<{ id: string }>) {
+ const { result } = await createStockLocationWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config: SubscriberConfig = {
+ event: "user.created",
+}
+```
+
+### Scheduled Job
+
+```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
+import { MedusaContainer } from "@medusajs/framework/types"
+import { createStockLocationWorkflow } from "../workflows/create-stock-location"
+
+export default async function myCustomJob(
+ container: MedusaContainer
+) {
+ const { result } = await createStockLocationWorkflow(container)
+ .run()
+
+ console.log(result)
+}
+
+export const config = {
+ name: "run-once-a-day",
+ schedule: `0 0 * * *`,
+}
+```
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
+
+***
+
+
# Sales Channel Module
In this section of the documentation, you will find resources to learn more about the Sales Channel Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/sales-channels/index.html.md) to learn how to manage sales channels using the dashboard.
-Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Sales Channel Module.
+Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Sales Channel Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -20186,7 +20472,7 @@ Some use case examples for using a sales channel:
## How to Use Sales Channel Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -20313,154 +20599,13 @@ Learn more about workflows in [this documentation](https://docs.medusajs.com/doc
***
-# Store Module
-
-In this section of the documentation, you will find resources to learn more about the Store Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/store/index.html.md) to learn how to manage your store using the dashboard.
-
-Medusa has store related features available out-of-the-box through the Store Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Store Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Store Features
-
-- [Store Management](https://docs.medusajs.com/references/store/models/Store/index.html.md): Create and manage stores in your application.
-- [Multi-Tenancy Support](https://docs.medusajs.com/references/store/models/Store/index.html.md): Create multiple stores, each having its own configurations.
-
-***
-
-## How to Use Store Module's Service
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/create-store.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const createStoreStep = createStep(
- "create-store",
- async ({}, { container }) => {
- const storeModuleService = container.resolve(Modules.STORE)
-
- const store = await storeModuleService.createStores({
- name: "My Store",
- supported_currencies: [{
- currency_code: "usd",
- is_default: true,
- }],
- })
-
- return new StepResponse({ store }, store.id)
- },
- async (storeId, { container }) => {
- if(!storeId) {
- return
- }
- const storeModuleService = container.resolve(Modules.STORE)
-
- await storeModuleService.deleteStores([storeId])
- }
-)
-
-export const createStoreWorkflow = createWorkflow(
- "create-store",
- () => {
- const { store } = createStoreStep()
-
- return new WorkflowResponse({ store })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { createStoreWorkflow } from "../../workflows/create-store"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await createStoreWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { createStoreWorkflow } from "../workflows/create-store"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await createStoreWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { createStoreWorkflow } from "../workflows/create-store"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await createStoreWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-
# User Module
In this section of the documentation, you will find resources to learn more about the User Module and how to use it in your application.
Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/users/index.html.md) to learn how to manage users using the dashboard.
-Medusa has user related features available out-of-the-box through the User Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this User Module.
+Medusa has user related features available out-of-the-box through the User Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this User Module.
Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
@@ -20473,7 +20618,7 @@ Learn more about why modules are isolated in [this documentation](https://docs.m
## How to Use User Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
@@ -20601,150 +20746,6 @@ The User Module accepts options for further configurations. Refer to [this docum
***
-# Tax Module
-
-In this section of the documentation, you will find resources to learn more about the Tax Module and how to use it in your application.
-
-Refer to the [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard.
-
-Medusa has tax related features available out-of-the-box through the Tax Module. A [module](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Tax Module.
-
-Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md).
-
-## Tax Features
-
-- [Tax Settings Per Region](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-region/index.html.md): Set different tax settings for each tax region.
-- [Tax Rates and Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-rates-and-rules/index.html.md): Manage each region's default tax rates and override them with conditioned tax rates.
-- [Retrieve Tax Lines for carts and orders](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/tax-calculation-with-provider/index.html.md): Calculate and retrieve the tax lines of a cart or order's line items and shipping methods with tax providers.
-
-***
-
-## How to Use Tax Module's Service
-
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
-
-You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
-
-For example:
-
-```ts title="src/workflows/create-tax-region.ts" highlights={highlights}
-import {
- createWorkflow,
- WorkflowResponse,
- createStep,
- StepResponse,
-} from "@medusajs/framework/workflows-sdk"
-import { Modules } from "@medusajs/framework/utils"
-
-const createTaxRegionStep = createStep(
- "create-tax-region",
- async ({}, { container }) => {
- const taxModuleService = container.resolve(Modules.TAX)
-
- const taxRegion = await taxModuleService.createTaxRegions({
- country_code: "us",
- })
-
- return new StepResponse({ taxRegion }, taxRegion.id)
- },
- async (taxRegionId, { container }) => {
- if (!taxRegionId) {
- return
- }
- const taxModuleService = container.resolve(Modules.TAX)
-
- await taxModuleService.deleteTaxRegions([taxRegionId])
- }
-)
-
-export const createTaxRegionWorkflow = createWorkflow(
- "create-tax-region",
- () => {
- const { taxRegion } = createTaxRegionStep()
-
- return new WorkflowResponse({ taxRegion })
- }
-)
-```
-
-You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
-
-### API Route
-
-```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import type {
- MedusaRequest,
- MedusaResponse,
-} from "@medusajs/framework/http"
-import { createTaxRegionWorkflow } from "../../workflows/create-tax-region"
-
-export async function GET(
- req: MedusaRequest,
- res: MedusaResponse
-) {
- const { result } = await createTaxRegionWorkflow(req.scope)
- .run()
-
- res.send(result)
-}
-```
-
-### Subscriber
-
-```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
-import {
- type SubscriberConfig,
- type SubscriberArgs,
-} from "@medusajs/framework"
-import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
-
-export default async function handleUserCreated({
- event: { data },
- container,
-}: SubscriberArgs<{ id: string }>) {
- const { result } = await createTaxRegionWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config: SubscriberConfig = {
- event: "user.created",
-}
-```
-
-### Scheduled Job
-
-```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
-import { MedusaContainer } from "@medusajs/framework/types"
-import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
-
-export default async function myCustomJob(
- container: MedusaContainer
-) {
- const { result } = await createTaxRegionWorkflow(container)
- .run()
-
- console.log(result)
-}
-
-export const config = {
- name: "run-once-a-day",
- schedule: `0 0 * * *`,
-}
-```
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md).
-
-***
-
-## Configure Tax Module
-
-The Tax Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/tax/module-options/index.html.md) for details on the module's options.
-
-***
-
-
# API Key Concepts
In this document, you’ll learn about the different types of API keys, their expiration and verification.
@@ -20775,7 +20776,7 @@ To verify a token received as an input or in a request, use the [authenticate me
# Links between API Key Module and Other Modules
-This document showcases the module links defined between the API Key Module and other commerce modules.
+This document showcases the module links defined between the API Key Module and other Commerce Modules.
## Summary
@@ -20871,6 +20872,263 @@ createRemoteLinkStep({
```
+# Customer Accounts
+
+In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers using the dashboard.
+
+## `has_account` Property
+
+The [Customer data model](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) has a `has_account` property, which is a boolean that indicates whether a customer is registered.
+
+When a guest customer places an order, a new `Customer` record is created with `has_account` set to `false`.
+
+When this or another guest customer registers an account with the same email, a new `Customer` record is created with `has_account` set to `true`.
+
+***
+
+## Email Uniqueness
+
+The above behavior means that two `Customer` records may exist with the same email. However, the main difference is the `has_account` property's value.
+
+So, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email.
+
+
+# Links between Customer Module and Other Modules
+
+This document showcases the module links defined between the Customer Module and other Commerce Modules.
+
+## Summary
+
+The Customer Module has the following links to other modules:
+
+Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
+
+|First Data Model|Second Data Model|Type|Description|
+|---|---|---|---|
+|| in |Stored||
+| in ||Read-only||
+| in ||Read-only||
+
+***
+
+## Payment Module
+
+Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it.
+
+This link is available starting from Medusa `v2.5.0`.
+
+### Retrieve with Query
+
+To retrieve the account holder associated with a customer with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: customers } = await query.graph({
+ entity: "customer",
+ fields: [
+ "account_holders.*",
+ ],
+})
+
+// customers.account_holders
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: customers } = useQueryGraphStep({
+ entity: "customer",
+ fields: [
+ "account_holders.*",
+ ],
+})
+
+// customers.account_holders
+```
+
+### Manage with Link
+
+To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
+
+### link.create
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+
+// ...
+
+await link.create({
+ [Modules.CUSTOMER]: {
+ customer_id: "cus_123",
+ },
+ [Modules.PAYMENT]: {
+ account_holder_id: "acchld_123",
+ },
+})
+```
+
+### createRemoteLinkStep
+
+```ts
+import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+createRemoteLinkStep({
+ [Modules.CUSTOMER]: {
+ customer_id: "cus_123",
+ },
+ [Modules.PAYMENT]: {
+ account_holder_id: "acchld_123",
+ },
+})
+```
+
+***
+
+## Cart Module
+
+Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Customer` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the customer of a cart, and not the other way around.
+
+### Retrieve with Query
+
+To retrieve the customer of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: carts } = await query.graph({
+ entity: "cart",
+ fields: [
+ "customer.*",
+ ],
+})
+
+// carts.customer
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: carts } = useQueryGraphStep({
+ entity: "cart",
+ fields: [
+ "customer.*",
+ ],
+})
+
+// carts.customer
+```
+
+***
+
+## Order Module
+
+Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Customer` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the customer of an order, and not the other way around.
+
+### Retrieve with Query
+
+To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: orders } = await query.graph({
+ entity: "order",
+ fields: [
+ "customer.*",
+ ],
+})
+
+// orders.customer
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: orders } = useQueryGraphStep({
+ entity: "order",
+ fields: [
+ "customer.*",
+ ],
+})
+
+// orders.customer
+```
+
+
+# Links between Currency Module and Other Modules
+
+This document showcases the module links defined between the Currency Module and other Commerce Modules.
+
+## Summary
+
+The Currency Module has the following links to other modules:
+
+Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
+
+|First Data Model|Second Data Model|Type|Description|
+|---|---|---|---|
+| in ||Read-only||
+
+***
+
+## Store Module
+
+The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol.
+
+Instead, Medusa defines a read-only link between the [Store Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/store/index.html.md)'s `Currency` data model and the Currency Module's `Currency` data model. Because the link is read-only from the `Store`'s side, you can only retrieve the details of a store's supported currencies, and not the other way around.
+
+### Retrieve with Query
+
+To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: stores } = await query.graph({
+ entity: "store",
+ fields: [
+ "supported_currencies.currency.*",
+ ],
+})
+
+// stores.supported_currencies
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: stores } = useQueryGraphStep({
+ entity: "store",
+ fields: [
+ "supported_currencies.currency.*",
+ ],
+})
+
+// stores.supported_currencies
+```
+
+
# Cart Concepts
In this document, you’ll get an overview of the main concepts of a cart.
@@ -20908,9 +21166,202 @@ If the fulfillment provider requires additional custom data to be passed along f
The `data` property is an object used to store custom data relevant later for fulfillment.
+# Promotions Adjustments in Carts
+
+In this document, you’ll learn how a promotion is applied to a cart’s line items and shipping methods using adjustment lines.
+
+## What are Adjustment Lines?
+
+An adjustment line indicates a change to an item or a shipping method’s amount. It’s used to apply promotions or discounts on a cart.
+
+The [LineItemAdjustment](https://docs.medusajs.com/references/cart/models/LineItemAdjustment/index.html.md) data model represents changes on a line item, and the [ShippingMethodAdjustment](https://docs.medusajs.com/references/cart/models/ShippingMethodAdjustment/index.html.md) data model represents changes on a shipping method.
+
+
+
+The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. Also, the ID of the applied promotion is stored in the `promotion_id` property of the adjustment line.
+
+***
+
+## Discountable Option
+
+The [LineItem](https://docs.medusajs.com/references/cart/models/LineItem/index.html.md) data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default.
+
+When disabled, a promotion can’t be applied to a line item. In the context of the Promotion Module, the promotion isn’t applied to the line item even if it matches its rules.
+
+***
+
+## Promotion Actions
+
+When using the Cart and Promotion modules together, such as in the Medusa application, use the [computeActions method of the Promotion Module’s main service](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). It retrieves the actions of line items and shipping methods.
+
+Learn more about actions in the [Promotion Module’s documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md).
+
+For example:
+
+```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
+import {
+ ComputeActionAdjustmentLine,
+ ComputeActionItemLine,
+ ComputeActionShippingLine,
+ // ...
+} from "@medusajs/framework/types"
+
+// retrieve the cart
+const cart = await cartModuleService.retrieveCart("cart_123", {
+ relations: [
+ "items.adjustments",
+ "shipping_methods.adjustments",
+ ],
+})
+
+// retrieve line item adjustments
+const lineItemAdjustments: ComputeActionItemLine[] = []
+cart.items.forEach((item) => {
+ const filteredAdjustments = item.adjustments?.filter(
+ (adjustment) => adjustment.code !== undefined
+ ) as unknown as ComputeActionAdjustmentLine[]
+ if (filteredAdjustments.length) {
+ lineItemAdjustments.push({
+ ...item,
+ adjustments: filteredAdjustments,
+ })
+ }
+})
+
+// retrieve shipping method adjustments
+const shippingMethodAdjustments: ComputeActionShippingLine[] =
+ []
+cart.shipping_methods.forEach((shippingMethod) => {
+ const filteredAdjustments =
+ shippingMethod.adjustments?.filter(
+ (adjustment) => adjustment.code !== undefined
+ ) as unknown as ComputeActionAdjustmentLine[]
+ if (filteredAdjustments.length) {
+ shippingMethodAdjustments.push({
+ ...shippingMethod,
+ adjustments: filteredAdjustments,
+ })
+ }
+})
+
+// compute actions
+const actions = await promotionModuleService.computeActions(
+ ["promo_123"],
+ {
+ items: lineItemAdjustments,
+ shipping_methods: shippingMethodAdjustments,
+ }
+)
+```
+
+The `computeActions` method accepts the existing adjustments of line items and shipping methods to compute the actions accurately.
+
+Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the cart’s line item and the shipping method’s adjustments.
+
+```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
+import {
+ AddItemAdjustmentAction,
+ AddShippingMethodAdjustment,
+ // ...
+} from "@medusajs/framework/types"
+
+// ...
+
+await cartModuleService.setLineItemAdjustments(
+ cart.id,
+ actions.filter(
+ (action) => action.action === "addItemAdjustment"
+ ) as AddItemAdjustmentAction[]
+)
+
+await cartModuleService.setShippingMethodAdjustments(
+ cart.id,
+ actions.filter(
+ (action) =>
+ action.action === "addShippingMethodAdjustment"
+ ) as AddShippingMethodAdjustment[]
+)
+```
+
+
+# Tax Lines in Cart Module
+
+In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module.
+
+## What are Tax Lines?
+
+A tax line indicates the tax rate of a line item or a shipping method. The [LineItemTaxLine data model](https://docs.medusajs.com/references/cart/models/LineItemTaxLine/index.html.md) represents a line item’s tax line, and the [ShippingMethodTaxLine data model](https://docs.medusajs.com/references/cart/models/ShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line.
+
+
+
+***
+
+## Tax Inclusivity
+
+By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount, and then adding them to the item/method’s subtotal.
+
+However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes.
+
+So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal.
+
+The following diagram is a simplified showcase of how a subtotal is calculated from the taxes perspective.
+
+
+
+For example, if a line item's amount is `5000`, the tax rate is `10`, and tax inclusivity is enabled, the tax amount is 10% of `5000`, which is `500`, making the unit price of the line item `4500`.
+
+***
+
+## Retrieve Tax Lines
+
+When using the Cart and Tax modules together, you can use the `getTaxLines` method of the Tax Module’s main service. It retrieves the tax lines for a cart’s line items and shipping methods.
+
+```ts
+// retrieve the cart
+const cart = await cartModuleService.retrieveCart("cart_123", {
+ relations: [
+ "items.tax_lines",
+ "shipping_methods.tax_lines",
+ "shipping_address",
+ ],
+})
+
+// retrieve the tax lines
+const taxLines = await taxModuleService.getTaxLines(
+ [
+ ...(cart.items as TaxableItemDTO[]),
+ ...(cart.shipping_methods as TaxableShippingDTO[]),
+ ],
+ {
+ address: {
+ ...cart.shipping_address,
+ country_code:
+ cart.shipping_address.country_code || "us",
+ },
+ }
+)
+```
+
+Then, use the returned tax lines to set the line items and shipping methods’ tax lines:
+
+```ts
+// set line item tax lines
+await cartModuleService.setLineItemTaxLines(
+ cart.id,
+ taxLines.filter((line) => "line_item_id" in line)
+)
+
+// set shipping method tax lines
+await cartModuleService.setLineItemTaxLines(
+ cart.id,
+ taxLines.filter((line) => "shipping_line_id" in line)
+)
+```
+
+
# Links between Cart Module and Other Modules
-This document showcases the module links defined between the Cart Module and other commerce modules.
+This document showcases the module links defined between the Cart Module and other Commerce Modules.
## Summary
@@ -21347,316 +21798,6 @@ const { data: carts } = useQueryGraphStep({
```
-# Tax Lines in Cart Module
-
-In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module.
-
-## What are Tax Lines?
-
-A tax line indicates the tax rate of a line item or a shipping method. The [LineItemTaxLine data model](https://docs.medusajs.com/references/cart/models/LineItemTaxLine/index.html.md) represents a line item’s tax line, and the [ShippingMethodTaxLine data model](https://docs.medusajs.com/references/cart/models/ShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line.
-
-
-
-***
-
-## Tax Inclusivity
-
-By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount, and then adding them to the item/method’s subtotal.
-
-However, line items and shipping methods have an `is_tax_inclusive` property that, when enabled, indicates that the item or method’s price already includes taxes.
-
-So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal.
-
-The following diagram is a simplified showcase of how a subtotal is calculated from the taxes perspective.
-
-
-
-For example, if a line item's amount is `5000`, the tax rate is `10`, and tax inclusivity is enabled, the tax amount is 10% of `5000`, which is `500`, making the unit price of the line item `4500`.
-
-***
-
-## Retrieve Tax Lines
-
-When using the Cart and Tax modules together, you can use the `getTaxLines` method of the Tax Module’s main service. It retrieves the tax lines for a cart’s line items and shipping methods.
-
-```ts
-// retrieve the cart
-const cart = await cartModuleService.retrieveCart("cart_123", {
- relations: [
- "items.tax_lines",
- "shipping_methods.tax_lines",
- "shipping_address",
- ],
-})
-
-// retrieve the tax lines
-const taxLines = await taxModuleService.getTaxLines(
- [
- ...(cart.items as TaxableItemDTO[]),
- ...(cart.shipping_methods as TaxableShippingDTO[]),
- ],
- {
- address: {
- ...cart.shipping_address,
- country_code:
- cart.shipping_address.country_code || "us",
- },
- }
-)
-```
-
-Then, use the returned tax lines to set the line items and shipping methods’ tax lines:
-
-```ts
-// set line item tax lines
-await cartModuleService.setLineItemTaxLines(
- cart.id,
- taxLines.filter((line) => "line_item_id" in line)
-)
-
-// set shipping method tax lines
-await cartModuleService.setLineItemTaxLines(
- cart.id,
- taxLines.filter((line) => "shipping_line_id" in line)
-)
-```
-
-
-# Promotions Adjustments in Carts
-
-In this document, you’ll learn how a promotion is applied to a cart’s line items and shipping methods using adjustment lines.
-
-## What are Adjustment Lines?
-
-An adjustment line indicates a change to an item or a shipping method’s amount. It’s used to apply promotions or discounts on a cart.
-
-The [LineItemAdjustment](https://docs.medusajs.com/references/cart/models/LineItemAdjustment/index.html.md) data model represents changes on a line item, and the [ShippingMethodAdjustment](https://docs.medusajs.com/references/cart/models/ShippingMethodAdjustment/index.html.md) data model represents changes on a shipping method.
-
-
-
-The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. Also, the ID of the applied promotion is stored in the `promotion_id` property of the adjustment line.
-
-***
-
-## Discountable Option
-
-The [LineItem](https://docs.medusajs.com/references/cart/models/LineItem/index.html.md) data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default.
-
-When disabled, a promotion can’t be applied to a line item. In the context of the Promotion Module, the promotion isn’t applied to the line item even if it matches its rules.
-
-***
-
-## Promotion Actions
-
-When using the Cart and Promotion modules together, such as in the Medusa application, use the [computeActions method of the Promotion Module’s main service](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). It retrieves the actions of line items and shipping methods.
-
-Learn more about actions in the [Promotion Module’s documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md).
-
-For example:
-
-```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
-import {
- ComputeActionAdjustmentLine,
- ComputeActionItemLine,
- ComputeActionShippingLine,
- // ...
-} from "@medusajs/framework/types"
-
-// retrieve the cart
-const cart = await cartModuleService.retrieveCart("cart_123", {
- relations: [
- "items.adjustments",
- "shipping_methods.adjustments",
- ],
-})
-
-// retrieve line item adjustments
-const lineItemAdjustments: ComputeActionItemLine[] = []
-cart.items.forEach((item) => {
- const filteredAdjustments = item.adjustments?.filter(
- (adjustment) => adjustment.code !== undefined
- ) as unknown as ComputeActionAdjustmentLine[]
- if (filteredAdjustments.length) {
- lineItemAdjustments.push({
- ...item,
- adjustments: filteredAdjustments,
- })
- }
-})
-
-// retrieve shipping method adjustments
-const shippingMethodAdjustments: ComputeActionShippingLine[] =
- []
-cart.shipping_methods.forEach((shippingMethod) => {
- const filteredAdjustments =
- shippingMethod.adjustments?.filter(
- (adjustment) => adjustment.code !== undefined
- ) as unknown as ComputeActionAdjustmentLine[]
- if (filteredAdjustments.length) {
- shippingMethodAdjustments.push({
- ...shippingMethod,
- adjustments: filteredAdjustments,
- })
- }
-})
-
-// compute actions
-const actions = await promotionModuleService.computeActions(
- ["promo_123"],
- {
- items: lineItemAdjustments,
- shipping_methods: shippingMethodAdjustments,
- }
-)
-```
-
-The `computeActions` method accepts the existing adjustments of line items and shipping methods to compute the actions accurately.
-
-Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the cart’s line item and the shipping method’s adjustments.
-
-```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
-import {
- AddItemAdjustmentAction,
- AddShippingMethodAdjustment,
- // ...
-} from "@medusajs/framework/types"
-
-// ...
-
-await cartModuleService.setLineItemAdjustments(
- cart.id,
- actions.filter(
- (action) => action.action === "addItemAdjustment"
- ) as AddItemAdjustmentAction[]
-)
-
-await cartModuleService.setShippingMethodAdjustments(
- cart.id,
- actions.filter(
- (action) =>
- action.action === "addShippingMethodAdjustment"
- ) as AddShippingMethodAdjustment[]
-)
-```
-
-
-# Auth Providers
-
-In this document, you’ll learn how the Auth Module handles authentication using providers.
-
-## What's an Auth Module Provider?
-
-An auth module provider handles authenticating customers and users, either using custom logic or by integrating a third-party service.
-
-For example, the EmailPass Auth Module Provider authenticates a user using their email and password, whereas the Google Auth Module Provider authenticates users using their Google account.
-
-### Auth Providers List
-
-- [Emailpass](https://docs.medusajs.com/commerce-modules/auth/auth-providers/emailpass/index.html.md)
-- [Google](https://docs.medusajs.com/commerce-modules/auth/auth-providers/google/index.html.md)
-- [GitHub](https://docs.medusajs.com/commerce-modules/auth/auth-providers/github/index.html.md)
-
-***
-
-## Configure Allowed Auth Providers of Actor Types
-
-By default, users of all actor types can authenticate with all installed auth module providers.
-
-To restrict the auth providers used for actor types, use the [authMethodsPerActor option](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthMethodsPerActor/index.html.md) in Medusa's configurations:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- projectConfig: {
- http: {
- authMethodsPerActor: {
- user: ["google"],
- customer: ["emailpass"],
- },
- // ...
- },
- // ...
- },
-})
-```
-
-When you specify the `authMethodsPerActor` configuration, it overrides the default. So, if you don't specify any providers for an actor type, users of that actor type can't authenticate with any provider.
-
-***
-
-## How to Create an Auth Module Provider
-
-Refer to [this guide](https://docs.medusajs.com/references/auth/provider/index.html.md) to learn how to create an auth module provider.
-
-
-# Auth Identity and Actor Types
-
-In this document, you’ll learn about concepts related to identity and actors in the Auth Module.
-
-## What is an Auth Identity?
-
-The [AuthIdentity data model](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) represents a user registered by an [authentication provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/index.html.md). When a user is registered using an authentication provider, the provider creates a record of `AuthIdentity`.
-
-Then, when the user logs-in in the future with the same authentication provider, the associated auth identity is used to validate their credentials.
-
-***
-
-## Actor Types
-
-An actor type is a type of user that can be authenticated. The Auth Module doesn't store or manage any user-like models, such as for customers or users. Instead, the user types are created and managed by other modules. For example, a customer is managed by the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md).
-
-Then, when an auth identity is created for the actor type, the ID of the user is stored in the `app_metadata` property of the auth identity.
-
-For example, an auth identity of a customer has the following `app_metadata` property:
-
-```json
-{
- "app_metadata": {
- "customer_id": "cus_123"
- }
-}
-```
-
-The ID of the user is stored in the key `{actor_type}_id` of the `app_metadata` property.
-
-***
-
-## Protect Routes by Actor Type
-
-When you protect routes with the `authenticate` middleware, you specify in its first parameter the actor type that must be authenticated to access the specified API routes.
-
-For example:
-
-```ts title="src/api/middlewares.ts" highlights={highlights}
-import {
- defineMiddlewares,
- authenticate,
-} from "@medusajs/framework/http"
-
-export default defineMiddlewares({
- routes: [
- {
- matcher: "/custom/admin*",
- middlewares: [
- authenticate("user", ["session", "bearer", "api-key"]),
- ],
- },
- ],
-})
-```
-
-By specifying `user` as the first parameter of `authenticate`, only authenticated users of actor type `user` (admin users) can access API routes starting with `/custom/admin`.
-
-***
-
-## Custom Actor Types
-
-You can define custom actor types that allows a custom user, managed by your custom module, to authenticate into Medusa.
-
-For example, if you have a custom module with a `Manager` data model, you can authenticate managers with the `manager` actor type.
-
-Learn how to create a custom actor type in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md).
-
-
# Authentication Flows with the Auth Main Service
In this document, you'll learn how to use the Auth Module's main service's methods to implement authentication flows and reset a user's password.
@@ -21859,6 +22000,123 @@ In the example above, you use the `emailpass` provider, so you have to pass an o
If the returned `success` property is `true`, the password has reset successfully.
+# Auth Identity and Actor Types
+
+In this document, you’ll learn about concepts related to identity and actors in the Auth Module.
+
+## What is an Auth Identity?
+
+The [AuthIdentity data model](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) represents a user registered by an [authentication provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/index.html.md). When a user is registered using an authentication provider, the provider creates a record of `AuthIdentity`.
+
+Then, when the user logs-in in the future with the same authentication provider, the associated auth identity is used to validate their credentials.
+
+***
+
+## Actor Types
+
+An actor type is a type of user that can be authenticated. The Auth Module doesn't store or manage any user-like models, such as for customers or users. Instead, the user types are created and managed by other modules. For example, a customer is managed by the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md).
+
+Then, when an auth identity is created for the actor type, the ID of the user is stored in the `app_metadata` property of the auth identity.
+
+For example, an auth identity of a customer has the following `app_metadata` property:
+
+```json
+{
+ "app_metadata": {
+ "customer_id": "cus_123"
+ }
+}
+```
+
+The ID of the user is stored in the key `{actor_type}_id` of the `app_metadata` property.
+
+***
+
+## Protect Routes by Actor Type
+
+When you protect routes with the `authenticate` middleware, you specify in its first parameter the actor type that must be authenticated to access the specified API routes.
+
+For example:
+
+```ts title="src/api/middlewares.ts" highlights={highlights}
+import {
+ defineMiddlewares,
+ authenticate,
+} from "@medusajs/framework/http"
+
+export default defineMiddlewares({
+ routes: [
+ {
+ matcher: "/custom/admin*",
+ middlewares: [
+ authenticate("user", ["session", "bearer", "api-key"]),
+ ],
+ },
+ ],
+})
+```
+
+By specifying `user` as the first parameter of `authenticate`, only authenticated users of actor type `user` (admin users) can access API routes starting with `/custom/admin`.
+
+***
+
+## Custom Actor Types
+
+You can define custom actor types that allows a custom user, managed by your custom module, to authenticate into Medusa.
+
+For example, if you have a custom module with a `Manager` data model, you can authenticate managers with the `manager` actor type.
+
+Learn how to create a custom actor type in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md).
+
+
+# Auth Providers
+
+In this document, you’ll learn how the Auth Module handles authentication using providers.
+
+## What's an Auth Module Provider?
+
+An auth module provider handles authenticating customers and users, either using custom logic or by integrating a third-party service.
+
+For example, the EmailPass Auth Module Provider authenticates a user using their email and password, whereas the Google Auth Module Provider authenticates users using their Google account.
+
+### Auth Providers List
+
+- [Emailpass](https://docs.medusajs.com/commerce-modules/auth/auth-providers/emailpass/index.html.md)
+- [Google](https://docs.medusajs.com/commerce-modules/auth/auth-providers/google/index.html.md)
+- [GitHub](https://docs.medusajs.com/commerce-modules/auth/auth-providers/github/index.html.md)
+
+***
+
+## Configure Allowed Auth Providers of Actor Types
+
+By default, users of all actor types can authenticate with all installed auth module providers.
+
+To restrict the auth providers used for actor types, use the [authMethodsPerActor option](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthMethodsPerActor/index.html.md) in Medusa's configurations:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ projectConfig: {
+ http: {
+ authMethodsPerActor: {
+ user: ["google"],
+ customer: ["emailpass"],
+ },
+ // ...
+ },
+ // ...
+ },
+})
+```
+
+When you specify the `authMethodsPerActor` configuration, it overrides the default. So, if you don't specify any providers for an actor type, users of that actor type can't authenticate with any provider.
+
+***
+
+## How to Create an Auth Module Provider
+
+Refer to [this guide](https://docs.medusajs.com/references/auth/provider/index.html.md) to learn how to create an auth module provider.
+
+
# How to Use Authentication Routes
In this document, you'll learn about the authentication routes and how to use them to create and log-in users, and reset their password.
@@ -22596,72 +22854,6 @@ In the workflow, you:
You can use this workflow when deleting a manager, such as in an API route.
-# Auth Module Options
-
-In this document, you'll learn about the options of the Auth Module.
-
-## providers
-
-The `providers` option is an array of auth module providers.
-
-When the Medusa application starts, these providers are registered and can be used to handle authentication.
-
-By default, the `emailpass` provider is registered to authenticate customers and admin users.
-
-For example:
-
-```ts title="medusa-config.ts"
-import { Modules, ContainerRegistrationKeys } from "@medusajs/framework/utils"
-
-// ...
-
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/medusa/auth",
- dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER],
- options: {
- providers: [
- {
- resolve: "@medusajs/medusa/auth-emailpass",
- id: "emailpass",
- options: {
- // provider options...
- },
- },
- ],
- },
- },
- ],
-})
-```
-
-The `providers` option is an array of objects that accept the following properties:
-
-- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory.
-- `id`: A string indicating the provider's unique name or ID.
-- `options`: An optional object of the module provider's options.
-
-***
-
-## Auth CORS
-
-The Medusa application's authentication API routes are defined under the `/auth` prefix that requires setting the `authCors` property of the `http` configuration.
-
-By default, the Medusa application you created will have an `AUTH_CORS` environment variable, which is used as the value of `authCors`.
-
-Refer to [Medusa's configuration guide](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthCors/index.html.md) to learn more about the `authCors` configuration.
-
-***
-
-## authMethodsPerActor Configuration
-
-The Medusa application's configuration accept an `authMethodsPerActor` configuration which restricts the allowed auth providers used with an actor type.
-
-Learn more about the `authMethodsPerActor` configuration in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers#configure-allowed-auth-providers-of-actor-types/index.html.md).
-
-
# How to Handle Password Reset Token Event
In this guide, you'll learn how to handle the `auth.password_reset` event, which is emitted when a request is sent to the [Generate Reset Password Token API route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#generate-reset-password-token-route/index.html.md).
@@ -22672,7 +22864,7 @@ You'll create a subscriber that listens to the event. When the event is emitted,
### Prerequisites
-- [A notification provider module, such as SendGrid](https://docs.medusajs.com/architectural-modules/notification/sendgrid/index.html.md)
+- [A notification provider module, such as SendGrid](https://docs.medusajs.com/infrastructure-modules/notification/sendgrid/index.html.md)
## 1. Create Subscriber
@@ -22776,204 +22968,70 @@ The page shows the user password fields to enter their new password, then submit
- [Storefront Guide: Reset Customer Password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/reset-password/index.html.md)
-# Customer Accounts
+# Auth Module Options
-In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application.
+In this document, you'll learn about the options of the Auth Module.
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/customers/index.html.md) to learn how to manage customers using the dashboard.
+## providers
-## `has_account` Property
+The `providers` option is an array of auth module providers.
-The [Customer data model](https://docs.medusajs.com/references/customer/models/Customer/index.html.md) has a `has_account` property, which is a boolean that indicates whether a customer is registered.
+When the Medusa application starts, these providers are registered and can be used to handle authentication.
-When a guest customer places an order, a new `Customer` record is created with `has_account` set to `false`.
+By default, the `emailpass` provider is registered to authenticate customers and admin users.
-When this or another guest customer registers an account with the same email, a new `Customer` record is created with `has_account` set to `true`.
+For example:
+
+```ts title="medusa-config.ts"
+import { Modules, ContainerRegistrationKeys } from "@medusajs/framework/utils"
+
+// ...
+
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/medusa/auth",
+ dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER],
+ options: {
+ providers: [
+ {
+ resolve: "@medusajs/medusa/auth-emailpass",
+ id: "emailpass",
+ options: {
+ // provider options...
+ },
+ },
+ ],
+ },
+ },
+ ],
+})
+```
+
+The `providers` option is an array of objects that accept the following properties:
+
+- `resolve`: A string indicating the package name of the module provider or the path to it relative to the `src` directory.
+- `id`: A string indicating the provider's unique name or ID.
+- `options`: An optional object of the module provider's options.
***
-## Email Uniqueness
+## Auth CORS
-The above behavior means that two `Customer` records may exist with the same email. However, the main difference is the `has_account` property's value.
+The Medusa application's authentication API routes are defined under the `/auth` prefix that requires setting the `authCors` property of the `http` configuration.
-So, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email.
+By default, the Medusa application you created will have an `AUTH_CORS` environment variable, which is used as the value of `authCors`.
-
-# Links between Customer Module and Other Modules
-
-This document showcases the module links defined between the Customer Module and other commerce modules.
-
-## Summary
-
-The Customer Module has the following links to other modules:
-
-Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
-
-|First Data Model|Second Data Model|Type|Description|
-|---|---|---|---|
-|| in |Stored||
-| in ||Read-only||
-| in ||Read-only||
+Refer to [Medusa's configuration guide](https://docs.medusajs.com/docs/learn/configurations/medusa-config#httpauthCors/index.html.md) to learn more about the `authCors` configuration.
***
-## Payment Module
+## authMethodsPerActor Configuration
-Medusa defines a link between the `Customer` and `AccountHolder` data models, allowing payment providers to save payment methods for a customer, if the payment provider supports it.
+The Medusa application's configuration accept an `authMethodsPerActor` configuration which restricts the allowed auth providers used with an actor type.
-This link is available starting from Medusa `v2.5.0`.
-
-### Retrieve with Query
-
-To retrieve the account holder associated with a customer with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: customers } = await query.graph({
- entity: "customer",
- fields: [
- "account_holders.*",
- ],
-})
-
-// customers.account_holders
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: customers } = useQueryGraphStep({
- entity: "customer",
- fields: [
- "account_holders.*",
- ],
-})
-
-// customers.account_holders
-```
-
-### Manage with Link
-
-To manage the account holders of a customer, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
-
-### link.create
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-await link.create({
- [Modules.CUSTOMER]: {
- customer_id: "cus_123",
- },
- [Modules.PAYMENT]: {
- account_holder_id: "acchld_123",
- },
-})
-```
-
-### createRemoteLinkStep
-
-```ts
-import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-createRemoteLinkStep({
- [Modules.CUSTOMER]: {
- customer_id: "cus_123",
- },
- [Modules.PAYMENT]: {
- account_holder_id: "acchld_123",
- },
-})
-```
-
-***
-
-## Cart Module
-
-Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Customer` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the customer of a cart, and not the other way around.
-
-### Retrieve with Query
-
-To retrieve the customer of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: carts } = await query.graph({
- entity: "cart",
- fields: [
- "customer.*",
- ],
-})
-
-// carts.customer
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: carts } = useQueryGraphStep({
- entity: "cart",
- fields: [
- "customer.*",
- ],
-})
-
-// carts.customer
-```
-
-***
-
-## Order Module
-
-Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Customer` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the customer of an order, and not the other way around.
-
-### Retrieve with Query
-
-To retrieve the customer of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: orders } = await query.graph({
- entity: "order",
- fields: [
- "customer.*",
- ],
-})
-
-// orders.customer
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: orders } = useQueryGraphStep({
- entity: "order",
- fields: [
- "customer.*",
- ],
-})
-
-// orders.customer
-```
+Learn more about the `authMethodsPerActor` configuration in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers#configure-allowed-auth-providers-of-actor-types/index.html.md).
# Inventory Concepts
@@ -23080,63 +23138,6 @@ This flow is implemented within the [confirmReturnReceiveWorkflow](https://docs.
If a returned item is considered damaged or is dismissed, its quantity doesn't increment the `stocked_quantity` of the inventory item's level.
-# Links between Currency Module and Other Modules
-
-This document showcases the module links defined between the Currency Module and other commerce modules.
-
-## Summary
-
-The Currency Module has the following links to other modules:
-
-Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
-
-|First Data Model|Second Data Model|Type|Description|
-|---|---|---|---|
-| in ||Read-only||
-
-***
-
-## Store Module
-
-The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol.
-
-Instead, Medusa defines a read-only link between the [Store Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/store/index.html.md)'s `Currency` data model and the Currency Module's `Currency` data model. Because the link is read-only from the `Store`'s side, you can only retrieve the details of a store's supported currencies, and not the other way around.
-
-### Retrieve with Query
-
-To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: stores } = await query.graph({
- entity: "store",
- fields: [
- "supported_currencies.currency.*",
- ],
-})
-
-// stores.supported_currencies
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: stores } = useQueryGraphStep({
- entity: "store",
- fields: [
- "supported_currencies.currency.*",
- ],
-})
-
-// stores.supported_currencies
-```
-
-
# Inventory Kits
In this guide, you'll learn how inventory kits can be used in the Medusa application to support use cases like multi-part products, bundled products, and shared inventory across products.
@@ -23527,7 +23528,7 @@ You can now [execute the workflow](https://docs.medusajs.com/docs/learn/fundamen
# Links between Inventory Module and Other Modules
-This document showcases the module links defined between the Inventory Module and other commerce modules.
+This document showcases the module links defined between the Inventory Module and other Commerce Modules.
## Summary
@@ -23712,6 +23713,33 @@ A shipping profile defines a type of items that are shipped in a similar manner.
A shipping profile is represented by the [ShippingProfile data model](https://docs.medusajs.com/references/fulfillment/models/ShippingProfile/index.html.md). It only defines the profile’s details, but it’s associated with the shipping options available for the item type.
+# Fulfillment Module Provider
+
+In this document, you’ll learn what a fulfillment module provider is.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/locations#manage-fulfillment-providers/index.html.md) to learn how to add a fulfillment provider to a location using the dashboard.
+
+## What’s a Fulfillment Module Provider?
+
+A fulfillment module provider handles fulfilling items, typically using a third-party integration.
+
+Fulfillment module providers registered in the Fulfillment Module's [options](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md) are stored and represented by the [FulfillmentProvider data model](https://docs.medusajs.com/references/fulfillment/models/FulfillmentProvider/index.html.md).
+
+***
+
+## Configure Fulfillment Providers
+
+The Fulfillment Module accepts a `providers` option that allows you to register providers in your application.
+
+Learn more about the `providers` option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md).
+
+***
+
+## How to Create a Fulfillment Provider?
+
+Refer to [this guide](https://docs.medusajs.com/references/fulfillment/provider/index.html.md) to learn how to create a fulfillment module provider.
+
+
# Item Fulfillment
In this document, you’ll learn about the concepts of item fulfillment.
@@ -23765,36 +23793,54 @@ The `Fulfillment` data model has three properties to keep track of the current s
- `delivered_at`: The date the fulfillment was delivered. If set, then the fulfillment has been delivered.
-# Fulfillment Module Provider
+# Fulfillment Module Options
-In this document, you’ll learn what a fulfillment module provider is.
+In this document, you'll learn about the options of the Fulfillment Module.
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/locations#manage-fulfillment-providers/index.html.md) to learn how to add a fulfillment provider to a location using the dashboard.
+## providers
-## What’s a Fulfillment Module Provider?
+The `providers` option is an array of fulfillment module providers.
-A fulfillment module provider handles fulfilling items, typically using a third-party integration.
+When the Medusa application starts, these providers are registered and can be used to process fulfillments.
-Fulfillment module providers registered in the Fulfillment Module's [options](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md) are stored and represented by the [FulfillmentProvider data model](https://docs.medusajs.com/references/fulfillment/models/FulfillmentProvider/index.html.md).
+For example:
-***
+```ts title="medusa-config.ts"
+import { Modules } from "@medusajs/framework/utils"
-## Configure Fulfillment Providers
+// ...
-The Fulfillment Module accepts a `providers` option that allows you to register providers in your application.
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/medusa/fulfillment",
+ options: {
+ providers: [
+ {
+ resolve: `@medusajs/medusa/fulfillment-manual`,
+ id: "manual",
+ options: {
+ // provider options...
+ },
+ },
+ ],
+ },
+ },
+ ],
+})
+```
-Learn more about the `providers` option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md).
+The `providers` option is an array of objects that accept the following properties:
-***
-
-## How to Create a Fulfillment Provider?
-
-Refer to [this guide](https://docs.medusajs.com/references/fulfillment/provider/index.html.md) to learn how to create a fulfillment module provider.
+- `resolve`: A string indicating either the package name of the module provider or the path to it relative to the `src` directory.
+- `id`: A string indicating the provider's unique name or ID.
+- `options`: An optional object of the module provider's options.
# Links between Fulfillment Module and Other Modules
-This document showcases the module links defined between the Fulfillment Module and other commerce modules.
+This document showcases the module links defined between the Fulfillment Module and other Commerce Modules.
## Summary
@@ -24152,51 +24198,6 @@ createRemoteLinkStep({
```
-# Fulfillment Module Options
-
-In this document, you'll learn about the options of the Fulfillment Module.
-
-## providers
-
-The `providers` option is an array of fulfillment module providers.
-
-When the Medusa application starts, these providers are registered and can be used to process fulfillments.
-
-For example:
-
-```ts title="medusa-config.ts"
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/medusa/fulfillment",
- options: {
- providers: [
- {
- resolve: `@medusajs/medusa/fulfillment-manual`,
- id: "manual",
- options: {
- // provider options...
- },
- },
- ],
- },
- },
- ],
-})
-```
-
-The `providers` option is an array of objects that accept the following properties:
-
-- `resolve`: A string indicating either the package name of the module provider or the path to it relative to the `src` directory.
-- `id`: A string indicating the provider's unique name or ID.
-- `options`: An optional object of the module provider's options.
-
-
# Shipping Option
In this document, you’ll learn about shipping options and their rules.
@@ -24476,7 +24477,7 @@ When an exchange is confirmed, the order’s version is incremented.
# Links between Order Module and Other Modules
-This document showcases the module links defined between the Order Module and other commerce modules.
+This document showcases the module links defined between the Order Module and other Commerce Modules.
## Summary
@@ -25378,7 +25379,7 @@ This flow is only supported if the chosen payment provider has implemented the n
# Links between Payment Module and Other Modules
-This document showcases the module links defined between the Payment Module and other commerce modules.
+This document showcases the module links defined between the Payment Module and other Commerce Modules.
## Summary
@@ -25777,6 +25778,162 @@ The `providers` option is an array of objects that accept the following properti
- `options`: An optional object of the module provider's options.
+# Payment
+
+In this document, you’ll learn what a payment is and how it's created, captured, and refunded.
+
+## What's a Payment?
+
+When a payment session is authorized, a payment, represented by the [Payment data model](https://docs.medusajs.com/references/payment/models/Payment/index.html.md), is created. This payment can later be captured or refunded.
+
+A payment carries many of the data and relations of a payment session:
+
+- It belongs to the same payment collection.
+- It’s associated with the same payment provider, which handles further payment processing.
+- It stores the payment session’s `data` property in its `data` property, as it’s still useful for the payment provider’s processing.
+
+***
+
+## Capture Payments
+
+When a payment is captured, a capture, represented by the [Capture data model](https://docs.medusajs.com/references/payment/models/Capture/index.html.md), is created. It holds details related to the capture, such as the amount, the capture date, and more.
+
+The payment can also be captured incrementally, each time a capture record is created for that amount.
+
+
+
+***
+
+## Refund Payments
+
+When a payment is refunded, a refund, represented by the [Refund data model](https://docs.medusajs.com/references/payment/models/Refund/index.html.md), is created. It holds details related to the refund, such as the amount, refund date, and more.
+
+A payment can be refunded multiple times, and each time a refund record is created.
+
+
+
+
+# Payment Module Provider
+
+In this document, you’ll learn what a payment module provider is.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage the payment providers available in a region using the dashboard.
+
+## What's a Payment Module Provider?
+
+A payment module provider registers a payment provider that handles payment processing in the Medusa application. It integrates third-party payment providers, such as Stripe.
+
+To authorize a payment amount with a payment provider, a payment session is created and associated with that payment provider. The payment provider is then used to handle the authorization.
+
+After the payment session is authorized, the payment provider is associated with the resulting payment and handles its payment processing, such as to capture or refund payment.
+
+### List of Payment Module Providers
+
+- [Stripe](https://docs.medusajs.com/commerce-modules/payment/payment-provider/stripe/index.html.md)
+
+***
+
+## System Payment Provider
+
+The Payment Module provides a `system` payment provider that acts as a placeholder payment provider.
+
+It doesn’t handle payment processing and delegates that to the merchant. It acts similarly to a cash-on-delivery (COD) payment method.
+
+***
+
+## How are Payment Providers Created?
+
+A payment provider is a module whose main service extends the `AbstractPaymentProvider` imported from `@medusajs/framework/utils`.
+
+Refer to [this guide](https://docs.medusajs.com/references/payment/provider/index.html.md) on how to create a payment provider for the Payment Module.
+
+***
+
+## Configure Payment Providers
+
+The Payment Module accepts a `providers` option that allows you to register providers in your application.
+
+Learn more about this option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/module-options#providers/index.html.md).
+
+***
+
+## PaymentProvider Data Model
+
+When the Medusa application starts and registers the payment providers, it also creates a record of the `PaymentProvider` data model if none exists.
+
+This data model is used to reference a payment provider and determine whether it’s installed in the application.
+
+
+# Payment Collection
+
+In this document, you’ll learn what a payment collection is and how the Medusa application uses it with the Cart Module.
+
+## What's a Payment Collection?
+
+A payment collection stores payment details related to a resource, such as a cart or an order. It’s represented by the [PaymentCollection data model](https://docs.medusajs.com/references/payment/models/PaymentCollection/index.html.md).
+
+Every purchase or request for payment starts with a payment collection. The collection holds details necessary to complete the payment, including:
+
+- The [payment sessions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-session/index.html.md) that represents the payment amount to authorize.
+- The [payments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment/index.html.md) that are created when a payment session is authorized. They can be captured and refunded.
+- The [payment providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/index.html.md) that handle the processing of each payment session, including the authorization, capture, and refund.
+
+***
+
+## Multiple Payments
+
+The payment collection supports multiple payment sessions and payments.
+
+You can use this to accept payments in increments or split payments across payment providers.
+
+
+
+***
+
+## Usage with the Cart Module
+
+The Cart Module provides cart management features. However, it doesn’t provide any features related to accepting payment.
+
+During checkout, the Medusa application links a cart to a payment collection, which will be used for further payment processing.
+
+It also implements the payment flow during checkout as explained in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-flow/index.html.md).
+
+
+
+
+# Payment Session
+
+In this document, you’ll learn what a payment session is.
+
+## What's a Payment Session?
+
+A payment session, represented by the [PaymentSession data model](https://docs.medusajs.com/references/payment/models/PaymentSession/index.html.md), is a payment amount to be authorized. It’s associated with a payment provider that handles authorizing it.
+
+A payment collection can have multiple payment sessions. Using this feature, you can implement payment in installments or payments using multiple providers.
+
+
+
+***
+
+## data Property
+
+Payment providers may need additional data to process the payment later. The `PaymentSession` data model has a `data` property used to store that data.
+
+For example, the customer's ID in Stripe is stored in the `data` property.
+
+***
+
+## Payment Session Status
+
+The `status` property of a payment session indicates its current status. Its value can be:
+
+- `pending`: The payment session is awaiting authorization.
+- `requires_more`: The payment session requires an action before it’s authorized. For example, to enter a 3DS code.
+- `authorized`: The payment session is authorized.
+- `error`: An error occurred while authorizing the payment.
+- `canceled`: The authorization of the payment session has been canceled.
+
+
# Accept Payment Flow
In this document, you’ll learn how to implement an accept-payment flow using workflows or the Payment Module's main service.
@@ -25944,162 +26101,6 @@ You can then:
Some payment providers allow capturing the payment automatically once it’s authorized. In that case, you don’t need to do it manually.
-# Payment Collection
-
-In this document, you’ll learn what a payment collection is and how the Medusa application uses it with the Cart Module.
-
-## What's a Payment Collection?
-
-A payment collection stores payment details related to a resource, such as a cart or an order. It’s represented by the [PaymentCollection data model](https://docs.medusajs.com/references/payment/models/PaymentCollection/index.html.md).
-
-Every purchase or request for payment starts with a payment collection. The collection holds details necessary to complete the payment, including:
-
-- The [payment sessions](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-session/index.html.md) that represents the payment amount to authorize.
-- The [payments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment/index.html.md) that are created when a payment session is authorized. They can be captured and refunded.
-- The [payment providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/index.html.md) that handle the processing of each payment session, including the authorization, capture, and refund.
-
-***
-
-## Multiple Payments
-
-The payment collection supports multiple payment sessions and payments.
-
-You can use this to accept payments in increments or split payments across payment providers.
-
-
-
-***
-
-## Usage with the Cart Module
-
-The Cart Module provides cart management features. However, it doesn’t provide any features related to accepting payment.
-
-During checkout, the Medusa application links a cart to a payment collection, which will be used for further payment processing.
-
-It also implements the payment flow during checkout as explained in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-flow/index.html.md).
-
-
-
-
-# Payment
-
-In this document, you’ll learn what a payment is and how it's created, captured, and refunded.
-
-## What's a Payment?
-
-When a payment session is authorized, a payment, represented by the [Payment data model](https://docs.medusajs.com/references/payment/models/Payment/index.html.md), is created. This payment can later be captured or refunded.
-
-A payment carries many of the data and relations of a payment session:
-
-- It belongs to the same payment collection.
-- It’s associated with the same payment provider, which handles further payment processing.
-- It stores the payment session’s `data` property in its `data` property, as it’s still useful for the payment provider’s processing.
-
-***
-
-## Capture Payments
-
-When a payment is captured, a capture, represented by the [Capture data model](https://docs.medusajs.com/references/payment/models/Capture/index.html.md), is created. It holds details related to the capture, such as the amount, the capture date, and more.
-
-The payment can also be captured incrementally, each time a capture record is created for that amount.
-
-
-
-***
-
-## Refund Payments
-
-When a payment is refunded, a refund, represented by the [Refund data model](https://docs.medusajs.com/references/payment/models/Refund/index.html.md), is created. It holds details related to the refund, such as the amount, refund date, and more.
-
-A payment can be refunded multiple times, and each time a refund record is created.
-
-
-
-
-# Payment Module Provider
-
-In this document, you’ll learn what a payment module provider is.
-
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/regions/index.html.md) to learn how to manage the payment providers available in a region using the dashboard.
-
-## What's a Payment Module Provider?
-
-A payment module provider registers a payment provider that handles payment processing in the Medusa application. It integrates third-party payment providers, such as Stripe.
-
-To authorize a payment amount with a payment provider, a payment session is created and associated with that payment provider. The payment provider is then used to handle the authorization.
-
-After the payment session is authorized, the payment provider is associated with the resulting payment and handles its payment processing, such as to capture or refund payment.
-
-### List of Payment Module Providers
-
-- [Stripe](https://docs.medusajs.com/commerce-modules/payment/payment-provider/stripe/index.html.md)
-
-***
-
-## System Payment Provider
-
-The Payment Module provides a `system` payment provider that acts as a placeholder payment provider.
-
-It doesn’t handle payment processing and delegates that to the merchant. It acts similarly to a cash-on-delivery (COD) payment method.
-
-***
-
-## How are Payment Providers Created?
-
-A payment provider is a module whose main service extends the `AbstractPaymentProvider` imported from `@medusajs/framework/utils`.
-
-Refer to [this guide](https://docs.medusajs.com/references/payment/provider/index.html.md) on how to create a payment provider for the Payment Module.
-
-***
-
-## Configure Payment Providers
-
-The Payment Module accepts a `providers` option that allows you to register providers in your application.
-
-Learn more about this option in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/module-options#providers/index.html.md).
-
-***
-
-## PaymentProvider Data Model
-
-When the Medusa application starts and registers the payment providers, it also creates a record of the `PaymentProvider` data model if none exists.
-
-This data model is used to reference a payment provider and determine whether it’s installed in the application.
-
-
-# Payment Session
-
-In this document, you’ll learn what a payment session is.
-
-## What's a Payment Session?
-
-A payment session, represented by the [PaymentSession data model](https://docs.medusajs.com/references/payment/models/PaymentSession/index.html.md), is a payment amount to be authorized. It’s associated with a payment provider that handles authorizing it.
-
-A payment collection can have multiple payment sessions. Using this feature, you can implement payment in installments or payments using multiple providers.
-
-
-
-***
-
-## data Property
-
-Payment providers may need additional data to process the payment later. The `PaymentSession` data model has a `data` property used to store that data.
-
-For example, the customer's ID in Stripe is stored in the `data` property.
-
-***
-
-## Payment Session Status
-
-The `status` property of a payment session indicates its current status. Its value can be:
-
-- `pending`: The payment session is awaiting authorization.
-- `requires_more`: The payment session requires an action before it’s authorized. For example, to enter a 3DS code.
-- `authorized`: The payment session is authorized.
-- `error`: An error occurred while authorizing the payment.
-- `canceled`: The authorization of the payment session has been canceled.
-
-
# Webhook Events
In this document, you’ll learn how the Payment Module supports listening to webhook events.
@@ -26136,508 +26137,60 @@ If the event's details indicate that the payment should be captured, then the [c
After the payment webhook actions are processed and the payment is authorized or captured, the Medusa application completes the cart associated with the payment's collection if it's not completed yet.
-# Pricing Concepts
+# Configure Selling Products
-In this document, you’ll learn about the main concepts in the Pricing Module.
+In this guide, you'll learn how to set up and configure your products based on their shipping and inventory requirements, the product type, how you want to sell them, or your commerce ecosystem.
-## Price Set
+The concepts in this guide are applicable starting from Medusa v2.5.1.
-A [PriceSet](https://docs.medusajs.com/references/pricing/models/PriceSet/index.html.md) represents a collection of prices that are linked to a resource (for example, a product or a shipping option).
+## Scenario
-Each of these prices are represented by the [Price data module](https://docs.medusajs.com/references/pricing/models/Price/index.html.md).
+Businesses can have different selling requirements:
-
+1. They may sell physical or digital items.
+2. They may sell items that don't require shipping or inventory management, such as selling digital products, services, or booking appointments.
+3. They may sell items whose inventory is managed by an external system, such as an ERP.
+
+Medusa supports these different selling requirements by allowing you to configure shipping and inventory requirements for products and their variants. This guide explains how these configurations work, then provides examples of setting up different use cases.
***
-## Price List
+## Configuring Shipping Requirements
-A [PriceList](https://docs.medusajs.com/references/pricing/models/PriceList/index.html.md) is a group of prices only enabled if their conditions and rules are satisfied.
+The Medusa application defines a link between the `Product` data model and a [ShippingProfile](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/concepts#shipping-profile/index.html.md) in the [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md), allowing you to associate a product with a shipping profile.
-A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied.
+When a product is associated with a shipping profile, its variants require shipping and fulfillment when purchased. This is useful for physical products or digital products that require custom fulfillment.
-Its associated prices are represented by the `Price` data model.
+If a product doesn't have an associated shipping profile, its variants don't require shipping and fulfillment when purchased. This is useful for digital products, for example, that don't require shipping.
+### Overriding Shipping Requirements for Variants
-# Links between Pricing Module and Other Modules
+A product variant whose inventory is managed by Medusa (its `manage_inventory` property is enabled) has an [inventory item](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventoryitem/index.html.md). The inventory item has a `requires_shipping` property that can be used to override its shipping requirement. This is useful if the product has an associated shipping profile but you want to disable shipping for a specific variant, or vice versa.
-This document showcases the module links defined between the Pricing Module and other commerce modules.
+Learn more about product variant's inventory in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory/index.html.md).
-## Summary
+When a product variant is purchased, the Medusa application decides whether the purchased item requires shipping in the following order:
-The Pricing Module has the following links to other modules:
-
-|First Data Model|Second Data Model|Type|Description|
-|---|---|---|---|
-| in ||Stored||
-| in ||Stored||
+1. The product variant has an inventory item. In this case, the Medusa application uses the inventory item's `requires_shipping` property to determine if the item requires shipping.
+2. If the product variant doesn't have an inventory item, the Medusa application checks whether the product has an associated shipping profile to determine if the item requires shipping.
***
-## Fulfillment Module
+## Use Case Examples
-The Fulfillment Module provides fulfillment-related functionalities, including shipping options that the customer chooses from when they place their order. However, it doesn't provide pricing-related functionalities for these options.
+By combining configurations of shipment requirements and inventory management, you can set up your products to support your use case:
-Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set.
-
-
-
-### Retrieve with Query
-
-To retrieve the shipping option of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `shipping_option.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: priceSets } = await query.graph({
- entity: "price_set",
- fields: [
- "shipping_option.*",
- ],
-})
-
-// priceSets.shipping_option
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: priceSets } = useQueryGraphStep({
- entity: "price_set",
- fields: [
- "shipping_option.*",
- ],
-})
-
-// priceSets.shipping_option
-```
-
-### Manage with Link
-
-To manage the price set of a shipping option, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
-
-### link.create
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-await link.create({
- [Modules.FULFILLMENT]: {
- shipping_option_id: "so_123",
- },
- [Modules.PRICING]: {
- price_set_id: "pset_123",
- },
-})
-```
-
-### createRemoteLinkStep
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-createRemoteLinkStep({
- [Modules.FULFILLMENT]: {
- shipping_option_id: "so_123",
- },
- [Modules.PRICING]: {
- price_set_id: "pset_123",
- },
-})
-```
-
-***
-
-## Product Module
-
-The Product Module doesn't store or manage the prices of product variants.
-
-Medusa defines a link between the `ProductVariant` and the `PriceSet`. A product variant’s prices are stored as prices belonging to a price set.
-
-
-
-So, when you want to add prices for a product variant, you create a price set and add the prices to it.
-
-You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context.
-
-### Retrieve with Query
-
-To retrieve the variant of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: priceSets } = await query.graph({
- entity: "price_set",
- fields: [
- "variant.*",
- ],
-})
-
-// priceSets.variant
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: priceSets } = useQueryGraphStep({
- entity: "price_set",
- fields: [
- "variant.*",
- ],
-})
-
-// priceSets.variant
-```
-
-### Manage with Link
-
-To manage the price set of a variant, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
-
-### link.create
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-await link.create({
- [Modules.PRODUCT]: {
- variant_id: "variant_123",
- },
- [Modules.PRICING]: {
- price_set_id: "pset_123",
- },
-})
-```
-
-### createRemoteLinkStep
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-createRemoteLinkStep({
- [Modules.PRODUCT]: {
- variant_id: "variant_123",
- },
- [Modules.PRICING]: {
- price_set_id: "pset_123",
- },
-})
-```
-
-
-# Prices Calculation
-
-In this document, you'll learn how prices are calculated when you use the [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) of the Pricing Module's main service.
-
-## calculatePrices Method
-
-The [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) accepts as parameters the ID of one or more price sets and a context.
-
-It returns a price object with the best matching price for each price set.
-
-### Calculation Context
-
-The calculation context is an optional object passed as a second parameter to the `calculatePrices` method. It accepts rules to restrict the selected prices in the price set.
-
-For example:
-
-```ts
-const price = await pricingModuleService.calculatePrices(
- { id: [priceSetId] },
- {
- context: {
- currency_code: currencyCode,
- region_id: "reg_123",
- },
- }
-)
-```
-
-In this example, you retrieve the prices in a price set for the specified currency code and region ID.
-
-### Returned Price Object
-
-For each price set, the `calculatePrices` method selects two prices:
-
-- A calculated price: Either a price that belongs to a price list and best matches the specified context, or the same as the original price.
-- An original price, which is either:
- - The same price as the calculated price if the price list it belongs to is of type `override`;
- - Or a price that doesn't belong to a price list and best matches the specified context.
-
-Both prices are returned in an object that has the following properties:
-
-- id: (\`string\`) The ID of the price set from which the price was selected.
-- is\_calculated\_price\_price\_list: (\`boolean\`) Whether the calculated price belongs to a price list.
-- calculated\_amount: (\`number\`) The amount of the calculated price, or \`null\` if there isn't a calculated price. This is the amount shown to the customer.
-- is\_original\_price\_price\_list: (\`boolean\`) Whether the original price belongs to a price list.
-- original\_amount: (\`number\`) The amount of the original price, or \`null\` if there isn't an original price. This amount is useful to compare with the \`calculated\_amount\`, such as to check for discounted value.
-- currency\_code: (\`string\`) The currency code of the calculated price, or \`null\` if there isn't a calculated price.
-- is\_calculated\_price\_tax\_inclusive: (\`boolean\`) Whether the calculated price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx)
-- is\_original\_price\_tax\_inclusive: (\`boolean\`) Whether the original price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx)
-- calculated\_price: (\`object\`) The calculated price's price details.
-
- - id: (\`string\`) The ID of the price.
-
- - price\_list\_id: (\`string\`) The ID of the associated price list.
-
- - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`.
-
- - min\_quantity: (\`number\`) The price's min quantity condition.
-
- - max\_quantity: (\`number\`) The price's max quantity condition.
-- original\_price: (\`object\`) The original price's price details.
-
- - id: (\`string\`) The ID of the price.
-
- - price\_list\_id: (\`string\`) The ID of the associated price list.
-
- - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`.
-
- - min\_quantity: (\`number\`) The price's min quantity condition.
-
- - max\_quantity: (\`number\`) The price's max quantity condition.
-
-***
-
-## Examples
-
-Consider the following price set:
-
-```ts
-const priceSet = await pricingModuleService.createPriceSets({
- prices: [
- // default price
- {
- amount: 500,
- currency_code: "EUR",
- rules: {},
- },
- // prices with rules
- {
- amount: 400,
- currency_code: "EUR",
- rules: {
- region_id: "reg_123",
- },
- },
- {
- amount: 450,
- currency_code: "EUR",
- rules: {
- city: "krakow",
- },
- },
- {
- amount: 500,
- currency_code: "EUR",
- rules: {
- city: "warsaw",
- region_id: "reg_123",
- },
- },
- ],
-})
-```
-
-### Default Price Selection
-
-### Code
-
-```ts
-const price = await pricingModuleService.calculatePrices(
- { id: [priceSet.id] },
- {
- context: {
- currency_code: "EUR"
- }
- }
-)
-```
-
-### Result
-
-### Calculate Prices with Rules
-
-### Code
-
-```ts
-const price = await pricingModuleService.calculatePrices(
- { id: [priceSet.id] },
- {
- context: {
- currency_code: "EUR",
- region_id: "reg_123",
- city: "krakow"
- }
- }
-)
-```
-
-### Result
-
-### Price Selection with Price List
-
-### Code
-
-```ts
-const priceList = pricingModuleService.createPriceLists([{
- title: "Summer Price List",
- description: "Price list for summer sale",
- starts_at: Date.parse("01/10/2023").toString(),
- ends_at: Date.parse("31/10/2023").toString(),
- rules: {
- region_id: ['PL']
- },
- type: "sale",
- prices: [
- {
- amount: 400,
- currency_code: "EUR",
- price_set_id: priceSet.id,
- },
- {
- amount: 450,
- currency_code: "EUR",
- price_set_id: priceSet.id,
- },
- ],
-}]);
-
-const price = await pricingModuleService.calculatePrices(
- { id: [priceSet.id] },
- {
- context: {
- currency_code: "EUR",
- region_id: "PL",
- city: "krakow"
- }
- }
-)
-```
-
-### Result
-
-
-# Price Rules
-
-In this document, you'll learn about price rules for price sets and price lists.
-
-## Price Rule
-
-You can restrict prices by rules. Each rule of a price is represented by the [PriceRule data model](https://docs.medusajs.com/references/pricing/models/PriceRule/index.html.md).
-
-The `Price` data model has a `rules_count` property, which indicates how many rules, represented by `PriceRule`, are applied to the price.
-
-For exmaple, you create a price restricted to `10557` zip codes.
-
-
-
-A price can have multiple price rules.
-
-For example, a price can be restricted by a region and a zip code.
-
-
-
-***
-
-## Price List Rules
-
-Rules applied to a price list are represented by the [PriceListRule data model](https://docs.medusajs.com/references/pricing/models/PriceListRule/index.html.md).
-
-The `rules_count` property of a `PriceList` indicates how many rules are applied to it.
-
-
-
-
-# Tax-Inclusive Pricing
-
-In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices.
-
-## What is Tax-Inclusive Pricing?
-
-A tax-inclusive price is a price of a resource that includes taxes. Medusa calculates the tax amount from the price rather than adds the amount to it.
-
-For example, if a product’s price is $50, the tax rate is 2%, and tax-inclusive pricing is enabled, then the product's price is $49, and the applied tax amount is $1.
-
-***
-
-## How is Tax-Inclusive Pricing Set?
-
-The [PricePreference data model](https://docs.medusajs.com/references/pricing/models/PricePreference/index.html.md) holds the tax-inclusive setting for a context. It has two properties that indicate the context:
-
-- `attribute`: The name of the attribute to compare against. For example, `region_id` or `currency_code`.
-- `value`: The attribute’s value. For example, `reg_123` or `usd`.
-
-Only `region_id` and `currency_code` are supported as an `attribute` at the moment.
-
-The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context.
-
-For example:
-
-```json
-{
- "attribute": "currency_code",
- "value": "USD",
- "is_tax_inclusive": true,
-}
-```
-
-In this example, tax-inclusivity is enabled for the `USD` currency code.
-
-***
-
-## Tax-Inclusive Pricing in Price Calculation
-
-### Tax Context
-
-As mentioned in the [Price Calculation documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), The `calculatePrices` method accepts as a parameter a calculation context.
-
-To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context.
-
-### Returned Tax Properties
-
-The `calculatePrices` method returns two properties related to tax-inclusivity:
-
-Learn more about the returned properties in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md).
-
-- `is_calculated_price_tax_inclusive`: Whether the selected `calculated_price` is tax-inclusive.
-- `is_original_price_tax_inclusive` : Whether the selected `original_price` is tax-inclusive.
-
-A price is considered tax-inclusive if:
-
-1. It belongs to the region or currency code specified in the calculation context;
-2. and the region or currency code has a price preference with `is_tax_inclusive` enabled.
-
-### Tax Context Precedence
-
-A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if:
-
-- both the `region_id` and `currency_code` are provided in the calculation context;
-- the selected price belongs to the region;
-- and the region has a price preference
+|Use Case|Configurations|Example|
+|---|---|---|---|---|
+|Item that's shipped on purchase, and its variant inventory is managed by the Medusa application.||Any stock-kept item (clothing, for example), whose inventory is managed in the Medusa application.|
+|Item that's shipped on purchase, but its variant inventory is managed externally (not by Medusa) or it has infinite stock.||Any stock-kept item (clothing, for example), whose inventory is managed in an ERP or has infinite stock.|
+|Item that's not shipped on purchase, but its variant inventory is managed by Medusa.||Digital products, such as licenses, that don't require shipping but have a limited quantity.|
+|Item that doesn't require shipping and its variant inventory isn't managed by Medusa.|||
# Links between Product Module and Other Modules
-This document showcases the module links defined between the Product Module and other commerce modules.
+This document showcases the module links defined between the Product Module and other Commerce Modules.
## Summary
@@ -27147,92 +26700,503 @@ The following guides provide more details on inventory management in the Medusa
- [Storefront guide: how to retrieve a product variant's inventory details](https://docs.medusajs.com/resources/storefront-development/products/inventory/index.html.md).
-# Configure Selling Products
+# Pricing Concepts
-In this guide, you'll learn how to set up and configure your products based on their shipping and inventory requirements, the product type, how you want to sell them, or your commerce ecosystem.
+In this document, you’ll learn about the main concepts in the Pricing Module.
-The concepts in this guide are applicable starting from Medusa v2.5.1.
+## Price Set
-## Scenario
+A [PriceSet](https://docs.medusajs.com/references/pricing/models/PriceSet/index.html.md) represents a collection of prices that are linked to a resource (for example, a product or a shipping option).
-Businesses can have different selling requirements:
+Each of these prices are represented by the [Price data module](https://docs.medusajs.com/references/pricing/models/Price/index.html.md).
-1. They may sell physical or digital items.
-2. They may sell items that don't require shipping or inventory management, such as selling digital products, services, or booking appointments.
-3. They may sell items whose inventory is managed by an external system, such as an ERP.
-
-Medusa supports these different selling requirements by allowing you to configure shipping and inventory requirements for products and their variants. This guide explains how these configurations work, then provides examples of setting up different use cases.
+
***
-## Configuring Shipping Requirements
+## Price List
-The Medusa application defines a link between the `Product` data model and a [ShippingProfile](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/concepts#shipping-profile/index.html.md) in the [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md), allowing you to associate a product with a shipping profile.
+A [PriceList](https://docs.medusajs.com/references/pricing/models/PriceList/index.html.md) is a group of prices only enabled if their conditions and rules are satisfied.
-When a product is associated with a shipping profile, its variants require shipping and fulfillment when purchased. This is useful for physical products or digital products that require custom fulfillment.
+A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied.
-If a product doesn't have an associated shipping profile, its variants don't require shipping and fulfillment when purchased. This is useful for digital products, for example, that don't require shipping.
+Its associated prices are represented by the `Price` data model.
-### Overriding Shipping Requirements for Variants
-A product variant whose inventory is managed by Medusa (its `manage_inventory` property is enabled) has an [inventory item](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventoryitem/index.html.md). The inventory item has a `requires_shipping` property that can be used to override its shipping requirement. This is useful if the product has an associated shipping profile but you want to disable shipping for a specific variant, or vice versa.
+# Links between Pricing Module and Other Modules
-Learn more about product variant's inventory in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory/index.html.md).
+This document showcases the module links defined between the Pricing Module and other Commerce Modules.
-When a product variant is purchased, the Medusa application decides whether the purchased item requires shipping in the following order:
+## Summary
-1. The product variant has an inventory item. In this case, the Medusa application uses the inventory item's `requires_shipping` property to determine if the item requires shipping.
-2. If the product variant doesn't have an inventory item, the Medusa application checks whether the product has an associated shipping profile to determine if the item requires shipping.
+The Pricing Module has the following links to other modules:
+
+|First Data Model|Second Data Model|Type|Description|
+|---|---|---|---|
+| in ||Stored||
+| in ||Stored||
***
-## Use Case Examples
+## Fulfillment Module
-By combining configurations of shipment requirements and inventory management, you can set up your products to support your use case:
+The Fulfillment Module provides fulfillment-related functionalities, including shipping options that the customer chooses from when they place their order. However, it doesn't provide pricing-related functionalities for these options.
-|Use Case|Configurations|Example|
-|---|---|---|---|---|
-|Item that's shipped on purchase, and its variant inventory is managed by the Medusa application.||Any stock-kept item (clothing, for example), whose inventory is managed in the Medusa application.|
-|Item that's shipped on purchase, but its variant inventory is managed externally (not by Medusa) or it has infinite stock.||Any stock-kept item (clothing, for example), whose inventory is managed in an ERP or has infinite stock.|
-|Item that's not shipped on purchase, but its variant inventory is managed by Medusa.||Digital products, such as licenses, that don't require shipping but have a limited quantity.|
-|Item that doesn't require shipping and its variant inventory isn't managed by Medusa.|||
+Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set.
+
-# Application Method
+### Retrieve with Query
-In this document, you'll learn what an application method is.
+To retrieve the shipping option of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `shipping_option.*` in `fields`:
-## What is an Application Method?
+### query.graph
-The [ApplicationMethod data model](https://docs.medusajs.com/references/promotion/models/ApplicationMethod/index.html.md) defines how a promotion is applied:
+```ts
+const { data: priceSets } = await query.graph({
+ entity: "price_set",
+ fields: [
+ "shipping_option.*",
+ ],
+})
-|Property|Purpose|
-|---|---|
-|\`type\`|Does the promotion discount a fixed amount or a percentage?|
-|\`target\_type\`|Is the promotion applied on a cart item, shipping method, or the entire order?|
-|\`allocation\`|Is the discounted amount applied on each item or split between the applicable items?|
+// priceSets.shipping_option
+```
-## Target Promotion Rules
+### useQueryGraphStep
-When the promotion is applied to a cart item or a shipping method, you can restrict which items/shipping methods the promotion is applied to.
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-The `ApplicationMethod` data model has a collection of `PromotionRule` records to restrict which items or shipping methods the promotion applies to. The `target_rules` property represents this relation.
+// ...
-
+const { data: priceSets } = useQueryGraphStep({
+ entity: "price_set",
+ fields: [
+ "shipping_option.*",
+ ],
+})
-In this example, the promotion is only applied on products in the cart having the SKU `SHIRT`.
+// priceSets.shipping_option
+```
+
+### Manage with Link
+
+To manage the price set of a shipping option, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
+
+### link.create
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+
+// ...
+
+await link.create({
+ [Modules.FULFILLMENT]: {
+ shipping_option_id: "so_123",
+ },
+ [Modules.PRICING]: {
+ price_set_id: "pset_123",
+ },
+})
+```
+
+### createRemoteLinkStep
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+createRemoteLinkStep({
+ [Modules.FULFILLMENT]: {
+ shipping_option_id: "so_123",
+ },
+ [Modules.PRICING]: {
+ price_set_id: "pset_123",
+ },
+})
+```
***
-## Buy Promotion Rules
+## Product Module
-When the promotion’s type is `buyget`, you must specify the “buy X” condition. For example, a cart must have two shirts before the promotion can be applied.
+The Product Module doesn't store or manage the prices of product variants.
-The application method has a collection of `PromotionRule` items to define the “buy X” rule. The `buy_rules` property represents this relation.
+Medusa defines a link between the `ProductVariant` and the `PriceSet`. A product variant’s prices are stored as prices belonging to a price set.
-
+
-In this example, the cart must have two products with the SKU `SHIRT` for the promotion to be applied.
+So, when you want to add prices for a product variant, you create a price set and add the prices to it.
+
+You can then benefit from adding rules to prices or using the `calculatePrices` method to retrieve the price of a product variant within a specified context.
+
+### Retrieve with Query
+
+To retrieve the variant of a price set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: priceSets } = await query.graph({
+ entity: "price_set",
+ fields: [
+ "variant.*",
+ ],
+})
+
+// priceSets.variant
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: priceSets } = useQueryGraphStep({
+ entity: "price_set",
+ fields: [
+ "variant.*",
+ ],
+})
+
+// priceSets.variant
+```
+
+### Manage with Link
+
+To manage the price set of a variant, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
+
+### link.create
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+
+// ...
+
+await link.create({
+ [Modules.PRODUCT]: {
+ variant_id: "variant_123",
+ },
+ [Modules.PRICING]: {
+ price_set_id: "pset_123",
+ },
+})
+```
+
+### createRemoteLinkStep
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+createRemoteLinkStep({
+ [Modules.PRODUCT]: {
+ variant_id: "variant_123",
+ },
+ [Modules.PRICING]: {
+ price_set_id: "pset_123",
+ },
+})
+```
+
+
+# Prices Calculation
+
+In this document, you'll learn how prices are calculated when you use the [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) of the Pricing Module's main service.
+
+## calculatePrices Method
+
+The [calculatePrices method](https://docs.medusajs.com/references/pricing/calculatePrices/index.html.md) accepts as parameters the ID of one or more price sets and a context.
+
+It returns a price object with the best matching price for each price set.
+
+### Calculation Context
+
+The calculation context is an optional object passed as a second parameter to the `calculatePrices` method. It accepts rules to restrict the selected prices in the price set.
+
+For example:
+
+```ts
+const price = await pricingModuleService.calculatePrices(
+ { id: [priceSetId] },
+ {
+ context: {
+ currency_code: currencyCode,
+ region_id: "reg_123",
+ },
+ }
+)
+```
+
+In this example, you retrieve the prices in a price set for the specified currency code and region ID.
+
+### Returned Price Object
+
+For each price set, the `calculatePrices` method selects two prices:
+
+- A calculated price: Either a price that belongs to a price list and best matches the specified context, or the same as the original price.
+- An original price, which is either:
+ - The same price as the calculated price if the price list it belongs to is of type `override`;
+ - Or a price that doesn't belong to a price list and best matches the specified context.
+
+Both prices are returned in an object that has the following properties:
+
+- id: (\`string\`) The ID of the price set from which the price was selected.
+- is\_calculated\_price\_price\_list: (\`boolean\`) Whether the calculated price belongs to a price list.
+- calculated\_amount: (\`number\`) The amount of the calculated price, or \`null\` if there isn't a calculated price. This is the amount shown to the customer.
+- is\_original\_price\_price\_list: (\`boolean\`) Whether the original price belongs to a price list.
+- original\_amount: (\`number\`) The amount of the original price, or \`null\` if there isn't an original price. This amount is useful to compare with the \`calculated\_amount\`, such as to check for discounted value.
+- currency\_code: (\`string\`) The currency code of the calculated price, or \`null\` if there isn't a calculated price.
+- is\_calculated\_price\_tax\_inclusive: (\`boolean\`) Whether the calculated price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx)
+- is\_original\_price\_tax\_inclusive: (\`boolean\`) Whether the original price is tax inclusive. Learn more about tax-inclusivity in \[this document]\(../tax-inclusive-pricing/page.mdx)
+- calculated\_price: (\`object\`) The calculated price's price details.
+
+ - id: (\`string\`) The ID of the price.
+
+ - price\_list\_id: (\`string\`) The ID of the associated price list.
+
+ - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`.
+
+ - min\_quantity: (\`number\`) The price's min quantity condition.
+
+ - max\_quantity: (\`number\`) The price's max quantity condition.
+- original\_price: (\`object\`) The original price's price details.
+
+ - id: (\`string\`) The ID of the price.
+
+ - price\_list\_id: (\`string\`) The ID of the associated price list.
+
+ - price\_list\_type: (\`string\`) The price list's type. For example, \`sale\`.
+
+ - min\_quantity: (\`number\`) The price's min quantity condition.
+
+ - max\_quantity: (\`number\`) The price's max quantity condition.
+
+***
+
+## Examples
+
+Consider the following price set:
+
+```ts
+const priceSet = await pricingModuleService.createPriceSets({
+ prices: [
+ // default price
+ {
+ amount: 500,
+ currency_code: "EUR",
+ rules: {},
+ },
+ // prices with rules
+ {
+ amount: 400,
+ currency_code: "EUR",
+ rules: {
+ region_id: "reg_123",
+ },
+ },
+ {
+ amount: 450,
+ currency_code: "EUR",
+ rules: {
+ city: "krakow",
+ },
+ },
+ {
+ amount: 500,
+ currency_code: "EUR",
+ rules: {
+ city: "warsaw",
+ region_id: "reg_123",
+ },
+ },
+ ],
+})
+```
+
+### Default Price Selection
+
+### Code
+
+```ts
+const price = await pricingModuleService.calculatePrices(
+ { id: [priceSet.id] },
+ {
+ context: {
+ currency_code: "EUR"
+ }
+ }
+)
+```
+
+### Result
+
+### Calculate Prices with Rules
+
+### Code
+
+```ts
+const price = await pricingModuleService.calculatePrices(
+ { id: [priceSet.id] },
+ {
+ context: {
+ currency_code: "EUR",
+ region_id: "reg_123",
+ city: "krakow"
+ }
+ }
+)
+```
+
+### Result
+
+### Price Selection with Price List
+
+### Code
+
+```ts
+const priceList = pricingModuleService.createPriceLists([{
+ title: "Summer Price List",
+ description: "Price list for summer sale",
+ starts_at: Date.parse("01/10/2023").toString(),
+ ends_at: Date.parse("31/10/2023").toString(),
+ rules: {
+ region_id: ['PL']
+ },
+ type: "sale",
+ prices: [
+ {
+ amount: 400,
+ currency_code: "EUR",
+ price_set_id: priceSet.id,
+ },
+ {
+ amount: 450,
+ currency_code: "EUR",
+ price_set_id: priceSet.id,
+ },
+ ],
+}]);
+
+const price = await pricingModuleService.calculatePrices(
+ { id: [priceSet.id] },
+ {
+ context: {
+ currency_code: "EUR",
+ region_id: "PL",
+ city: "krakow"
+ }
+ }
+)
+```
+
+### Result
+
+
+# Price Rules
+
+In this document, you'll learn about price rules for price sets and price lists.
+
+## Price Rule
+
+You can restrict prices by rules. Each rule of a price is represented by the [PriceRule data model](https://docs.medusajs.com/references/pricing/models/PriceRule/index.html.md).
+
+The `Price` data model has a `rules_count` property, which indicates how many rules, represented by `PriceRule`, are applied to the price.
+
+For exmaple, you create a price restricted to `10557` zip codes.
+
+
+
+A price can have multiple price rules.
+
+For example, a price can be restricted by a region and a zip code.
+
+
+
+***
+
+## Price List Rules
+
+Rules applied to a price list are represented by the [PriceListRule data model](https://docs.medusajs.com/references/pricing/models/PriceListRule/index.html.md).
+
+The `rules_count` property of a `PriceList` indicates how many rules are applied to it.
+
+
+
+
+# Tax-Inclusive Pricing
+
+In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices.
+
+## What is Tax-Inclusive Pricing?
+
+A tax-inclusive price is a price of a resource that includes taxes. Medusa calculates the tax amount from the price rather than adds the amount to it.
+
+For example, if a product’s price is $50, the tax rate is 2%, and tax-inclusive pricing is enabled, then the product's price is $49, and the applied tax amount is $1.
+
+***
+
+## How is Tax-Inclusive Pricing Set?
+
+The [PricePreference data model](https://docs.medusajs.com/references/pricing/models/PricePreference/index.html.md) holds the tax-inclusive setting for a context. It has two properties that indicate the context:
+
+- `attribute`: The name of the attribute to compare against. For example, `region_id` or `currency_code`.
+- `value`: The attribute’s value. For example, `reg_123` or `usd`.
+
+Only `region_id` and `currency_code` are supported as an `attribute` at the moment.
+
+The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context.
+
+For example:
+
+```json
+{
+ "attribute": "currency_code",
+ "value": "USD",
+ "is_tax_inclusive": true,
+}
+```
+
+In this example, tax-inclusivity is enabled for the `USD` currency code.
+
+***
+
+## Tax-Inclusive Pricing in Price Calculation
+
+### Tax Context
+
+As mentioned in the [Price Calculation documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), The `calculatePrices` method accepts as a parameter a calculation context.
+
+To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context.
+
+### Returned Tax Properties
+
+The `calculatePrices` method returns two properties related to tax-inclusivity:
+
+Learn more about the returned properties in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md).
+
+- `is_calculated_price_tax_inclusive`: Whether the selected `calculated_price` is tax-inclusive.
+- `is_original_price_tax_inclusive` : Whether the selected `original_price` is tax-inclusive.
+
+A price is considered tax-inclusive if:
+
+1. It belongs to the region or currency code specified in the calculation context;
+2. and the region or currency code has a price preference with `is_tax_inclusive` enabled.
+
+### Tax Context Precedence
+
+A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if:
+
+- both the `region_id` and `currency_code` are provided in the calculation context;
+- the selected price belongs to the region;
+- and the region has a price preference
# Promotion Actions
@@ -27346,32 +27310,6 @@ export interface CampaignBudgetExceededAction {
Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.CampaignBudgetExceededAction/index.html.md) for details on the object’s properties.
-# Campaign
-
-In this document, you'll learn about campaigns.
-
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/campaigns/index.html.md) to learn how to manage campaigns using the dashboard.
-
-## What is a Campaign?
-
-A [Campaign](https://docs.medusajs.com/references/promotion/models/Campaign/index.html.md) combines promotions under the same conditions, such as start and end dates.
-
-
-
-***
-
-## Campaign Limits
-
-Each campaign has a budget represented by the [CampaignBudget data model](https://docs.medusajs.com/references/promotion/models/CampaignBudget/index.html.md). The budget limits how many times the promotion can be used.
-
-There are two types of budgets:
-
-- `spend`: An amount that, when crossed, the promotion becomes unusable. For example, if the amount limit is set to `$100`, and the total amount of usage of this promotion crosses that threshold, the promotion can no longer be applied.
-- `usage`: The number of times that a promotion can be used. For example, if the usage limit is set to `10`, the promotion can be used only 10 times by customers. After that, it can no longer be applied.
-
-
-
-
# Promotion Concepts
In this document, you’ll learn about the main promotion and rule concepts in the Promotion Module.
@@ -27429,9 +27367,72 @@ For example, to restrict the promotion to only `VIP` and `B2B` customer groups:
In this case, a customer’s group must be in the `VIP` and `B2B` set of values to use the promotion.
+# Application Method
+
+In this document, you'll learn what an application method is.
+
+## What is an Application Method?
+
+The [ApplicationMethod data model](https://docs.medusajs.com/references/promotion/models/ApplicationMethod/index.html.md) defines how a promotion is applied:
+
+|Property|Purpose|
+|---|---|
+|\`type\`|Does the promotion discount a fixed amount or a percentage?|
+|\`target\_type\`|Is the promotion applied on a cart item, shipping method, or the entire order?|
+|\`allocation\`|Is the discounted amount applied on each item or split between the applicable items?|
+
+## Target Promotion Rules
+
+When the promotion is applied to a cart item or a shipping method, you can restrict which items/shipping methods the promotion is applied to.
+
+The `ApplicationMethod` data model has a collection of `PromotionRule` records to restrict which items or shipping methods the promotion applies to. The `target_rules` property represents this relation.
+
+
+
+In this example, the promotion is only applied on products in the cart having the SKU `SHIRT`.
+
+***
+
+## Buy Promotion Rules
+
+When the promotion’s type is `buyget`, you must specify the “buy X” condition. For example, a cart must have two shirts before the promotion can be applied.
+
+The application method has a collection of `PromotionRule` items to define the “buy X” rule. The `buy_rules` property represents this relation.
+
+
+
+In this example, the cart must have two products with the SKU `SHIRT` for the promotion to be applied.
+
+
+# Campaign
+
+In this document, you'll learn about campaigns.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/promotions/campaigns/index.html.md) to learn how to manage campaigns using the dashboard.
+
+## What is a Campaign?
+
+A [Campaign](https://docs.medusajs.com/references/promotion/models/Campaign/index.html.md) combines promotions under the same conditions, such as start and end dates.
+
+
+
+***
+
+## Campaign Limits
+
+Each campaign has a budget represented by the [CampaignBudget data model](https://docs.medusajs.com/references/promotion/models/CampaignBudget/index.html.md). The budget limits how many times the promotion can be used.
+
+There are two types of budgets:
+
+- `spend`: An amount that, when crossed, the promotion becomes unusable. For example, if the amount limit is set to `$100`, and the total amount of usage of this promotion crosses that threshold, the promotion can no longer be applied.
+- `usage`: The number of times that a promotion can be used. For example, if the usage limit is set to `10`, the promotion can be used only 10 times by customers. After that, it can no longer be applied.
+
+
+
+
# Links between Promotion Module and Other Modules
-This document showcases the module links defined between the Promotion Module and other commerce modules.
+This document showcases the module links defined between the Promotion Module and other Commerce Modules.
## Summary
@@ -27612,6 +27613,416 @@ createRemoteLinkStep({
```
+# Links between Region Module and Other Modules
+
+This document showcases the module links defined between the Region Module and other Commerce Modules.
+
+## Summary
+
+The Region Module has the following links to other modules:
+
+Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
+
+|First Data Model|Second Data Model|Type|Description|
+|---|---|---|---|
+| in ||Read-only||
+| in ||Read-only||
+|| in |Stored||
+
+***
+
+## Cart Module
+
+Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Region` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the region of a cart, and not the other way around.
+
+### Retrieve with Query
+
+To retrieve the region of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: carts } = await query.graph({
+ entity: "cart",
+ fields: [
+ "region.*",
+ ],
+})
+
+// carts.region
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: carts } = useQueryGraphStep({
+ entity: "cart",
+ fields: [
+ "region.*",
+ ],
+})
+
+// carts.region
+```
+
+***
+
+## Order Module
+
+Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Region` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the region of an order, and not the other way around.
+
+### Retrieve with Query
+
+To retrieve the region of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: orders } = await query.graph({
+ entity: "order",
+ fields: [
+ "region.*",
+ ],
+})
+
+// orders.region
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: orders } = useQueryGraphStep({
+ entity: "order",
+ fields: [
+ "region.*",
+ ],
+})
+
+// orders.region
+```
+
+***
+
+## Payment Module
+
+You can specify for each region which payment providers are available for use.
+
+Medusa defines a module link between the `PaymentProvider` and the `Region` data models.
+
+
+
+### Retrieve with Query
+
+To retrieve the payment providers of a region with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_providers.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: regions } = await query.graph({
+ entity: "region",
+ fields: [
+ "payment_providers.*",
+ ],
+})
+
+// regions.payment_providers
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: regions } = useQueryGraphStep({
+ entity: "region",
+ fields: [
+ "payment_providers.*",
+ ],
+})
+
+// regions.payment_providers
+```
+
+### Manage with Link
+
+To manage the payment providers in a region, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
+
+### link.create
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+
+// ...
+
+await link.create({
+ [Modules.REGION]: {
+ region_id: "reg_123",
+ },
+ [Modules.PAYMENT]: {
+ payment_provider_id: "pp_stripe_stripe",
+ },
+})
+```
+
+### createRemoteLinkStep
+
+```ts
+import { Modules } from "@medusajs/framework/utils"
+import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+createRemoteLinkStep({
+ [Modules.REGION]: {
+ region_id: "reg_123",
+ },
+ [Modules.PAYMENT]: {
+ payment_provider_id: "pp_stripe_stripe",
+ },
+})
+```
+
+
+# Links between Store Module and Other Modules
+
+This document showcases the module links defined between the Store Module and other Commerce Modules.
+
+## Summary
+
+The Store Module has the following links to other modules:
+
+Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
+
+|First Data Model|Second Data Model|Type|Description|
+|---|---|---|---|
+|| in |Read-only||
+
+***
+
+## Currency Module
+
+The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol.
+
+Instead, Medusa defines a read-only link between the [Currency Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/index.html.md)'s `Currency` data model and the Store Module's `Currency` data model. This means you can retrieve the details of a store's supported currencies, but you don't manage the links in a pivot table in the database. The currencies of a store are determined by the `currency_code` of the [Currency](https://docs.medusajs.com/references/store/models/Currency/index.html.md) data model in the Store Module (not in the Currency Module).
+
+### Retrieve with Query
+
+To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`:
+
+### query.graph
+
+```ts
+const { data: stores } = await query.graph({
+ entity: "store",
+ fields: [
+ "supported_currencies.currency.*",
+ ],
+})
+
+// stores.supported_currencies
+```
+
+### useQueryGraphStep
+
+```ts
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+
+// ...
+
+const { data: stores } = useQueryGraphStep({
+ entity: "store",
+ fields: [
+ "supported_currencies.currency.*",
+ ],
+})
+
+// stores.supported_currencies
+```
+
+
+# Tax Module Options
+
+In this document, you'll learn about the options of the Tax Module.
+
+## providers
+
+The `providers` option is an array of either tax module providers or path to a file that defines a tax provider.
+
+When the Medusa application starts, these providers are registered and can be used to retrieve tax lines.
+
+```ts title="medusa-config.ts"
+import { Modules } from "@medusajs/framework/utils"
+
+// ...
+
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/tax",
+ options: {
+ providers: [
+ {
+ resolve: "./path/to/my-provider",
+ id: "my-provider",
+ options: {
+ // ...
+ },
+ },
+ ],
+ },
+ },
+ ],
+})
+```
+
+The objects in the array accept the following properties:
+
+- `resolve`: A string indicating the package name of the module provider or the path to it.
+- `id`: A string indicating the provider's unique name or ID.
+- `options`: An optional object of the module provider's options.
+
+
+# Tax Calculation with the Tax Provider
+
+In this document, you’ll learn how tax lines are calculated and what a tax provider is.
+
+## Tax Lines Calculation
+
+Tax lines are calculated and retrieved using the [getTaxLines method of the Tax Module’s main service](https://docs.medusajs.com/references/tax/getTaxLines/index.html.md). It accepts an array of line items and shipping methods, and the context of the calculation.
+
+For example:
+
+```ts
+const taxLines = await taxModuleService.getTaxLines(
+ [
+ {
+ id: "cali_123",
+ product_id: "prod_123",
+ unit_price: 1000,
+ quantity: 1,
+ },
+ {
+ id: "casm_123",
+ shipping_option_id: "so_123",
+ unit_price: 2000,
+ },
+ ],
+ {
+ address: {
+ country_code: "us",
+ },
+ }
+)
+```
+
+The context object is used to determine which tax regions and rates to use in the calculation. It includes properties related to the address and customer.
+
+The example above retrieves the tax lines based on the tax region for the United States.
+
+The method returns tax lines for the line item and shipping methods. For example:
+
+```json
+[
+ {
+ "line_item_id": "cali_123",
+ "rate_id": "txr_1",
+ "rate": 10,
+ "code": "XXX",
+ "name": "Tax Rate 1"
+ },
+ {
+ "shipping_line_id": "casm_123",
+ "rate_id": "txr_2",
+ "rate": 5,
+ "code": "YYY",
+ "name": "Tax Rate 2"
+ }
+]
+```
+
+***
+
+## Using the Tax Provider in the Calculation
+
+The tax lines retrieved by the `getTaxLines` method are actually retrieved from the tax region’s provider.
+
+A tax module provider whose main service implements the logic to shape tax lines. Each tax region has a tax provider.
+
+The Tax Module provides a `system` tax provider that only transforms calculated item and shipping tax rates into the required return type.
+
+{/* ---
+
+TODO add once tax provider guide is updated + add module providers match other modules.
+
+## Create Tax Provider
+
+Refer to [this guide](/modules/tax/provider) to learn more about creating a tax provider. */}
+
+
+# Tax Rates and Rules
+
+In this document, you’ll learn about tax rates and rules.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions#manage-tax-rate-overrides/index.html.md) to learn how to manage tax rates using the dashboard.
+
+## What are Tax Rates?
+
+A tax rate is a percentage amount used to calculate the tax amount for each taxable item’s price, such as line items or shipping methods, in a cart. The sum of all calculated tax amounts are then added to the cart’s total as a tax total.
+
+Each tax region has a default tax rate. This tax rate is applied to all taxable items of a cart in that region.
+
+### Combinable Tax Rates
+
+Tax regions can have parent tax regions. To inherit the tax rates of the parent tax region, set the `is_combinable` of the child’s tax rates to `true`.
+
+Then, when tax rates are retrieved for a taxable item in the child region, both the child and the parent tax regions’ applicable rates are returned.
+
+***
+
+## Override Tax Rates with Rules
+
+You can create tax rates that override the default for specific conditions or rules.
+
+For example, you can have a default tax rate is 10%, but for products of type “Shirt” is %15.
+
+A tax region can have multiple tax rates, and each tax rate can have multiple tax rules. The [TaxRateRule data model](https://docs.medusajs.com/references/tax/models/TaxRateRule/index.html.md) represents a tax rate’s rule.
+
+
+
+These two properties of the data model identify the rule’s target:
+
+- `reference`: the name of the table in the database that this rule points to. For example, `product_type`.
+- `reference_id`: the ID of the data model’s record that this points to. For example, a product type’s ID.
+
+So, to override the default tax rate for product types “Shirt”, you create a tax rate and associate with it a tax rule whose `reference` is `product_type` and `reference_id` the ID of the “Shirt” product type.
+
+
+# Tax Region
+
+In this document, you’ll learn about tax regions and how to use them with the Region Module.
+
+Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard.
+
+## What is a Tax Region?
+
+A tax region, represented by the [TaxRegion data model](https://docs.medusajs.com/references/tax/models/TaxRegion/index.html.md), stores tax settings related to a region that your store serves.
+
+Tax regions can inherit settings and rules from a parent tax region.
+
+Each tax region has tax rules and a tax provider.
+
+
# Stock Location Concepts
In this document, you’ll learn about the main concepts in the Stock Location Module.
@@ -27631,7 +28042,7 @@ The `StockLocationAddress` data model belongs to the `StockLocation` data model.
# Links between Stock Location Module and Other Modules
-This document showcases the module links defined between the Stock Location Module and other commerce modules.
+This document showcases the module links defined between the Stock Location Module and other Commerce Modules.
## Summary
@@ -27859,189 +28270,9 @@ createRemoteLinkStep({
```
-# Links between Region Module and Other Modules
-
-This document showcases the module links defined between the Region Module and other commerce modules.
-
-## Summary
-
-The Region Module has the following links to other modules:
-
-Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
-
-|First Data Model|Second Data Model|Type|Description|
-|---|---|---|---|
-| in ||Read-only||
-| in ||Read-only||
-|| in |Stored||
-
-***
-
-## Cart Module
-
-Medusa defines a read-only link between the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model and the `Region` data model. Because the link is read-only from the `Cart`'s side, you can only retrieve the region of a cart, and not the other way around.
-
-### Retrieve with Query
-
-To retrieve the region of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: carts } = await query.graph({
- entity: "cart",
- fields: [
- "region.*",
- ],
-})
-
-// carts.region
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: carts } = useQueryGraphStep({
- entity: "cart",
- fields: [
- "region.*",
- ],
-})
-
-// carts.region
-```
-
-***
-
-## Order Module
-
-Medusa defines a read-only link between the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model and the `Region` data model. Because the link is read-only from the `Order`'s side, you can only retrieve the region of an order, and not the other way around.
-
-### Retrieve with Query
-
-To retrieve the region of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `region.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: orders } = await query.graph({
- entity: "order",
- fields: [
- "region.*",
- ],
-})
-
-// orders.region
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: orders } = useQueryGraphStep({
- entity: "order",
- fields: [
- "region.*",
- ],
-})
-
-// orders.region
-```
-
-***
-
-## Payment Module
-
-You can specify for each region which payment providers are available for use.
-
-Medusa defines a module link between the `PaymentProvider` and the `Region` data models.
-
-
-
-### Retrieve with Query
-
-To retrieve the payment providers of a region with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_providers.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: regions } = await query.graph({
- entity: "region",
- fields: [
- "payment_providers.*",
- ],
-})
-
-// regions.payment_providers
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-const { data: regions } = useQueryGraphStep({
- entity: "region",
- fields: [
- "payment_providers.*",
- ],
-})
-
-// regions.payment_providers
-```
-
-### Manage with Link
-
-To manage the payment providers in a region, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md):
-
-### link.create
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-await link.create({
- [Modules.REGION]: {
- region_id: "reg_123",
- },
- [Modules.PAYMENT]: {
- payment_provider_id: "pp_stripe_stripe",
- },
-})
-```
-
-### createRemoteLinkStep
-
-```ts
-import { Modules } from "@medusajs/framework/utils"
-import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
-
-// ...
-
-createRemoteLinkStep({
- [Modules.REGION]: {
- region_id: "reg_123",
- },
- [Modules.PAYMENT]: {
- payment_provider_id: "pp_stripe_stripe",
- },
-})
-```
-
-
# Links between Sales Channel Module and Other Modules
-This document showcases the module links defined between the Sales Channel Module and other commerce modules.
+This document showcases the module links defined between the Sales Channel Module and other Commerce Modules.
## Summary
@@ -28411,60 +28642,40 @@ The Medusa application infers the associated sales channels and ensures that onl
To create a publishable API key, either use the [Medusa Admin](https://docs.medusajs.com/user-guide/settings/developer/publishable-api-keys/index.html.md) or the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys).
-# Links between Store Module and Other Modules
+# User Module Options
-This document showcases the module links defined between the Store Module and other commerce modules.
+In this document, you'll learn about the options of the User Module.
-## Summary
+## Module Options
-The Store Module has the following links to other modules:
-
-Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database.
-
-|First Data Model|Second Data Model|Type|Description|
-|---|---|---|---|
-|| in |Read-only||
-
-***
-
-## Currency Module
-
-The Store Module has a `Currency` data model that stores the supported currencies of a store. However, these currencies don't hold all the details of a currency, such as its name or symbol.
-
-Instead, Medusa defines a read-only link between the [Currency Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/currency/index.html.md)'s `Currency` data model and the Store Module's `Currency` data model. This means you can retrieve the details of a store's supported currencies, but you don't manage the links in a pivot table in the database. The currencies of a store are determined by the `currency_code` of the [Currency](https://docs.medusajs.com/references/store/models/Currency/index.html.md) data model in the Store Module (not in the Currency Module).
-
-### Retrieve with Query
-
-To retrieve the details of a store's currencies with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `supported_currencies.currency.*` in `fields`:
-
-### query.graph
-
-```ts
-const { data: stores } = await query.graph({
- entity: "store",
- fields: [
- "supported_currencies.currency.*",
- ],
-})
-
-// stores.supported_currencies
-```
-
-### useQueryGraphStep
-
-```ts
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+```ts title="medusa-config.ts"
+import { Modules } from "@medusajs/framework/utils"
// ...
-const { data: stores } = useQueryGraphStep({
- entity: "store",
- fields: [
- "supported_currencies.currency.*",
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "@medusajs/user",
+ options: {
+ jwt_secret: process.env.JWT_SECRET,
+ },
+ },
],
})
+```
-// stores.supported_currencies
+|Option|Description|Required|
+|---|---|---|---|---|
+|\`jwt\_secret\`|A string indicating the secret used to sign the invite tokens.|Yes|
+
+### Environment Variables
+
+Make sure to add the necessary environment variables for the above options in `.env`:
+
+```bash
+JWT_SECRET=supersecret
```
@@ -28548,216 +28759,6 @@ if (!count) {
```
-# User Module Options
-
-In this document, you'll learn about the options of the User Module.
-
-## Module Options
-
-```ts title="medusa-config.ts"
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/user",
- options: {
- jwt_secret: process.env.JWT_SECRET,
- },
- },
- ],
-})
-```
-
-|Option|Description|Required|
-|---|---|---|---|---|
-|\`jwt\_secret\`|A string indicating the secret used to sign the invite tokens.|Yes|
-
-### Environment Variables
-
-Make sure to add the necessary environment variables for the above options in `.env`:
-
-```bash
-JWT_SECRET=supersecret
-```
-
-
-# Tax Rates and Rules
-
-In this document, you’ll learn about tax rates and rules.
-
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions#manage-tax-rate-overrides/index.html.md) to learn how to manage tax rates using the dashboard.
-
-## What are Tax Rates?
-
-A tax rate is a percentage amount used to calculate the tax amount for each taxable item’s price, such as line items or shipping methods, in a cart. The sum of all calculated tax amounts are then added to the cart’s total as a tax total.
-
-Each tax region has a default tax rate. This tax rate is applied to all taxable items of a cart in that region.
-
-### Combinable Tax Rates
-
-Tax regions can have parent tax regions. To inherit the tax rates of the parent tax region, set the `is_combinable` of the child’s tax rates to `true`.
-
-Then, when tax rates are retrieved for a taxable item in the child region, both the child and the parent tax regions’ applicable rates are returned.
-
-***
-
-## Override Tax Rates with Rules
-
-You can create tax rates that override the default for specific conditions or rules.
-
-For example, you can have a default tax rate is 10%, but for products of type “Shirt” is %15.
-
-A tax region can have multiple tax rates, and each tax rate can have multiple tax rules. The [TaxRateRule data model](https://docs.medusajs.com/references/tax/models/TaxRateRule/index.html.md) represents a tax rate’s rule.
-
-
-
-These two properties of the data model identify the rule’s target:
-
-- `reference`: the name of the table in the database that this rule points to. For example, `product_type`.
-- `reference_id`: the ID of the data model’s record that this points to. For example, a product type’s ID.
-
-So, to override the default tax rate for product types “Shirt”, you create a tax rate and associate with it a tax rule whose `reference` is `product_type` and `reference_id` the ID of the “Shirt” product type.
-
-
-# Tax Module Options
-
-In this document, you'll learn about the options of the Tax Module.
-
-## providers
-
-The `providers` option is an array of either tax module providers or path to a file that defines a tax provider.
-
-When the Medusa application starts, these providers are registered and can be used to retrieve tax lines.
-
-```ts title="medusa-config.ts"
-import { Modules } from "@medusajs/framework/utils"
-
-// ...
-
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "@medusajs/tax",
- options: {
- providers: [
- {
- resolve: "./path/to/my-provider",
- id: "my-provider",
- options: {
- // ...
- },
- },
- ],
- },
- },
- ],
-})
-```
-
-The objects in the array accept the following properties:
-
-- `resolve`: A string indicating the package name of the module provider or the path to it.
-- `id`: A string indicating the provider's unique name or ID.
-- `options`: An optional object of the module provider's options.
-
-
-# Tax Region
-
-In this document, you’ll learn about tax regions and how to use them with the Region Module.
-
-Refer to this [Medusa Admin User Guide](https://docs.medusajs.com/user-guide/settings/tax-regions/index.html.md) to learn how to manage tax regions using the dashboard.
-
-## What is a Tax Region?
-
-A tax region, represented by the [TaxRegion data model](https://docs.medusajs.com/references/tax/models/TaxRegion/index.html.md), stores tax settings related to a region that your store serves.
-
-Tax regions can inherit settings and rules from a parent tax region.
-
-Each tax region has tax rules and a tax provider.
-
-
-# Tax Calculation with the Tax Provider
-
-In this document, you’ll learn how tax lines are calculated and what a tax provider is.
-
-## Tax Lines Calculation
-
-Tax lines are calculated and retrieved using the [getTaxLines method of the Tax Module’s main service](https://docs.medusajs.com/references/tax/getTaxLines/index.html.md). It accepts an array of line items and shipping methods, and the context of the calculation.
-
-For example:
-
-```ts
-const taxLines = await taxModuleService.getTaxLines(
- [
- {
- id: "cali_123",
- product_id: "prod_123",
- unit_price: 1000,
- quantity: 1,
- },
- {
- id: "casm_123",
- shipping_option_id: "so_123",
- unit_price: 2000,
- },
- ],
- {
- address: {
- country_code: "us",
- },
- }
-)
-```
-
-The context object is used to determine which tax regions and rates to use in the calculation. It includes properties related to the address and customer.
-
-The example above retrieves the tax lines based on the tax region for the United States.
-
-The method returns tax lines for the line item and shipping methods. For example:
-
-```json
-[
- {
- "line_item_id": "cali_123",
- "rate_id": "txr_1",
- "rate": 10,
- "code": "XXX",
- "name": "Tax Rate 1"
- },
- {
- "shipping_line_id": "casm_123",
- "rate_id": "txr_2",
- "rate": 5,
- "code": "YYY",
- "name": "Tax Rate 2"
- }
-]
-```
-
-***
-
-## Using the Tax Provider in the Calculation
-
-The tax lines retrieved by the `getTaxLines` method are actually retrieved from the tax region’s provider.
-
-A tax module provider whose main service implements the logic to shape tax lines. Each tax region has a tax provider.
-
-The Tax Module provides a `system` tax provider that only transforms calculated item and shipping tax rates into the required return type.
-
-{/* ---
-
-TODO add once tax provider guide is updated + add module providers match other modules.
-
-## Create Tax Provider
-
-Refer to [this guide](/modules/tax/provider) to learn more about creating a tax provider. */}
-
-
# Emailpass Auth Module Provider
In this document, you’ll learn about the Emailpass auth module provider and how to install and use it in the Auth Module.
@@ -28989,6 +28990,86 @@ The [Authenticate or Login API Route](https://docs.medusajs.com/Users/shahednass
- [How to implement Google social login in the storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/third-party-login/index.html.md).
+# Get Product Variant Prices using Query
+
+In this document, you'll learn how to retrieve product variant prices in the Medusa application using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md).
+
+The Product Module doesn't provide pricing functionalities. The Medusa application links the Product Module's `ProductVariant` data model to the Pricing Module's `PriceSet` data model.
+
+So, to retrieve data across the linked records of the two modules, you use Query.
+
+## Retrieve All Product Variant Prices
+
+To retrieve all product variant prices, retrieve the product using Query and include among its fields `variants.prices.*`.
+
+For example:
+
+```ts highlights={[["6"]]}
+const { data: products } = await query.graph({
+ entity: "product",
+ fields: [
+ "*",
+ "variants.*",
+ "variants.prices.*",
+ ],
+ filters: {
+ id: [
+ "prod_123",
+ ],
+ },
+})
+```
+
+Each variant in the retrieved products has a `prices` array property with all the product variant prices. Each price object has the properties of the [Pricing Module's Price data model](https://docs.medusajs.com/references/pricing/models/Price/index.html.md).
+
+***
+
+## Retrieve Calculated Price for a Context
+
+The Pricing Module can calculate prices of a variant based on a [context](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), such as the region ID or the currency code.
+
+Learn more about prices calculation in [this Pricing Module documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md).
+
+To retrieve calculated prices of variants based on a context, retrieve the products using Query and:
+
+- Pass `variants.calculated_price.*` in the `fields` property.
+- Pass a `context` property in the object parameter. Its value is an object of objects that sets the context for the retrieved fields.
+
+For example:
+
+```ts highlights={[["10"], ["15"], ["16"], ["17"], ["18"], ["19"], ["20"], ["21"], ["22"]]}
+import { QueryContext } from "@medusajs/framework/utils"
+
+// ...
+
+const { data: products } = await query.graph({
+ entity: "product",
+ fields: [
+ "*",
+ "variants.*",
+ "variants.calculated_price.*",
+ ],
+ filters: {
+ id: "prod_123",
+ },
+ context: {
+ variants: {
+ calculated_price: QueryContext({
+ region_id: "reg_01J3MRPDNXXXDSCC76Y6YCZARS",
+ currency_code: "eur",
+ }),
+ },
+ },
+})
+```
+
+For the context of the product variant's calculated price, you pass an object to `context` with the property `variants`, whose value is another object with the property `calculated_price`.
+
+`calculated_price`'s value is created using `QueryContext` from the Modules SDK, passing it a [calculation context object](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md).
+
+Each variant in the retrieved products has a `calculated_price` object. Learn more about its properties in [this Pricing Module guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md).
+
+
# Stripe Module Provider
In this document, you’ll learn about the Stripe Module Provider and how to configure it in the Payment Module.
@@ -29099,86 +29180,6 @@ When you set up the webhook in Stripe, choose the following events to listen to:
- [Customize Stripe Integration in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/guides/customize-stripe/index.html.md).
-# Get Product Variant Prices using Query
-
-In this document, you'll learn how to retrieve product variant prices in the Medusa application using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md).
-
-The Product Module doesn't provide pricing functionalities. The Medusa application links the Product Module's `ProductVariant` data model to the Pricing Module's `PriceSet` data model.
-
-So, to retrieve data across the linked records of the two modules, you use Query.
-
-## Retrieve All Product Variant Prices
-
-To retrieve all product variant prices, retrieve the product using Query and include among its fields `variants.prices.*`.
-
-For example:
-
-```ts highlights={[["6"]]}
-const { data: products } = await query.graph({
- entity: "product",
- fields: [
- "*",
- "variants.*",
- "variants.prices.*",
- ],
- filters: {
- id: [
- "prod_123",
- ],
- },
-})
-```
-
-Each variant in the retrieved products has a `prices` array property with all the product variant prices. Each price object has the properties of the [Pricing Module's Price data model](https://docs.medusajs.com/references/pricing/models/Price/index.html.md).
-
-***
-
-## Retrieve Calculated Price for a Context
-
-The Pricing Module can calculate prices of a variant based on a [context](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md), such as the region ID or the currency code.
-
-Learn more about prices calculation in [this Pricing Module documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation/index.html.md).
-
-To retrieve calculated prices of variants based on a context, retrieve the products using Query and:
-
-- Pass `variants.calculated_price.*` in the `fields` property.
-- Pass a `context` property in the object parameter. Its value is an object of objects that sets the context for the retrieved fields.
-
-For example:
-
-```ts highlights={[["10"], ["15"], ["16"], ["17"], ["18"], ["19"], ["20"], ["21"], ["22"]]}
-import { QueryContext } from "@medusajs/framework/utils"
-
-// ...
-
-const { data: products } = await query.graph({
- entity: "product",
- fields: [
- "*",
- "variants.*",
- "variants.calculated_price.*",
- ],
- filters: {
- id: "prod_123",
- },
- context: {
- variants: {
- calculated_price: QueryContext({
- region_id: "reg_01J3MRPDNXXXDSCC76Y6YCZARS",
- currency_code: "eur",
- }),
- },
- },
-})
-```
-
-For the context of the product variant's calculated price, you pass an object to `context` with the property `variants`, whose value is another object with the property `calculated_price`.
-
-`calculated_price`'s value is created using `QueryContext` from the Modules SDK, passing it a [calculation context object](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context/index.html.md).
-
-Each variant in the retrieved products has a `calculated_price` object. Learn more about its properties in [this Pricing Module guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object/index.html.md).
-
-
# Calculate Product Variant Price with Taxes
In this document, you'll learn how to calculate a product variant's price with taxes.
@@ -29366,48 +29367,40 @@ For each product variant, you:
## Workflows
-- [generateResetPasswordTokenWorkflow](https://docs.medusajs.com/references/medusa-workflows/generateResetPasswordTokenWorkflow/index.html.md)
-- [addShippingMethodToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addShippingMethodToCartWorkflow/index.html.md)
-- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md)
-- [completeCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeCartWorkflow/index.html.md)
-- [confirmVariantInventoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmVariantInventoryWorkflow/index.html.md)
-- [createCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartCreditLinesWorkflow/index.html.md)
-- [createCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartWorkflow/index.html.md)
-- [createPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentCollectionForCartWorkflow/index.html.md)
-- [deleteCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCartCreditLinesWorkflow/index.html.md)
-- [listShippingOptionsForCartWithPricingWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWithPricingWorkflow/index.html.md)
-- [listShippingOptionsForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWorkflow/index.html.md)
-- [refreshCartItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartItemsWorkflow/index.html.md)
-- [refreshCartShippingMethodsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartShippingMethodsWorkflow/index.html.md)
-- [refreshPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshPaymentCollectionForCartWorkflow/index.html.md)
-- [transferCartCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md)
-- [updateCartPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartPromotionsWorkflow/index.html.md)
-- [updateCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartWorkflow/index.html.md)
-- [updateLineItemInCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLineItemInCartWorkflow/index.html.md)
-- [updateTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxLinesWorkflow/index.html.md)
-- [validateExistingPaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/validateExistingPaymentCollectionStep/index.html.md)
-- [batchLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinksWorkflow/index.html.md)
+- [createApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/createApiKeysWorkflow/index.html.md)
+- [deleteApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteApiKeysWorkflow/index.html.md)
+- [revokeApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/revokeApiKeysWorkflow/index.html.md)
+- [updateApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateApiKeysWorkflow/index.html.md)
+- [linkSalesChannelsToApiKeyWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md)
- [createLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLinksWorkflow/index.html.md)
- [dismissLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissLinksWorkflow/index.html.md)
+- [batchLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinksWorkflow/index.html.md)
- [updateLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLinksWorkflow/index.html.md)
+- [generateResetPasswordTokenWorkflow](https://docs.medusajs.com/references/medusa-workflows/generateResetPasswordTokenWorkflow/index.html.md)
+- [addShippingMethodToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addShippingMethodToCartWorkflow/index.html.md)
+- [completeCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeCartWorkflow/index.html.md)
+- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md)
+- [createCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartWorkflow/index.html.md)
+- [confirmVariantInventoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmVariantInventoryWorkflow/index.html.md)
+- [createPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentCollectionForCartWorkflow/index.html.md)
+- [createCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartCreditLinesWorkflow/index.html.md)
+- [listShippingOptionsForCartWithPricingWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWithPricingWorkflow/index.html.md)
+- [deleteCartCreditLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCartCreditLinesWorkflow/index.html.md)
+- [refreshCartShippingMethodsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartShippingMethodsWorkflow/index.html.md)
+- [listShippingOptionsForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/listShippingOptionsForCartWorkflow/index.html.md)
+- [transferCartCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md)
+- [refreshCartItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshCartItemsWorkflow/index.html.md)
+- [refreshPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshPaymentCollectionForCartWorkflow/index.html.md)
+- [updateCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartWorkflow/index.html.md)
+- [updateCartPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCartPromotionsWorkflow/index.html.md)
+- [updateTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxLinesWorkflow/index.html.md)
+- [validateExistingPaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/validateExistingPaymentCollectionStep/index.html.md)
+- [updateLineItemInCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLineItemInCartWorkflow/index.html.md)
- [createCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerGroupsWorkflow/index.html.md)
- [deleteCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerGroupsWorkflow/index.html.md)
- [linkCustomerGroupsToCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomerGroupsToCustomerWorkflow/index.html.md)
-- [linkCustomersToCustomerGroupWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomersToCustomerGroupWorkflow/index.html.md)
- [updateCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerGroupsWorkflow/index.html.md)
-- [createCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md)
-- [createCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md)
-- [createCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomersWorkflow/index.html.md)
-- [deleteCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md)
-- [updateCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerAddressesWorkflow/index.html.md)
-- [removeCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md)
-- [deleteCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomersWorkflow/index.html.md)
-- [updateCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomersWorkflow/index.html.md)
-- [linkSalesChannelsToApiKeyWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md)
-- [revokeApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/revokeApiKeysWorkflow/index.html.md)
-- [deleteApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteApiKeysWorkflow/index.html.md)
-- [createApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/createApiKeysWorkflow/index.html.md)
-- [updateApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateApiKeysWorkflow/index.html.md)
+- [linkCustomersToCustomerGroupWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomersToCustomerGroupWorkflow/index.html.md)
- [createDefaultsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createDefaultsWorkflow/index.html.md)
- [deleteFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFilesWorkflow/index.html.md)
- [uploadFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/uploadFilesWorkflow/index.html.md)
@@ -29418,286 +29411,294 @@ For each product variant, you:
- [createReturnFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnFulfillmentWorkflow/index.html.md)
- [createServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createServiceZonesWorkflow/index.html.md)
- [createShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShipmentWorkflow/index.html.md)
-- [createShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingProfilesWorkflow/index.html.md)
- [createShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md)
+- [createShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingProfilesWorkflow/index.html.md)
- [deleteFulfillmentSetsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFulfillmentSetsWorkflow/index.html.md)
-- [markFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md)
-- [deleteShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingOptionsWorkflow/index.html.md)
- [deleteServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteServiceZonesWorkflow/index.html.md)
+- [deleteShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingOptionsWorkflow/index.html.md)
- [updateFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateFulfillmentWorkflow/index.html.md)
+- [markFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md)
- [updateShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingOptionsWorkflow/index.html.md)
- [updateShippingProfilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateShippingProfilesWorkflow/index.html.md)
-- [updateServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateServiceZonesWorkflow/index.html.md)
- [validateFulfillmentDeliverabilityStep](https://docs.medusajs.com/references/medusa-workflows/validateFulfillmentDeliverabilityStep/index.html.md)
+- [updateServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateServiceZonesWorkflow/index.html.md)
+- [createInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInvitesWorkflow/index.html.md)
+- [deleteInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInvitesWorkflow/index.html.md)
+- [refreshInviteTokensWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshInviteTokensWorkflow/index.html.md)
+- [acceptInviteWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptInviteWorkflow/index.html.md)
- [batchInventoryItemLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchInventoryItemLevelsWorkflow/index.html.md)
-- [createInventoryItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryItemsWorkflow/index.html.md)
- [bulkCreateDeleteLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/bulkCreateDeleteLevelsWorkflow/index.html.md)
+- [createInventoryItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryItemsWorkflow/index.html.md)
+- [deleteInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryLevelsWorkflow/index.html.md)
- [createInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryLevelsWorkflow/index.html.md)
- [deleteInventoryItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryItemWorkflow/index.html.md)
- [updateInventoryItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateInventoryItemsWorkflow/index.html.md)
-- [deleteInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryLevelsWorkflow/index.html.md)
- [updateInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateInventoryLevelsWorkflow/index.html.md)
- [validateInventoryLevelsDelete](https://docs.medusajs.com/references/medusa-workflows/validateInventoryLevelsDelete/index.html.md)
-- [acceptInviteWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptInviteWorkflow/index.html.md)
-- [deleteInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInvitesWorkflow/index.html.md)
-- [createInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInvitesWorkflow/index.html.md)
-- [refreshInviteTokensWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshInviteTokensWorkflow/index.html.md)
- [deleteLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteLineItemsWorkflow/index.html.md)
-- [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md)
-- [processPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/processPaymentWorkflow/index.html.md)
-- [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md)
-- [refundPaymentsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentsWorkflow/index.html.md)
-- [validatePaymentsRefundStep](https://docs.medusajs.com/references/medusa-workflows/validatePaymentsRefundStep/index.html.md)
-- [validateRefundStep](https://docs.medusajs.com/references/medusa-workflows/validateRefundStep/index.html.md)
- [acceptOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferWorkflow/index.html.md)
-- [acceptOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md)
- [addOrderLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrderLineItemsWorkflow/index.html.md)
+- [acceptOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md)
+- [beginClaimOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md)
- [archiveOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/archiveOrderWorkflow/index.html.md)
- [beginClaimOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderValidationStep/index.html.md)
-- [beginClaimOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md)
-- [beginExchangeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md)
- [beginOrderEditOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditOrderWorkflow/index.html.md)
-- [beginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditValidationStep/index.html.md)
-- [beginReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnValidationStep/index.html.md)
- [beginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderExchangeValidationStep/index.html.md)
-- [beginReceiveReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md)
-- [beginReturnOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderValidationStep/index.html.md)
-- [cancelBeginOrderClaimValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimValidationStep/index.html.md)
+- [beginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditValidationStep/index.html.md)
+- [beginExchangeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md)
+- [beginReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnValidationStep/index.html.md)
- [beginReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderWorkflow/index.html.md)
+- [cancelBeginOrderClaimValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimValidationStep/index.html.md)
- [cancelBeginOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimWorkflow/index.html.md)
-- [cancelBeginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md)
+- [beginReturnOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderValidationStep/index.html.md)
- [cancelBeginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditValidationStep/index.html.md)
+- [cancelBeginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md)
- [cancelBeginOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditWorkflow/index.html.md)
+- [beginReceiveReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md)
- [cancelBeginOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeWorkflow/index.html.md)
-- [cancelOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md)
- [cancelClaimValidateOrderStep](https://docs.medusajs.com/references/medusa-workflows/cancelClaimValidateOrderStep/index.html.md)
- [cancelExchangeValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelExchangeValidateOrder/index.html.md)
- [cancelOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderClaimWorkflow/index.html.md)
- [cancelOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderExchangeWorkflow/index.html.md)
-- [cancelOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md)
- [cancelOrderFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentValidateOrder/index.html.md)
+- [cancelOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md)
- [cancelOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderTransferRequestWorkflow/index.html.md)
+- [cancelOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md)
- [cancelOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderWorkflow/index.html.md)
-- [cancelReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelReceiveReturnValidationStep/index.html.md)
- [cancelRequestReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelRequestReturnValidationStep/index.html.md)
- [cancelReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnReceiveWorkflow/index.html.md)
- [cancelReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnRequestWorkflow/index.html.md)
- [cancelReturnValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelReturnValidateOrder/index.html.md)
-- [cancelReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnWorkflow/index.html.md)
- [cancelTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelTransferOrderRequestValidationStep/index.html.md)
+- [cancelReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelReceiveReturnValidationStep/index.html.md)
+- [cancelReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnWorkflow/index.html.md)
- [cancelValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelValidateOrder/index.html.md)
- [confirmClaimRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmClaimRequestValidationStep/index.html.md)
- [completeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeOrderWorkflow/index.html.md)
- [confirmClaimRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmClaimRequestWorkflow/index.html.md)
-- [confirmOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestValidationStep/index.html.md)
- [confirmExchangeRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmExchangeRequestValidationStep/index.html.md)
- [confirmExchangeRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmExchangeRequestWorkflow/index.html.md)
- [confirmOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestWorkflow/index.html.md)
-- [confirmReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestValidationStep/index.html.md)
-- [confirmReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnReceiveWorkflow/index.html.md)
- [confirmReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReceiveReturnValidationStep/index.html.md)
+- [confirmReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnReceiveWorkflow/index.html.md)
+- [confirmReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestValidationStep/index.html.md)
- [confirmReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnRequestWorkflow/index.html.md)
+- [confirmOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestValidationStep/index.html.md)
- [createAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/createAndCompleteReturnOrderWorkflow/index.html.md)
- [createClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodValidationStep/index.html.md)
- [createClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodWorkflow/index.html.md)
- [createCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/createCompleteReturnValidationStep/index.html.md)
- [createExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createExchangeShippingMethodValidationStep/index.html.md)
- [createExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createExchangeShippingMethodWorkflow/index.html.md)
-- [createFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentValidateOrder/index.html.md)
- [createOrUpdateOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrUpdateOrderPaymentCollectionWorkflow/index.html.md)
-- [createOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeWorkflow/index.html.md)
-- [createOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodValidationStep/index.html.md)
- [createOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeActionsWorkflow/index.html.md)
+- [createOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeWorkflow/index.html.md)
+- [createFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentValidateOrder/index.html.md)
+- [createOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodValidationStep/index.html.md)
- [createOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodWorkflow/index.html.md)
-- [createOrderShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderShipmentWorkflow/index.html.md)
- [createOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderFulfillmentWorkflow/index.html.md)
-- [createOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md)
- [createOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderWorkflow/index.html.md)
+- [createOrderShipmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderShipmentWorkflow/index.html.md)
- [createOrdersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrdersWorkflow/index.html.md)
- [createReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createReturnShippingMethodValidationStep/index.html.md)
- [createReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnShippingMethodWorkflow/index.html.md)
+- [createOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md)
- [createShipmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createShipmentValidateOrder/index.html.md)
-- [declineTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md)
- [declineOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderTransferRequestWorkflow/index.html.md)
- [declineOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderChangeWorkflow/index.html.md)
- [deleteOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeActionsWorkflow/index.html.md)
-- [deleteOrderPaymentCollections](https://docs.medusajs.com/references/medusa-workflows/deleteOrderPaymentCollections/index.html.md)
+- [declineTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md)
- [deleteOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeWorkflow/index.html.md)
+- [deleteOrderPaymentCollections](https://docs.medusajs.com/references/medusa-workflows/deleteOrderPaymentCollections/index.html.md)
- [dismissItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestValidationStep/index.html.md)
-- [dismissItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestWorkflow/index.html.md)
- [exchangeAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeAddNewItemValidationStep/index.html.md)
- [exchangeRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeRequestItemReturnValidationStep/index.html.md)
+- [markOrderFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow/index.html.md)
+- [dismissItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestWorkflow/index.html.md)
- [getOrderDetailWorkflow](https://docs.medusajs.com/references/medusa-workflows/getOrderDetailWorkflow/index.html.md)
- [getOrdersListWorkflow](https://docs.medusajs.com/references/medusa-workflows/getOrdersListWorkflow/index.html.md)
-- [markOrderFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow/index.html.md)
-- [markPaymentCollectionAsPaid](https://docs.medusajs.com/references/medusa-workflows/markPaymentCollectionAsPaid/index.html.md)
- [orderClaimAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemValidationStep/index.html.md)
- [orderClaimAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemWorkflow/index.html.md)
-- [orderClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemValidationStep/index.html.md)
+- [markPaymentCollectionAsPaid](https://docs.medusajs.com/references/medusa-workflows/markPaymentCollectionAsPaid/index.html.md)
- [orderClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemWorkflow/index.html.md)
+- [orderClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemValidationStep/index.html.md)
- [orderClaimRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnValidationStep/index.html.md)
+- [orderEditAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemValidationStep/index.html.md)
- [orderClaimRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnWorkflow/index.html.md)
- [orderEditAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemWorkflow/index.html.md)
-- [orderEditAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditAddNewItemValidationStep/index.html.md)
- [orderEditUpdateItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityValidationStep/index.html.md)
+- [orderExchangeAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderExchangeAddNewItemWorkflow/index.html.md)
- [orderEditUpdateItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityWorkflow/index.html.md)
- [orderFulfillmentDeliverablilityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderFulfillmentDeliverablilityValidationStep/index.html.md)
- [orderExchangeRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderExchangeRequestItemReturnWorkflow/index.html.md)
-- [orderExchangeAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderExchangeAddNewItemWorkflow/index.html.md)
- [receiveAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveAndCompleteReturnOrderWorkflow/index.html.md)
-- [receiveCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveCompleteReturnValidationStep/index.html.md)
-- [receiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md)
- [receiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestValidationStep/index.html.md)
-- [removeClaimAddItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md)
- [removeAddItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeAddItemClaimActionWorkflow/index.html.md)
+- [receiveCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveCompleteReturnValidationStep/index.html.md)
+- [removeClaimAddItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md)
- [removeClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodValidationStep/index.html.md)
- [removeClaimItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimItemActionValidationStep/index.html.md)
+- [receiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md)
+- [removeExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodValidationStep/index.html.md)
- [removeExchangeItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeItemActionValidationStep/index.html.md)
- [removeClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodWorkflow/index.html.md)
-- [removeExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodValidationStep/index.html.md)
- [removeExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeExchangeShippingMethodWorkflow/index.html.md)
-- [removeItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemClaimActionWorkflow/index.html.md)
- [removeItemReceiveReturnActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionValidationStep/index.html.md)
-- [removeItemOrderEditActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemOrderEditActionWorkflow/index.html.md)
+- [removeItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemClaimActionWorkflow/index.html.md)
- [removeItemExchangeActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemExchangeActionWorkflow/index.html.md)
+- [removeItemOrderEditActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemOrderEditActionWorkflow/index.html.md)
+- [removeItemReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReturnActionWorkflow/index.html.md)
- [removeItemReceiveReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionWorkflow/index.html.md)
- [removeOrderEditItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditItemActionValidationStep/index.html.md)
-- [removeOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodValidationStep/index.html.md)
-- [removeItemReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReturnActionWorkflow/index.html.md)
-- [removeOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodWorkflow/index.html.md)
- [removeReturnItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnItemActionValidationStep/index.html.md)
+- [removeOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodValidationStep/index.html.md)
+- [removeOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditShippingMethodWorkflow/index.html.md)
- [removeReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnShippingMethodValidationStep/index.html.md)
-- [requestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnValidationStep/index.html.md)
- [removeReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeReturnShippingMethodWorkflow/index.html.md)
+- [requestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnValidationStep/index.html.md)
- [requestOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestValidationStep/index.html.md)
-- [requestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnWorkflow/index.html.md)
- [requestOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestWorkflow/index.html.md)
- [requestOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferValidationStep/index.html.md)
- [requestOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferWorkflow/index.html.md)
-- [throwUnlessPaymentCollectionNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessPaymentCollectionNotPaid/index.html.md)
+- [requestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnWorkflow/index.html.md)
- [throwUnlessStatusIsNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessStatusIsNotPaid/index.html.md)
+- [throwUnlessPaymentCollectionNotPaid](https://docs.medusajs.com/references/medusa-workflows/throwUnlessPaymentCollectionNotPaid/index.html.md)
- [updateClaimAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemValidationStep/index.html.md)
- [updateClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemValidationStep/index.html.md)
-- [updateClaimAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemWorkflow/index.html.md)
-- [updateClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemWorkflow/index.html.md)
-- [updateExchangeAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md)
-- [updateClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodWorkflow/index.html.md)
- [updateClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodValidationStep/index.html.md)
+- [updateClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemWorkflow/index.html.md)
+- [updateClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodWorkflow/index.html.md)
+- [updateClaimAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemWorkflow/index.html.md)
+- [updateExchangeAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md)
- [updateExchangeAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemWorkflow/index.html.md)
- [updateExchangeShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodValidationStep/index.html.md)
-- [updateExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodWorkflow/index.html.md)
- [updateOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangeActionsWorkflow/index.html.md)
- [updateOrderChangesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangesWorkflow/index.html.md)
+- [updateExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodWorkflow/index.html.md)
- [updateOrderEditItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityValidationStep/index.html.md)
-- [updateOrderEditAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemValidationStep/index.html.md)
- [updateOrderEditAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemWorkflow/index.html.md)
-- [updateOrderEditItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityWorkflow/index.html.md)
+- [updateOrderEditAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditAddItemValidationStep/index.html.md)
- [updateOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodValidationStep/index.html.md)
-- [updateOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodWorkflow/index.html.md)
- [updateOrderTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderTaxLinesWorkflow/index.html.md)
- [updateOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderValidationStep/index.html.md)
-- [updateReceiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestValidationStep/index.html.md)
+- [updateOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodWorkflow/index.html.md)
- [updateOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderWorkflow/index.html.md)
+- [updateOrderEditItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityWorkflow/index.html.md)
+- [updateReceiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestValidationStep/index.html.md)
- [updateReceiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReceiveItemReturnRequestWorkflow/index.html.md)
- [updateRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnValidationStep/index.html.md)
-- [updateReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodWorkflow/index.html.md)
- [updateReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodValidationStep/index.html.md)
-- [updateRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md)
- [updateReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReturnValidationStep/index.html.md)
+- [updateReturnShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodWorkflow/index.html.md)
+- [updateRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md)
- [updateReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnWorkflow/index.html.md)
+- [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md)
+- [processPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/processPaymentWorkflow/index.html.md)
+- [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md)
+- [refundPaymentsWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentsWorkflow/index.html.md)
+- [validateRefundStep](https://docs.medusajs.com/references/medusa-workflows/validateRefundStep/index.html.md)
+- [validatePaymentsRefundStep](https://docs.medusajs.com/references/medusa-workflows/validatePaymentsRefundStep/index.html.md)
+- [createPaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentSessionsWorkflow/index.html.md)
+- [createRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRefundReasonsWorkflow/index.html.md)
+- [deleteRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRefundReasonsWorkflow/index.html.md)
+- [deletePaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePaymentSessionsWorkflow/index.html.md)
+- [updateRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRefundReasonsWorkflow/index.html.md)
- [batchPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPriceListPricesWorkflow/index.html.md)
-- [createPriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListsWorkflow/index.html.md)
-- [deletePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePriceListsWorkflow/index.html.md)
- [createPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListPricesWorkflow/index.html.md)
+- [deletePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePriceListsWorkflow/index.html.md)
+- [createPriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListsWorkflow/index.html.md)
- [removePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/removePriceListPricesWorkflow/index.html.md)
- [updatePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListsWorkflow/index.html.md)
- [updatePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListPricesWorkflow/index.html.md)
-- [createPaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentSessionsWorkflow/index.html.md)
-- [createRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRefundReasonsWorkflow/index.html.md)
-- [deletePaymentSessionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePaymentSessionsWorkflow/index.html.md)
-- [updateRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRefundReasonsWorkflow/index.html.md)
-- [deleteRefundReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRefundReasonsWorkflow/index.html.md)
+- [createCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md)
+- [createCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md)
+- [deleteCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md)
+- [deleteCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomersWorkflow/index.html.md)
+- [createCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomersWorkflow/index.html.md)
+- [updateCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomersWorkflow/index.html.md)
+- [updateCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerAddressesWorkflow/index.html.md)
+- [removeCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md)
- [createPricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPricePreferencesWorkflow/index.html.md)
- [deletePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePricePreferencesWorkflow/index.html.md)
- [updatePricePreferencesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePricePreferencesWorkflow/index.html.md)
-- [batchLinkProductsToCategoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCategoryWorkflow/index.html.md)
-- [batchProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductsWorkflow/index.html.md)
-- [batchLinkProductsToCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCollectionWorkflow/index.html.md)
-- [batchProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductVariantsWorkflow/index.html.md)
-- [createCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCollectionsWorkflow/index.html.md)
-- [createProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductOptionsWorkflow/index.html.md)
-- [createProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTypesWorkflow/index.html.md)
-- [createProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTagsWorkflow/index.html.md)
-- [createProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductVariantsWorkflow/index.html.md)
-- [deleteCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCollectionsWorkflow/index.html.md)
-- [deleteProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md)
-- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md)
-- [deleteProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTagsWorkflow/index.html.md)
-- [deleteProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTypesWorkflow/index.html.md)
-- [deleteProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductsWorkflow/index.html.md)
-- [deleteProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductVariantsWorkflow/index.html.md)
-- [exportProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/exportProductsWorkflow/index.html.md)
-- [importProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/importProductsWorkflow/index.html.md)
-- [updateCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCollectionsWorkflow/index.html.md)
-- [updateProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductOptionsWorkflow/index.html.md)
-- [updateProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTagsWorkflow/index.html.md)
-- [updateProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTypesWorkflow/index.html.md)
-- [updateProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductVariantsWorkflow/index.html.md)
-- [updateProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductsWorkflow/index.html.md)
-- [upsertVariantPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/upsertVariantPricesWorkflow/index.html.md)
-- [batchPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPromotionRulesWorkflow/index.html.md)
-- [addOrRemoveCampaignPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/index.html.md)
-- [validateProductInputStep](https://docs.medusajs.com/references/medusa-workflows/validateProductInputStep/index.html.md)
- [createCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCampaignsWorkflow/index.html.md)
-- [deleteCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md)
+- [batchPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPromotionRulesWorkflow/index.html.md)
- [createPromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionRulesWorkflow/index.html.md)
+- [addOrRemoveCampaignPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/index.html.md)
- [createPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPromotionsWorkflow/index.html.md)
- [deletePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionRulesWorkflow/index.html.md)
-- [updateCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCampaignsWorkflow/index.html.md)
+- [deleteCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md)
+- [updatePromotionsStatusWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md)
- [updatePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionRulesWorkflow/index.html.md)
- [deletePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionsWorkflow/index.html.md)
-- [updatePromotionsStatusWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md)
+- [updateCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCampaignsWorkflow/index.html.md)
- [updatePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsWorkflow/index.html.md)
- [updatePromotionsValidationStep](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsValidationStep/index.html.md)
- [createProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductCategoriesWorkflow/index.html.md)
-- [deleteProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md)
- [updateProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductCategoriesWorkflow/index.html.md)
-- [createRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRegionsWorkflow/index.html.md)
-- [deleteRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRegionsWorkflow/index.html.md)
-- [updateRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRegionsWorkflow/index.html.md)
+- [deleteProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md)
+- [batchProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductVariantsWorkflow/index.html.md)
+- [batchLinkProductsToCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCollectionWorkflow/index.html.md)
+- [batchLinkProductsToCategoryWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCategoryWorkflow/index.html.md)
+- [createProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTagsWorkflow/index.html.md)
+- [batchProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductsWorkflow/index.html.md)
+- [createProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductOptionsWorkflow/index.html.md)
+- [createCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCollectionsWorkflow/index.html.md)
+- [createProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTypesWorkflow/index.html.md)
+- [createProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductsWorkflow/index.html.md)
+- [deleteCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCollectionsWorkflow/index.html.md)
+- [createProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductVariantsWorkflow/index.html.md)
+- [deleteProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md)
+- [deleteProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTagsWorkflow/index.html.md)
+- [deleteProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTypesWorkflow/index.html.md)
+- [deleteProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductsWorkflow/index.html.md)
+- [exportProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/exportProductsWorkflow/index.html.md)
+- [importProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/importProductsWorkflow/index.html.md)
+- [updateProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductOptionsWorkflow/index.html.md)
+- [deleteProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductVariantsWorkflow/index.html.md)
+- [updateCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCollectionsWorkflow/index.html.md)
+- [updateProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTagsWorkflow/index.html.md)
+- [updateProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductTypesWorkflow/index.html.md)
+- [updateProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductsWorkflow/index.html.md)
+- [updateProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductVariantsWorkflow/index.html.md)
+- [upsertVariantPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/upsertVariantPricesWorkflow/index.html.md)
+- [validateProductInputStep](https://docs.medusajs.com/references/medusa-workflows/validateProductInputStep/index.html.md)
- [createReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReservationsWorkflow/index.html.md)
-- [updateReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReservationsWorkflow/index.html.md)
-- [deleteReservationsByLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsByLineItemsWorkflow/index.html.md)
- [deleteReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsWorkflow/index.html.md)
-- [createReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnReasonsWorkflow/index.html.md)
+- [deleteReservationsByLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsByLineItemsWorkflow/index.html.md)
+- [updateReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReservationsWorkflow/index.html.md)
+- [deleteRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRegionsWorkflow/index.html.md)
+- [createRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createRegionsWorkflow/index.html.md)
+- [updateRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRegionsWorkflow/index.html.md)
- [deleteReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReturnReasonsWorkflow/index.html.md)
+- [createReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnReasonsWorkflow/index.html.md)
- [updateReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnReasonsWorkflow/index.html.md)
- [createSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createSalesChannelsWorkflow/index.html.md)
- [deleteSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteSalesChannelsWorkflow/index.html.md)
- [linkProductsToSalesChannelWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkProductsToSalesChannelWorkflow/index.html.md)
- [updateSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateSalesChannelsWorkflow/index.html.md)
+- [createLocationFulfillmentSetWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLocationFulfillmentSetWorkflow/index.html.md)
+- [createStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStockLocationsWorkflow/index.html.md)
+- [updateStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStockLocationsWorkflow/index.html.md)
+- [deleteStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStockLocationsWorkflow/index.html.md)
+- [linkSalesChannelsToStockLocationWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToStockLocationWorkflow/index.html.md)
- [deleteShippingProfileWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteShippingProfileWorkflow/index.html.md)
- [validateStepShippingProfileDelete](https://docs.medusajs.com/references/medusa-workflows/validateStepShippingProfileDelete/index.html.md)
-- [createLocationFulfillmentSetWorkflow](https://docs.medusajs.com/references/medusa-workflows/createLocationFulfillmentSetWorkflow/index.html.md)
-- [linkSalesChannelsToStockLocationWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToStockLocationWorkflow/index.html.md)
-- [createStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStockLocationsWorkflow/index.html.md)
-- [deleteStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStockLocationsWorkflow/index.html.md)
-- [updateStockLocationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStockLocationsWorkflow/index.html.md)
- [createStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStoresWorkflow/index.html.md)
-- [updateStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStoresWorkflow/index.html.md)
- [deleteStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteStoresWorkflow/index.html.md)
+- [updateStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateStoresWorkflow/index.html.md)
- [createTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRateRulesWorkflow/index.html.md)
- [createTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRatesWorkflow/index.html.md)
- [createTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRegionsWorkflow/index.html.md)
-- [deleteTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md)
- [deleteTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRateRulesWorkflow/index.html.md)
- [deleteTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRegionsWorkflow/index.html.md)
-- [setTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md)
+- [deleteTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md)
- [maybeListTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/maybeListTaxRateRuleIdsStep/index.html.md)
-- [updateTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md)
+- [setTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md)
- [updateTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRatesWorkflow/index.html.md)
-- [createUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUserAccountWorkflow/index.html.md)
+- [updateTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md)
- [createUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUsersWorkflow/index.html.md)
+- [createUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUserAccountWorkflow/index.html.md)
- [deleteUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteUsersWorkflow/index.html.md)
- [removeUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeUserAccountWorkflow/index.html.md)
- [updateUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateUsersWorkflow/index.html.md)
@@ -29705,262 +29706,262 @@ For each product variant, you:
## Steps
+- [createApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/createApiKeysStep/index.html.md)
+- [deleteApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteApiKeysStep/index.html.md)
+- [linkSalesChannelsToApiKeyStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkSalesChannelsToApiKeyStep/index.html.md)
+- [revokeApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/revokeApiKeysStep/index.html.md)
+- [updateApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateApiKeysStep/index.html.md)
+- [validateSalesChannelsExistStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateSalesChannelsExistStep/index.html.md)
- [setAuthAppMetadataStep](https://docs.medusajs.com/references/medusa-workflows/steps/setAuthAppMetadataStep/index.html.md)
+- [createCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerAddressesStep/index.html.md)
+- [deleteCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerAddressesStep/index.html.md)
+- [maybeUnsetDefaultShippingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/index.html.md)
+- [maybeUnsetDefaultBillingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md)
+- [createCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomersStep/index.html.md)
+- [updateCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomersStep/index.html.md)
+- [updateCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerAddressesStep/index.html.md)
+- [deleteCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomersStep/index.html.md)
+- [validateCustomerAccountCreation](https://docs.medusajs.com/references/medusa-workflows/steps/validateCustomerAccountCreation/index.html.md)
+- [createRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRemoteLinkStep/index.html.md)
+- [dismissRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/dismissRemoteLinkStep/index.html.md)
+- [emitEventStep](https://docs.medusajs.com/references/medusa-workflows/steps/emitEventStep/index.html.md)
+- [updateRemoteLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md)
+- [removeRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md)
+- [useQueryGraphStep](https://docs.medusajs.com/references/medusa-workflows/steps/useQueryGraphStep/index.html.md)
+- [validatePresenceOfStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md)
+- [useRemoteQueryStep](https://docs.medusajs.com/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md)
+- [createDefaultStoreStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md)
+- [cancelFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelFulfillmentStep/index.html.md)
+- [calculateShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/calculateShippingOptionsPricesStep/index.html.md)
+- [createFulfillmentSets](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentSets/index.html.md)
+- [buildPriceSet](https://docs.medusajs.com/references/medusa-workflows/steps/buildPriceSet/index.html.md)
+- [createFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentStep/index.html.md)
+- [createReturnFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnFulfillmentStep/index.html.md)
+- [createServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createServiceZonesStep/index.html.md)
+- [createShippingOptionsPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/index.html.md)
+- [createShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionRulesStep/index.html.md)
+- [deleteFulfillmentSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFulfillmentSetsStep/index.html.md)
+- [createShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingProfilesStep/index.html.md)
+- [deleteShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionRulesStep/index.html.md)
+- [deleteServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteServiceZonesStep/index.html.md)
+- [deleteShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionsStep/index.html.md)
+- [setShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/setShippingOptionsPricesStep/index.html.md)
+- [updateFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateFulfillmentStep/index.html.md)
+- [updateShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingOptionRulesStep/index.html.md)
+- [updateServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateServiceZonesStep/index.html.md)
+- [upsertShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/upsertShippingOptionsStep/index.html.md)
+- [updateShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingProfilesStep/index.html.md)
+- [validateShippingOptionPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md)
+- [validateShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShipmentStep/index.html.md)
+- [deleteFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFilesStep/index.html.md)
+- [uploadFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/uploadFilesStep/index.html.md)
+- [createCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerGroupsStep/index.html.md)
+- [deleteCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerGroupStep/index.html.md)
+- [linkCustomersToCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomersToCustomerGroupStep/index.html.md)
+- [linkCustomerGroupsToCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomerGroupsToCustomerStep/index.html.md)
+- [updateCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerGroupsStep/index.html.md)
+- [attachInventoryItemToVariants](https://docs.medusajs.com/references/medusa-workflows/steps/attachInventoryItemToVariants/index.html.md)
+- [createInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryItemsStep/index.html.md)
+- [createInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryLevelsStep/index.html.md)
+- [deleteInventoryItemStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryItemStep/index.html.md)
+- [deleteInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryLevelsStep/index.html.md)
+- [updateInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryItemsStep/index.html.md)
+- [adjustInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/adjustInventoryLevelsStep/index.html.md)
+- [validateInventoryItemsForCreate](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryItemsForCreate/index.html.md)
+- [updateInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryLevelsStep/index.html.md)
+- [validateInventoryDeleteStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryDeleteStep/index.html.md)
+- [validateInventoryLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryLocationsStep/index.html.md)
+- [notifyOnFailureStep](https://docs.medusajs.com/references/medusa-workflows/steps/notifyOnFailureStep/index.html.md)
+- [sendNotificationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/sendNotificationsStep/index.html.md)
- [addShippingMethodToCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/addShippingMethodToCartStep/index.html.md)
-- [confirmInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/confirmInventoryStep/index.html.md)
-- [createCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCartsStep/index.html.md)
- [createLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemAdjustmentsStep/index.html.md)
+- [createCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCartsStep/index.html.md)
+- [confirmInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/confirmInventoryStep/index.html.md)
- [createLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemsStep/index.html.md)
- [createPaymentCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentCollectionsStep/index.html.md)
- [findOneOrAnyRegionStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOneOrAnyRegionStep/index.html.md)
- [createShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingMethodAdjustmentsStep/index.html.md)
-- [findOrCreateCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOrCreateCustomerStep/index.html.md)
- [findSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/findSalesChannelStep/index.html.md)
- [getActionsToComputeFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getActionsToComputeFromPromotionsStep/index.html.md)
-- [getLineItemActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getLineItemActionsStep/index.html.md)
-- [getPromotionCodesToApply](https://docs.medusajs.com/references/medusa-workflows/steps/getPromotionCodesToApply/index.html.md)
- [getVariantPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantPriceSetsStep/index.html.md)
-- [removeLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeLineItemAdjustmentsStep/index.html.md)
-- [prepareAdjustmentsFromPromotionActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/prepareAdjustmentsFromPromotionActionsStep/index.html.md)
-- [removeShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodAdjustmentsStep/index.html.md)
+- [getPromotionCodesToApply](https://docs.medusajs.com/references/medusa-workflows/steps/getPromotionCodesToApply/index.html.md)
+- [getLineItemActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getLineItemActionsStep/index.html.md)
- [getVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantsStep/index.html.md)
+- [prepareAdjustmentsFromPromotionActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/prepareAdjustmentsFromPromotionActionsStep/index.html.md)
+- [removeLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeLineItemAdjustmentsStep/index.html.md)
+- [removeShippingMethodAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodAdjustmentsStep/index.html.md)
- [removeShippingMethodFromCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodFromCartStep/index.html.md)
- [reserveInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/reserveInventoryStep/index.html.md)
+- [findOrCreateCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/findOrCreateCustomerStep/index.html.md)
+- [retrieveCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/retrieveCartStep/index.html.md)
- [setTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setTaxLinesForItemsStep/index.html.md)
- [updateCartPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartPromotionsStep/index.html.md)
-- [retrieveCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/retrieveCartStep/index.html.md)
-- [updateCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartsStep/index.html.md)
-- [updateShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingMethodsStep/index.html.md)
- [updateLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStep/index.html.md)
-- [validateCartPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartPaymentsStep/index.html.md)
+- [updateShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingMethodsStep/index.html.md)
- [validateAndReturnShippingMethodsDataStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateAndReturnShippingMethodsDataStep/index.html.md)
+- [validateCartPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartPaymentsStep/index.html.md)
- [validateCartShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsStep/index.html.md)
-- [validateCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartStep/index.html.md)
- [validateCartShippingOptionsPriceStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsPriceStep/index.html.md)
+- [validateCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartStep/index.html.md)
- [validateLineItemPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateLineItemPricesStep/index.html.md)
- [validateShippingStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingStep/index.html.md)
- [validateVariantPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPricesStep/index.html.md)
-- [createApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/createApiKeysStep/index.html.md)
-- [deleteApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteApiKeysStep/index.html.md)
-- [revokeApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/revokeApiKeysStep/index.html.md)
-- [updateApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateApiKeysStep/index.html.md)
-- [linkSalesChannelsToApiKeyStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkSalesChannelsToApiKeyStep/index.html.md)
-- [validateSalesChannelsExistStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateSalesChannelsExistStep/index.html.md)
-- [createRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRemoteLinkStep/index.html.md)
-- [emitEventStep](https://docs.medusajs.com/references/medusa-workflows/steps/emitEventStep/index.html.md)
-- [removeRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md)
-- [dismissRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/dismissRemoteLinkStep/index.html.md)
-- [useQueryGraphStep](https://docs.medusajs.com/references/medusa-workflows/steps/useQueryGraphStep/index.html.md)
-- [useRemoteQueryStep](https://docs.medusajs.com/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md)
-- [updateRemoteLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md)
-- [validatePresenceOfStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md)
-- [createCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomersStep/index.html.md)
-- [deleteCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomersStep/index.html.md)
-- [deleteCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerAddressesStep/index.html.md)
-- [maybeUnsetDefaultBillingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md)
-- [maybeUnsetDefaultShippingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/index.html.md)
-- [createCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerAddressesStep/index.html.md)
-- [updateCustomerAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerAddressesStep/index.html.md)
-- [updateCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomersStep/index.html.md)
-- [validateCustomerAccountCreation](https://docs.medusajs.com/references/medusa-workflows/steps/validateCustomerAccountCreation/index.html.md)
-- [createCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomerGroupsStep/index.html.md)
-- [deleteCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomerGroupStep/index.html.md)
-- [linkCustomerGroupsToCustomerStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomerGroupsToCustomerStep/index.html.md)
-- [linkCustomersToCustomerGroupStep](https://docs.medusajs.com/references/medusa-workflows/steps/linkCustomersToCustomerGroupStep/index.html.md)
-- [updateCustomerGroupsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCustomerGroupsStep/index.html.md)
-- [createDefaultStoreStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md)
-- [deleteFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFilesStep/index.html.md)
-- [uploadFilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/uploadFilesStep/index.html.md)
-- [adjustInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/adjustInventoryLevelsStep/index.html.md)
-- [attachInventoryItemToVariants](https://docs.medusajs.com/references/medusa-workflows/steps/attachInventoryItemToVariants/index.html.md)
-- [createInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryItemsStep/index.html.md)
-- [createInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInventoryLevelsStep/index.html.md)
-- [deleteInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryLevelsStep/index.html.md)
-- [updateInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryItemsStep/index.html.md)
-- [updateInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryLevelsStep/index.html.md)
-- [deleteInventoryItemStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryItemStep/index.html.md)
-- [validateInventoryDeleteStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryDeleteStep/index.html.md)
-- [validateInventoryItemsForCreate](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryItemsForCreate/index.html.md)
-- [validateInventoryLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryLocationsStep/index.html.md)
-- [calculateShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/calculateShippingOptionsPricesStep/index.html.md)
-- [cancelFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelFulfillmentStep/index.html.md)
-- [createFulfillmentSets](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentSets/index.html.md)
-- [buildPriceSet](https://docs.medusajs.com/references/medusa-workflows/steps/buildPriceSet/index.html.md)
-- [createFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentStep/index.html.md)
-- [createShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionRulesStep/index.html.md)
-- [createServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createServiceZonesStep/index.html.md)
-- [createReturnFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnFulfillmentStep/index.html.md)
-- [createShippingOptionsPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/index.html.md)
-- [createShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingProfilesStep/index.html.md)
-- [deleteFulfillmentSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteFulfillmentSetsStep/index.html.md)
-- [deleteShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionRulesStep/index.html.md)
-- [deleteShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingOptionsStep/index.html.md)
-- [deleteServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteServiceZonesStep/index.html.md)
-- [setShippingOptionsPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/setShippingOptionsPricesStep/index.html.md)
-- [updateFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateFulfillmentStep/index.html.md)
-- [updateServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateServiceZonesStep/index.html.md)
-- [updateShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingOptionRulesStep/index.html.md)
-- [updateShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingProfilesStep/index.html.md)
-- [validateShippingOptionPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md)
-- [upsertShippingOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/upsertShippingOptionsStep/index.html.md)
-- [validateShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShipmentStep/index.html.md)
-- [deleteInvitesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInvitesStep/index.html.md)
+- [updateCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartsStep/index.html.md)
- [createInviteStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInviteStep/index.html.md)
+- [deleteInvitesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInvitesStep/index.html.md)
- [refreshInviteTokensStep](https://docs.medusajs.com/references/medusa-workflows/steps/refreshInviteTokensStep/index.html.md)
- [validateTokenStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateTokenStep/index.html.md)
-- [sendNotificationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/sendNotificationsStep/index.html.md)
-- [notifyOnFailureStep](https://docs.medusajs.com/references/medusa-workflows/steps/notifyOnFailureStep/index.html.md)
-- [deleteLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteLineItemsStep/index.html.md)
-- [updateLineItemsStepWithSelector](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStepWithSelector/index.html.md)
- [listLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listLineItemsStep/index.html.md)
-- [addOrderTransactionStep](https://docs.medusajs.com/references/medusa-workflows/steps/addOrderTransactionStep/index.html.md)
+- [updateLineItemsStepWithSelector](https://docs.medusajs.com/references/medusa-workflows/steps/updateLineItemsStepWithSelector/index.html.md)
+- [deleteLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteLineItemsStep/index.html.md)
- [archiveOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/archiveOrdersStep/index.html.md)
- [cancelOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderChangeStep/index.html.md)
- [cancelOrderClaimStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderClaimStep/index.html.md)
- [cancelOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderFulfillmentStep/index.html.md)
- [cancelOrderExchangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderExchangeStep/index.html.md)
- [cancelOrderReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderReturnStep/index.html.md)
+- [addOrderTransactionStep](https://docs.medusajs.com/references/medusa-workflows/steps/addOrderTransactionStep/index.html.md)
+- [completeOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/completeOrdersStep/index.html.md)
- [cancelOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrdersStep/index.html.md)
- [createCompleteReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCompleteReturnStep/index.html.md)
-- [completeOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/completeOrdersStep/index.html.md)
-- [createOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderChangeStep/index.html.md)
-- [createOrderExchangeItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md)
-- [createOrderClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderClaimsStep/index.html.md)
- [createOrderClaimItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderClaimItemsFromActionsStep/index.html.md)
+- [createOrderClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderClaimsStep/index.html.md)
+- [createOrderExchangeItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md)
- [createOrderExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangesStep/index.html.md)
- [createOrderLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderLineItemsStep/index.html.md)
- [createOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrdersStep/index.html.md)
-- [deleteClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteClaimsStep/index.html.md)
+- [createOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderChangeStep/index.html.md)
- [createReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnsStep/index.html.md)
- [declineOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/declineOrderChangeStep/index.html.md)
-- [deleteExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteExchangesStep/index.html.md)
+- [deleteClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteClaimsStep/index.html.md)
- [deleteOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangeActionsStep/index.html.md)
-- [deleteOrderLineItems](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderLineItems/index.html.md)
- [deleteOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangesStep/index.html.md)
+- [deleteOrderLineItems](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderLineItems/index.html.md)
- [deleteOrderShippingMethods](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderShippingMethods/index.html.md)
-- [previewOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/previewOrderChangeStep/index.html.md)
- [deleteReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnsStep/index.html.md)
+- [deleteExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteExchangesStep/index.html.md)
+- [previewOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/previewOrderChangeStep/index.html.md)
- [registerOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderChangesStep/index.html.md)
-- [updateOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangeActionsStep/index.html.md)
- [registerOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderFulfillmentStep/index.html.md)
- [setOrderTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setOrderTaxLinesForItemsStep/index.html.md)
- [registerOrderShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderShipmentStep/index.html.md)
- [updateOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangesStep/index.html.md)
-- [updateReturnItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnItemsStep/index.html.md)
- [updateOrderShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderShippingMethodsStep/index.html.md)
-- [updateReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnsStep/index.html.md)
+- [updateOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangeActionsStep/index.html.md)
- [updateOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrdersStep/index.html.md)
-- [authorizePaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/authorizePaymentSessionStep/index.html.md)
-- [cancelPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelPaymentStep/index.html.md)
-- [refundPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentStep/index.html.md)
-- [capturePaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/capturePaymentStep/index.html.md)
-- [refundPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentsStep/index.html.md)
-- [createRefundReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRefundReasonStep/index.html.md)
-- [deletePaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePaymentSessionsStep/index.html.md)
-- [createPaymentAccountHolderStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentAccountHolderStep/index.html.md)
-- [createPaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentSessionStep/index.html.md)
-- [deleteRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md)
-- [updatePaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePaymentCollectionStep/index.html.md)
-- [validateDeletedPaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDeletedPaymentSessionsStep/index.html.md)
-- [updateRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRefundReasonsStep/index.html.md)
-- [createPricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPricePreferencesStep/index.html.md)
-- [createPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceSetsStep/index.html.md)
-- [updatePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesStep/index.html.md)
-- [updatePricePreferencesAsArrayStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesAsArrayStep/index.html.md)
-- [updatePriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceSetsStep/index.html.md)
-- [deletePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md)
- [createPriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListPricesStep/index.html.md)
+- [updateReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnsStep/index.html.md)
+- [updateReturnItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnItemsStep/index.html.md)
- [createPriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListsStep/index.html.md)
- [deletePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePriceListsStep/index.html.md)
- [getExistingPriceListsPriceIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getExistingPriceListsPriceIdsStep/index.html.md)
- [removePriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/removePriceListPricesStep/index.html.md)
+- [updatePriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListPricesStep/index.html.md)
- [updatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListsStep/index.html.md)
- [validateVariantPriceLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateVariantPriceLinksStep/index.html.md)
-- [updatePriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceListPricesStep/index.html.md)
- [validatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePriceListsStep/index.html.md)
-- [updateProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductCategoriesStep/index.html.md)
-- [deleteProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductCategoriesStep/index.html.md)
-- [createProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductCategoriesStep/index.html.md)
+- [createPricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPricePreferencesStep/index.html.md)
+- [deletePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md)
+- [createPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceSetsStep/index.html.md)
+- [updatePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesStep/index.html.md)
+- [updatePriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePriceSetsStep/index.html.md)
+- [updatePricePreferencesAsArrayStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesAsArrayStep/index.html.md)
- [batchLinkProductsToCategoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCategoryStep/index.html.md)
- [batchLinkProductsToCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCollectionStep/index.html.md)
-- [createCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCollectionsStep/index.html.md)
- [createProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductOptionsStep/index.html.md)
- [createProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTagsStep/index.html.md)
-- [createProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTypesStep/index.html.md)
+- [createCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCollectionsStep/index.html.md)
- [createProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductsStep/index.html.md)
-- [createProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductVariantsStep/index.html.md)
+- [createProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTypesStep/index.html.md)
- [createVariantPricingLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createVariantPricingLinkStep/index.html.md)
- [deleteCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCollectionsStep/index.html.md)
+- [createProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductVariantsStep/index.html.md)
- [deleteProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTagsStep/index.html.md)
- [deleteProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTypesStep/index.html.md)
- [deleteProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductOptionsStep/index.html.md)
-- [deleteProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductVariantsStep/index.html.md)
- [deleteProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductsStep/index.html.md)
-- [getProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getProductsStep/index.html.md)
- [generateProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/generateProductCsvStep/index.html.md)
-- [getAllProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getAllProductsStep/index.html.md)
+- [getProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getProductsStep/index.html.md)
+- [deleteProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductVariantsStep/index.html.md)
- [getVariantAvailabilityStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantAvailabilityStep/index.html.md)
+- [getAllProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getAllProductsStep/index.html.md)
- [groupProductsForBatchStep](https://docs.medusajs.com/references/medusa-workflows/steps/groupProductsForBatchStep/index.html.md)
-- [parseProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/parseProductCsvStep/index.html.md)
-- [updateProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductOptionsStep/index.html.md)
- [updateCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCollectionsStep/index.html.md)
-- [updateProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTypesStep/index.html.md)
- [updateProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTagsStep/index.html.md)
+- [parseProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/parseProductCsvStep/index.html.md)
- [updateProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductVariantsStep/index.html.md)
-- [updateProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductsStep/index.html.md)
+- [updateProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductOptionsStep/index.html.md)
+- [updateProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTypesStep/index.html.md)
- [waitConfirmationProductImportStep](https://docs.medusajs.com/references/medusa-workflows/steps/waitConfirmationProductImportStep/index.html.md)
+- [updateProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductsStep/index.html.md)
+- [capturePaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/capturePaymentStep/index.html.md)
+- [authorizePaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/authorizePaymentSessionStep/index.html.md)
+- [cancelPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelPaymentStep/index.html.md)
+- [refundPaymentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentsStep/index.html.md)
+- [refundPaymentStep](https://docs.medusajs.com/references/medusa-workflows/steps/refundPaymentStep/index.html.md)
+- [createProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductCategoriesStep/index.html.md)
+- [deleteProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductCategoriesStep/index.html.md)
+- [updateProductCategoriesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductCategoriesStep/index.html.md)
- [addCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addCampaignPromotionsStep/index.html.md)
-- [createCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCampaignsStep/index.html.md)
-- [deleteCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md)
- [addRulesToPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addRulesToPromotionsStep/index.html.md)
- [createPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPromotionsStep/index.html.md)
-- [removeCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md)
+- [createCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCampaignsStep/index.html.md)
+- [deleteCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md)
- [deletePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePromotionsStep/index.html.md)
-- [removeRulesFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md)
- [updateCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCampaignsStep/index.html.md)
+- [removeRulesFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md)
+- [removeCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md)
- [updatePromotionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionRulesStep/index.html.md)
- [updatePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionsStep/index.html.md)
-- [createRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRegionsStep/index.html.md)
-- [deleteRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRegionsStep/index.html.md)
- [setRegionsPaymentProvidersStep](https://docs.medusajs.com/references/medusa-workflows/steps/setRegionsPaymentProvidersStep/index.html.md)
- [updateRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRegionsStep/index.html.md)
-- [deleteReservationsByLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md)
-- [createReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReservationsStep/index.html.md)
-- [deleteReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsStep/index.html.md)
-- [updateReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReservationsStep/index.html.md)
-- [createReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnReasonsStep/index.html.md)
-- [updateReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnReasonsStep/index.html.md)
-- [deleteReturnReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnReasonStep/index.html.md)
-- [listShippingOptionsForContextStep](https://docs.medusajs.com/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md)
-- [canDeleteSalesChannelsOrThrowStep](https://docs.medusajs.com/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md)
+- [createRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRegionsStep/index.html.md)
+- [deleteRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRegionsStep/index.html.md)
- [associateLocationsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateLocationsWithSalesChannelsStep/index.html.md)
- [associateProductsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateProductsWithSalesChannelsStep/index.html.md)
+- [canDeleteSalesChannelsOrThrowStep](https://docs.medusajs.com/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md)
+- [createSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createSalesChannelsStep/index.html.md)
- [createDefaultSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultSalesChannelStep/index.html.md)
- [detachLocationsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachLocationsFromSalesChannelsStep/index.html.md)
- [deleteSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteSalesChannelsStep/index.html.md)
-- [createSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createSalesChannelsStep/index.html.md)
- [detachProductsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachProductsFromSalesChannelsStep/index.html.md)
- [updateSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateSalesChannelsStep/index.html.md)
-- [deleteStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md)
-- [createStockLocations](https://docs.medusajs.com/references/medusa-workflows/steps/createStockLocations/index.html.md)
-- [updateStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md)
+- [createReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnReasonsStep/index.html.md)
+- [deleteReturnReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnReasonStep/index.html.md)
+- [updateReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnReasonsStep/index.html.md)
+- [createReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReservationsStep/index.html.md)
+- [deleteReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsStep/index.html.md)
+- [deleteReservationsByLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md)
+- [updateReservationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReservationsStep/index.html.md)
- [deleteShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteShippingProfilesStep/index.html.md)
+- [createStockLocations](https://docs.medusajs.com/references/medusa-workflows/steps/createStockLocations/index.html.md)
+- [deleteStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md)
+- [updateStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md)
+- [listShippingOptionsForContextStep](https://docs.medusajs.com/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md)
+- [createTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md)
- [createTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRegionsStep/index.html.md)
- [createTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRatesStep/index.html.md)
-- [createTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md)
- [deleteTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRateRulesStep/index.html.md)
- [deleteTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRatesStep/index.html.md)
- [deleteTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteTaxRegionsStep/index.html.md)
- [getItemTaxLinesStep](https://docs.medusajs.com/references/medusa-workflows/steps/getItemTaxLinesStep/index.html.md)
-- [updateTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRatesStep/index.html.md)
-- [listTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateRuleIdsStep/index.html.md)
-- [updateTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRegionsStep/index.html.md)
- [listTaxRateIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateIdsStep/index.html.md)
-- [updateUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateUsersStep/index.html.md)
-- [deleteUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteUsersStep/index.html.md)
+- [updateTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRatesStep/index.html.md)
+- [updateTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateTaxRegionsStep/index.html.md)
+- [listTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateRuleIdsStep/index.html.md)
+- [createPaymentAccountHolderStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentAccountHolderStep/index.html.md)
+- [createRefundReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRefundReasonStep/index.html.md)
+- [createPaymentSessionStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentSessionStep/index.html.md)
+- [deletePaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePaymentSessionsStep/index.html.md)
+- [deleteRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md)
+- [updatePaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePaymentCollectionStep/index.html.md)
+- [updateRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRefundReasonsStep/index.html.md)
+- [validateDeletedPaymentSessionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateDeletedPaymentSessionsStep/index.html.md)
- [createUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createUsersStep/index.html.md)
-- [createStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/createStoresStep/index.html.md)
+- [deleteUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteUsersStep/index.html.md)
+- [updateUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateUsersStep/index.html.md)
- [deleteStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStoresStep/index.html.md)
+- [createStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/createStoresStep/index.html.md)
- [updateStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStoresStep/index.html.md)
@@ -29987,20 +29988,172 @@ npx medusa --help
***
-# exec Command - Medusa CLI Reference
+# develop Command - Medusa CLI Reference
-Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md).
+Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application.
```bash
-npx medusa exec [file] [args...]
+npx medusa develop
+```
+
+## Options
+
+|Option|Description|Default|
+|---|---|---|---|---|
+|\`-H \\`|Set host of the Medusa server.|\`localhost\`|
+|\`-p \\`|Set port of the Medusa server.|\`9000\`|
+
+
+# build Command - Medusa CLI Reference
+
+Create a standalone build of the Medusa application.
+
+This creates a build that:
+
+- Doesn't rely on the source TypeScript files.
+- Can be copied to a production server reliably.
+
+The build is outputted to a new `.medusa/server` directory.
+
+```bash
+npx medusa build
+```
+
+Refer to [this section](#run-built-medusa-application) for next steps.
+
+## Options
+
+|Option|Description|
+|---|---|---|
+|\`--admin-only\`|Whether to only build the admin to host it separately. If this option is not passed, the admin is built to the |
+
+***
+
+## Run Built Medusa Application
+
+After running the `build` command, use the following step to run the built Medusa application:
+
+- Change to the `.medusa/server` directory and install the dependencies:
+
+```bash npm2yarn
+cd .medusa/server && npm install
+```
+
+- When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead.
+
+```bash npm2yarn
+cp .env .medusa/server/.env.production
+```
+
+- In the system environment variables, set `NODE_ENV` to `production`:
+
+```bash
+NODE_ENV=production
+```
+
+- Use the `start` command to run the application:
+
+```bash npm2yarn
+cd .medusa/server && npm run start
+```
+
+***
+
+## Build Medusa Admin
+
+By default, the Medusa Admin is built to the `.medusa/server/public/admin` directory.
+
+If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead.
+
+
+# new Command - Medusa CLI Reference
+
+Create a new Medusa application. Unlike the `create-medusa-app` CLI tool, this command provides more flexibility for experienced Medusa developers in creating and configuring their project.
+
+```bash
+medusa new [ []]
```
## Arguments
+|Argument|Description|Required|Default|
+|---|---|---|---|---|---|---|
+|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-|
+|\`starter\_url\`|The URL of the starter repository to create the project from.|No|\`https://github.com/medusajs/medusa-starter-default\`|
+
+## Options
+
+|Option|Description|
+|---|---|---|
+|\`-y\`|Skip all prompts, such as databaes prompts. A database might not be created if default PostgreSQL credentials don't work.|
+|\`--skip-db\`|Skip database creation.|
+|\`--skip-env\`|Skip populating |
+|\`--db-user \\`|The database user to use for database setup.|
+|\`--db-database \\`|The name of the database used for database setup.|
+|\`--db-pass \\`|The database password to use for database setup.|
+|\`--db-port \\`|The database port to use for database setup.|
+|\`--db-host \\`|The database host to use for database setup.|
+
+
+# plugin Commands - Medusa CLI Reference
+
+Commands starting with `plugin:` perform actions related to [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) development.
+
+These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
+
+## plugin:publish
+
+Publish a plugin into the local packages registry. The command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. You can then install the plugin in a local Medusa project using the [plugin:add](#pluginadd) command.
+
+```bash
+npx medusa plugin:publish
+```
+
+***
+
+## plugin:add
+
+Install the specified plugins from the local package registry into a local Medusa application. Plugins can be added to the local package registry using the [plugin:publish](#pluginpublish) command.
+
+```bash
+npx medusa plugin:add [names...]
+```
+
+### Arguments
+
|Argument|Description|Required|
|---|---|---|---|---|
-|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes|
-|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No|
+|\`names\`|The names of one or more plugins to install from the local package registry. A plugin's name is as specified in its |Yes|
+
+***
+
+## plugin:develop
+
+Start a development server for a plugin. The command will watch for changes in the plugin's source code and automatically re-publish the changes into the local package registry.
+
+```bash
+npx medusa plugin:develop
+```
+
+***
+
+## plugin:db:generate
+
+Generate migrations for all modules in a plugin.
+
+```bash
+npx medusa plugin:db:generate
+```
+
+***
+
+## plugin:build
+
+Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory.
+
+```bash
+npx medusa plugin:build
+```
# db Commands - Medusa CLI Reference
@@ -30123,74 +30276,28 @@ npx medusa db:sync-links
|\`--execute-all\`|Skip prompts when syncing links and execute all (including unsafe) actions.|No|Prompts are shown for unsafe actions, by default.|
-# build Command - Medusa CLI Reference
+# exec Command - Medusa CLI Reference
-Create a standalone build of the Medusa application.
-
-This creates a build that:
-
-- Doesn't rely on the source TypeScript files.
-- Can be copied to a production server reliably.
-
-The build is outputted to a new `.medusa/server` directory.
+Run a custom CLI script. Learn more about it in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/custom-cli-scripts/index.html.md).
```bash
-npx medusa build
+npx medusa exec [file] [args...]
```
-Refer to [this section](#run-built-medusa-application) for next steps.
+## Arguments
-## Options
+|Argument|Description|Required|
+|---|---|---|---|---|
+|\`file\`|The path to the TypeScript or JavaScript file holding the function to execute.|Yes|
+|\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No|
-|Option|Description|
-|---|---|---|
-|\`--admin-only\`|Whether to only build the admin to host it separately. If this option is not passed, the admin is built to the |
-***
+# start Command - Medusa CLI Reference
-## Run Built Medusa Application
-
-After running the `build` command, use the following step to run the built Medusa application:
-
-- Change to the `.medusa/server` directory and install the dependencies:
-
-```bash npm2yarn
-cd .medusa/server && npm install
-```
-
-- When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead.
-
-```bash npm2yarn
-cp .env .medusa/server/.env.production
-```
-
-- In the system environment variables, set `NODE_ENV` to `production`:
+Start the Medusa application in production.
```bash
-NODE_ENV=production
-```
-
-- Use the `start` command to run the application:
-
-```bash npm2yarn
-cd .medusa/server && npm run start
-```
-
-***
-
-## Build Medusa Admin
-
-By default, the Medusa Admin is built to the `.medusa/server/public/admin` directory.
-
-If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead.
-
-
-# develop Command - Medusa CLI Reference
-
-Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application.
-
-```bash
-npx medusa develop
+npx medusa start
```
## Options
@@ -30199,96 +30306,7 @@ npx medusa develop
|---|---|---|---|---|
|\`-H \\`|Set host of the Medusa server.|\`localhost\`|
|\`-p \\`|Set port of the Medusa server.|\`9000\`|
-
-
-# new Command - Medusa CLI Reference
-
-Create a new Medusa application. Unlike the `create-medusa-app` CLI tool, this command provides more flexibility for experienced Medusa developers in creating and configuring their project.
-
-```bash
-medusa new [ []]
-```
-
-## Arguments
-
-|Argument|Description|Required|Default|
-|---|---|---|---|---|---|---|
-|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-|
-|\`starter\_url\`|The URL of the starter repository to create the project from.|No|\`https://github.com/medusajs/medusa-starter-default\`|
-
-## Options
-
-|Option|Description|
-|---|---|---|
-|\`-y\`|Skip all prompts, such as databaes prompts. A database might not be created if default PostgreSQL credentials don't work.|
-|\`--skip-db\`|Skip database creation.|
-|\`--skip-env\`|Skip populating |
-|\`--db-user \\`|The database user to use for database setup.|
-|\`--db-database \\`|The name of the database used for database setup.|
-|\`--db-pass \\`|The database password to use for database setup.|
-|\`--db-port \\`|The database port to use for database setup.|
-|\`--db-host \\`|The database host to use for database setup.|
-
-
-# plugin Commands - Medusa CLI Reference
-
-Commands starting with `plugin:` perform actions related to [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) development.
-
-These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
-
-## plugin:publish
-
-Publish a plugin into the local packages registry. The command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. You can then install the plugin in a local Medusa project using the [plugin:add](#pluginadd) command.
-
-```bash
-npx medusa plugin:publish
-```
-
-***
-
-## plugin:add
-
-Install the specified plugins from the local package registry into a local Medusa application. Plugins can be added to the local package registry using the [plugin:publish](#pluginpublish) command.
-
-```bash
-npx medusa plugin:add [names...]
-```
-
-### Arguments
-
-|Argument|Description|Required|
-|---|---|---|---|---|
-|\`names\`|The names of one or more plugins to install from the local package registry. A plugin's name is as specified in its |Yes|
-
-***
-
-## plugin:develop
-
-Start a development server for a plugin. The command will watch for changes in the plugin's source code and automatically re-publish the changes into the local package registry.
-
-```bash
-npx medusa plugin:develop
-```
-
-***
-
-## plugin:db:generate
-
-Generate migrations for all modules in a plugin.
-
-```bash
-npx medusa plugin:db:generate
-```
-
-***
-
-## plugin:build
-
-Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory.
-
-```bash
-npx medusa plugin:build
-```
+|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.|
# telemetry Command - Medusa CLI Reference
@@ -30326,23 +30344,6 @@ npx medusa user --email [--password ]
If ran successfully, you'll receive the invite token in the output.|No|\`false\`|
-# start Command - Medusa CLI Reference
-
-Start the Medusa application in production.
-
-```bash
-npx medusa start
-```
-
-## Options
-
-|Option|Description|Default|
-|---|---|---|---|---|
-|\`-H \\`|Set host of the Medusa server.|\`localhost\`|
-|\`-p \\`|Set port of the Medusa server.|\`9000\`|
-|\`--cluster \\`|Start Medusa's Node.js server in |Cluster mode is disabled by default. If the option is passed but no number is passed, Medusa will try to consume all available CPU cores.|
-
-
# Medusa CLI Reference
The Medusa CLI tool provides commands that facilitate your development.
@@ -30366,6 +30367,112 @@ npx medusa --help
***
+# develop Command - Medusa CLI Reference
+
+Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application.
+
+```bash
+npx medusa develop
+```
+
+## Options
+
+|Option|Description|Default|
+|---|---|---|---|---|
+|\`-H \\`|Set host of the Medusa server.|\`localhost\`|
+|\`-p \\`|Set port of the Medusa server.|\`9000\`|
+
+
+# new Command - Medusa CLI Reference
+
+Create a new Medusa application. Unlike the `create-medusa-app` CLI tool, this command provides more flexibility for experienced Medusa developers in creating and configuring their project.
+
+```bash
+medusa new [ []]
+```
+
+## Arguments
+
+|Argument|Description|Required|Default|
+|---|---|---|---|---|---|---|
+|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-|
+|\`starter\_url\`|The URL of the starter repository to create the project from.|No|\`https://github.com/medusajs/medusa-starter-default\`|
+
+## Options
+
+|Option|Description|
+|---|---|---|
+|\`-y\`|Skip all prompts, such as databaes prompts. A database might not be created if default PostgreSQL credentials don't work.|
+|\`--skip-db\`|Skip database creation.|
+|\`--skip-env\`|Skip populating |
+|\`--db-user \\`|The database user to use for database setup.|
+|\`--db-database \\`|The name of the database used for database setup.|
+|\`--db-pass \\`|The database password to use for database setup.|
+|\`--db-port \\`|The database port to use for database setup.|
+|\`--db-host \\`|The database host to use for database setup.|
+
+
+# plugin Commands - Medusa CLI Reference
+
+Commands starting with `plugin:` perform actions related to [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) development.
+
+These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
+
+## plugin:publish
+
+Publish a plugin into the local packages registry. The command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. You can then install the plugin in a local Medusa project using the [plugin:add](#pluginadd) command.
+
+```bash
+npx medusa plugin:publish
+```
+
+***
+
+## plugin:add
+
+Install the specified plugins from the local package registry into a local Medusa application. Plugins can be added to the local package registry using the [plugin:publish](#pluginpublish) command.
+
+```bash
+npx medusa plugin:add [names...]
+```
+
+### Arguments
+
+|Argument|Description|Required|
+|---|---|---|---|---|
+|\`names\`|The names of one or more plugins to install from the local package registry. A plugin's name is as specified in its |Yes|
+
+***
+
+## plugin:develop
+
+Start a development server for a plugin. The command will watch for changes in the plugin's source code and automatically re-publish the changes into the local package registry.
+
+```bash
+npx medusa plugin:develop
+```
+
+***
+
+## plugin:db:generate
+
+Generate migrations for all modules in a plugin.
+
+```bash
+npx medusa plugin:db:generate
+```
+
+***
+
+## plugin:build
+
+Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory.
+
+```bash
+npx medusa plugin:build
+```
+
+
# db Commands - Medusa CLI Reference
Commands starting with `db:` perform actions on the database.
@@ -30486,110 +30593,66 @@ npx medusa db:sync-links
|\`--execute-all\`|Skip prompts when syncing links and execute all (including unsafe) actions.|No|Prompts are shown for unsafe actions, by default.|
-# develop Command - Medusa CLI Reference
+# build Command - Medusa CLI Reference
-Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application.
+Create a standalone build of the Medusa application.
+
+This creates a build that:
+
+- Doesn't rely on the source TypeScript files.
+- Can be copied to a production server reliably.
+
+The build is outputted to a new `.medusa/server` directory.
```bash
-npx medusa develop
+npx medusa build
```
-## Options
-
-|Option|Description|Default|
-|---|---|---|---|---|
-|\`-H \\`|Set host of the Medusa server.|\`localhost\`|
-|\`-p \\`|Set port of the Medusa server.|\`9000\`|
-
-
-# new Command - Medusa CLI Reference
-
-Create a new Medusa application. Unlike the `create-medusa-app` CLI tool, this command provides more flexibility for experienced Medusa developers in creating and configuring their project.
-
-```bash
-medusa new [ []]
-```
-
-## Arguments
-
-|Argument|Description|Required|Default|
-|---|---|---|---|---|---|---|
-|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-|
-|\`starter\_url\`|The URL of the starter repository to create the project from.|No|\`https://github.com/medusajs/medusa-starter-default\`|
+Refer to [this section](#run-built-medusa-application) for next steps.
## Options
|Option|Description|
|---|---|---|
-|\`-y\`|Skip all prompts, such as databaes prompts. A database might not be created if default PostgreSQL credentials don't work.|
-|\`--skip-db\`|Skip database creation.|
-|\`--skip-env\`|Skip populating |
-|\`--db-user \\`|The database user to use for database setup.|
-|\`--db-database \\`|The name of the database used for database setup.|
-|\`--db-pass \\`|The database password to use for database setup.|
-|\`--db-port \\`|The database port to use for database setup.|
-|\`--db-host \\`|The database host to use for database setup.|
+|\`--admin-only\`|Whether to only build the admin to host it separately. If this option is not passed, the admin is built to the |
+***
-# plugin Commands - Medusa CLI Reference
+## Run Built Medusa Application
-Commands starting with `plugin:` perform actions related to [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) development.
+After running the `build` command, use the following step to run the built Medusa application:
-These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0).
+- Change to the `.medusa/server` directory and install the dependencies:
-## plugin:publish
+```bash npm2yarn
+cd .medusa/server && npm install
+```
-Publish a plugin into the local packages registry. The command uses [Yalc](https://github.com/wclr/yalc) under the hood to publish the plugin to a local package registry. You can then install the plugin in a local Medusa project using the [plugin:add](#pluginadd) command.
+- When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead.
+
+```bash npm2yarn
+cp .env .medusa/server/.env.production
+```
+
+- In the system environment variables, set `NODE_ENV` to `production`:
```bash
-npx medusa plugin:publish
+NODE_ENV=production
+```
+
+- Use the `start` command to run the application:
+
+```bash npm2yarn
+cd .medusa/server && npm run start
```
***
-## plugin:add
+## Build Medusa Admin
-Install the specified plugins from the local package registry into a local Medusa application. Plugins can be added to the local package registry using the [plugin:publish](#pluginpublish) command.
+By default, the Medusa Admin is built to the `.medusa/server/public/admin` directory.
-```bash
-npx medusa plugin:add [names...]
-```
-
-### Arguments
-
-|Argument|Description|Required|
-|---|---|---|---|---|
-|\`names\`|The names of one or more plugins to install from the local package registry. A plugin's name is as specified in its |Yes|
-
-***
-
-## plugin:develop
-
-Start a development server for a plugin. The command will watch for changes in the plugin's source code and automatically re-publish the changes into the local package registry.
-
-```bash
-npx medusa plugin:develop
-```
-
-***
-
-## plugin:db:generate
-
-Generate migrations for all modules in a plugin.
-
-```bash
-npx medusa plugin:db:generate
-```
-
-***
-
-## plugin:build
-
-Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory.
-
-```bash
-npx medusa plugin:build
-```
+If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead.
# start Command - Medusa CLI Reference
@@ -30660,68 +30723,6 @@ npx medusa user --email [--password ]
If ran successfully, you'll receive the invite token in the output.|No|\`false\`|
-# build Command - Medusa CLI Reference
-
-Create a standalone build of the Medusa application.
-
-This creates a build that:
-
-- Doesn't rely on the source TypeScript files.
-- Can be copied to a production server reliably.
-
-The build is outputted to a new `.medusa/server` directory.
-
-```bash
-npx medusa build
-```
-
-Refer to [this section](#run-built-medusa-application) for next steps.
-
-## Options
-
-|Option|Description|
-|---|---|---|
-|\`--admin-only\`|Whether to only build the admin to host it separately. If this option is not passed, the admin is built to the |
-
-***
-
-## Run Built Medusa Application
-
-After running the `build` command, use the following step to run the built Medusa application:
-
-- Change to the `.medusa/server` directory and install the dependencies:
-
-```bash npm2yarn
-cd .medusa/server && npm install
-```
-
-- When running the application locally, make sure to copy the `.env` file from the root project's directory. In production, use system environment variables instead.
-
-```bash npm2yarn
-cp .env .medusa/server/.env.production
-```
-
-- In the system environment variables, set `NODE_ENV` to `production`:
-
-```bash
-NODE_ENV=production
-```
-
-- Use the `start` command to run the application:
-
-```bash npm2yarn
-cd .medusa/server && npm run start
-```
-
-***
-
-## Build Medusa Admin
-
-By default, the Medusa Admin is built to the `.medusa/server/public/admin` directory.
-
-If you want a separate build to host the admin standalone, such as on Vercel, pass the `--admin-only` option as explained in the [Options](#options) section. This outputs the admin to the `.medusa/admin` directory instead.
-
-
# Medusa JS SDK
In this documentation, you'll learn how to install and use Medusa's JS SDK.
@@ -32964,7 +32965,7 @@ A module link forms an association between two data models of different modules,
### Define a Link
-To define a link between your custom module and a commerce module, such as the Product Module:
+To define a link between your custom module and a Commerce Module, such as the Product Module:
1. Create the file `src/links/blog-product.ts` with the following content:
@@ -33797,7 +33798,7 @@ const { transaction } = await myLongRunningWorkflow(req.scope)
.run()
```
-2. In an API route, workflow, or other resource, change a step's status to successful using the [Worfklow Engine Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/workflow-engine/index.html.md):
+2. In an API route, workflow, or other resource, change a step's status to successful using the [Worfklow Engine Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/workflow-engine/index.html.md):
```ts highlights={stepSuccessHighlights}
const workflowEngineService = container.resolve(
@@ -33818,7 +33819,7 @@ await workflowEngineService.setStepSuccess({
})
```
-3. In an API route, workflow, or other resource, change a step's status to failure using the [Worfklow Engine Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/workflow-engine/index.html.md):
+3. In an API route, workflow, or other resource, change a step's status to failure using the [Worfklow Engine Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/workflow-engine/index.html.md):
```ts highlights={stepFailureHighlights}
const workflowEngineService = container.resolve(
@@ -34583,7 +34584,7 @@ npm run test:modules
## Commerce Modules
-Medusa provides all its commerce features as separate commerce modules, such as the Product or Order modules.
+Medusa provides all its commerce features as separate Commerce Modules, such as the Product or Order modules.
Refer to the [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) documentation for concepts and reference of every module's main service.
@@ -35172,800 +35173,11 @@ const user = await userModuleService.createUsers({
```
-# Implement Custom Line Item Pricing in Medusa
-
-In this guide, you'll learn how to add line items with custom prices to a cart in Medusa.
-
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box. These features include managing carts and adding line items to them.
-
-By default, you can add product variants to the cart, where the price of its associated line item is based on the product variant's price. However, you can build customizations to add line items with custom prices to the cart. This is useful when integrating an Enterprise Resource Planning (ERP), Product Information Management (PIM), or other third-party services that provide real-time prices for your products.
-
-To showcase how to add line items with custom prices to the cart, this guide uses [GoldAPI.io](https://www.goldapi.io) as an example of a third-party system that you can integrate for real-time prices. You can follow the same approach for other third-party integrations that provide custom pricing.
-
-You can follow this guide whether you're new to Medusa or an advanced Medusa developer.
-
-### Summary
-
-This guide will teach you how to:
-
-- Install and set up Medusa.
-- Integrate the third-party service [GoldAPI.io](https://www.goldapi.io) that retrieves real-time prices for metals like Gold and Silver.
-- Add an API route to add a product variant that has metals, such as a gold ring, to the cart with the real-time price retrieved from the third-party service.
-
-
-
-- [Custom Item Price Repository](https://github.com/medusajs/examples/tree/main/custom-item-price): Find the full code for this guide in this repository.
-- [OpenApi Specs for Postman](https://res.cloudinary.com/dza7lstvk/raw/upload/v1738246728/OpenApi/Custom_Item_Price_gdfnl3.yaml): Import this OpenApi Specs file into tools like Postman.
-
-***
-
-## Step 1: Install a Medusa Application
-
-### Prerequisites
-
-- [Node.js v20+](https://nodejs.org/en/download)
-- [Git CLI tool](https://git-scm.com/downloads)
-- [PostgreSQL](https://www.postgresql.org/download/)
-
-Start by installing the Medusa application on your machine with the following command:
-
-```bash
-npx create-medusa-app@latest
-```
-
-You'll first be asked for the project's name. You can also optionally choose to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md).
-
-Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name. If you chose to install the Next.js starter, it'll be installed in a separate directory with the `{project-name}-storefront` name.
-
-The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more about Medusa's architecture in [this documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md).
-
-Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard.
-
-Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help.
-
-***
-
-## Step 2: Integrate GoldAPI.io
-
-### Prerequisites
-
-- [GoldAPI.io Account. You can create a free account.](https://www.goldapi.io)
-
-To integrate third-party services into Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup.
-
-In this step, you'll create a Metal Price Module that uses the GoldAPI.io service to retrieve real-time prices for metals like Gold and Silver. You'll use this module later to retrieve the real-time price of a product variant based on the metals in it, and add it to the cart with that custom price.
-
-Learn more about modules in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md).
-
-### Create Module Directory
-
-A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/metal-prices`.
-
-
-
-### Create Module's Service
-
-You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to the database, which is useful if your module defines tables in the database, or connect to a third-party service.
-
-In this section, you'll create the Metal Prices Module's service that connects to the GoldAPI.io service to retrieve real-time prices for metals.
-
-Start by creating the file `src/modules/metal-prices/service.ts` with the following content:
-
-
-
-```ts title="src/modules/metal-prices/service.ts"
-type Options = {
- accessToken: string
- sandbox?: boolean
-}
-
-export default class MetalPricesModuleService {
- protected options_: Options
-
- constructor({}, options: Options) {
- this.options_ = options
- }
-}
-```
-
-A module can accept options that are passed to its service. You define an `Options` type that indicates the options the module accepts. It accepts two options:
-
-- `accessToken`: The access token for the GoldAPI.io service.
-- `sandbox`: A boolean that indicates whether to simulate sending requests to the GoldAPI.io service. This is useful when running in a test environment.
-
-The service's constructor receives the module's options as a second parameter. You store the options in the service's `options_` property.
-
-A module has a container of Medusa Framework tools and local resources in the module that you can access in the service constructor's first parameter. Learn more in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md).
-
-#### Add Method to Retrieve Metal Prices
-
-Next, you'll add the method to retrieve the metal prices from the third-party service.
-
-First, add the following types at the beginning of `src/modules/metal-prices/service.ts`:
-
-```ts title="src/modules/metal-prices/service.ts"
-export enum MetalSymbols {
- Gold = "XAU",
- Silver = "XAG",
- Platinum = "XPT",
- Palladium = "XPD"
-}
-
-export type PriceResponse = {
- metal: MetalSymbols
- currency: string
- exchange: string
- symbol: string
- price: number
- [key: string]: unknown
-}
-
-```
-
-The `MetalSymbols` enum defines the symbols for metals like Gold, Silver, Platinum, and Palladium. The `PriceResponse` type defines the structure of the response from the GoldAPI.io's endpoint.
-
-Next, add the method `getMetalPrices` to the `MetalPricesModuleService` class:
-
-```ts title="src/modules/metal-prices/service.ts"
-import { MedusaError } from "@medusajs/framework/utils"
-
-// ...
-
-export default class MetalPricesModuleService {
- // ...
- async getMetalPrice(
- symbol: MetalSymbols,
- currency: string
- ): Promise {
- const upperCaseSymbol = symbol.toUpperCase()
- const upperCaseCurrency = currency.toUpperCase()
-
- return fetch(`https://www.goldapi.io/api/${upperCaseSymbol}/${upperCaseCurrency}`, {
- headers: {
- "x-access-token": this.options_.accessToken,
- "Content-Type": "application/json",
- },
- redirect: "follow",
- }).then((response) => response.json())
- .then((response) => {
- if (response.error) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- response.error
- )
- }
-
- return response
- })
- }
-}
-```
-
-The `getMetalPrice` method accepts the metal symbol and currency as parameters. You send a request to GoldAPI.io's `/api/{symbol}/{currency}` endpoint to retrieve the metal's price, also passing the access token in the request's headers.
-
-If the response contains an error, you throw a `MedusaError` with the error message. Otherwise, you return the response, which is of type `PriceResponse`.
-
-#### Add Helper Methods
-
-You'll also add two helper methods to the `MetalPricesModuleService`. The first one is `getMetalSymbols` that returns the metal symbols as an array of strings:
-
-```ts title="src/modules/metal-prices/service.ts"
-export default class MetalPricesModuleService {
- // ...
- async getMetalSymbols(): Promise {
- return Object.values(MetalSymbols)
- }
-}
-```
-
-The second is `getMetalSymbol` that receives a name like `gold` and returns the corresponding metal symbol:
-
-```ts title="src/modules/metal-prices/service.ts"
-export default class MetalPricesModuleService {
- // ...
- async getMetalSymbol(name: string): Promise {
- const formattedName = name.charAt(0).toUpperCase() + name.slice(1).toLowerCase()
- return MetalSymbols[formattedName as keyof typeof MetalSymbols]
- }
-}
-```
-
-You'll use these methods in later steps.
-
-### Export Module Definition
-
-The final piece to a module is its definition, which you export in an `index.ts` file at its root directory. This definition tells Medusa the name of the module and its service.
-
-So, create the file `src/modules/metal-prices/index.ts` with the following content:
-
-
-
-```ts title="src/modules/metal-prices/index.ts"
-import { Module } from "@medusajs/framework/utils"
-import MetalPricesModuleService from "./service"
-
-export const METAL_PRICES_MODULE = "metal-prices"
-
-export default Module(METAL_PRICES_MODULE, {
- service: MetalPricesModuleService,
-})
-```
-
-You use the `Module` function from the Modules SDK to create the module's definition. It accepts two parameters:
-
-1. The module's name, which is `metal-prices`.
-2. An object with a required property `service` indicating the module's service.
-
-### Add Module to Medusa's Configurations
-
-Once you finish building the module, add it to Medusa's configurations to start using it.
-
-In `medusa-config.ts`, add a `modules` property and pass an array with your custom module:
-
-```ts title="medusa-config.ts"
-module.exports = defineConfig({
- // ...
- modules: [
- {
- resolve: "./src/modules/metal-prices",
- options: {
- accessToken: process.env.GOLD_API_TOKEN,
- sandbox: process.env.GOLD_API_SANDBOX === "true",
- },
- },
- ],
-})
-```
-
-Each object in the `modules` array has a `resolve` property, whose value is either a path to the module's directory, or an `npm` package’s name.
-
-The object also has an `options` property that accepts the module's options. You set the `accessToken` and `sandbox` options based on environment variables.
-
-You'll find the access token at the top of your GoldAPI.io dashboard.
-
-
-
-Set the access token as an environment variable in `.env`:
-
-```bash
-GOLD_API_TOKEN=
-```
-
-You'll start using the module in the next steps.
-
-***
-
-## Step 3: Add Custom Item to Cart Workflow
-
-In this section, you'll implement the logic to retrieve the real-time price of a variant based on the metals in it, then add the variant to the cart with the custom price. You'll implement this logic in a workflow.
-
-A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. Then, you execute the workflow from other customizations, such as in an endpoint.
-
-Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md)
-
-The workflow you'll implement in this section has the following steps:
-
-- [useQueryGraphStep (Retrieve Cart)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the cart's ID and currency using Query.
-- [useQueryGraphStep (Retrieve Variant)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the variant's details using Query
-- [getVariantMetalPricesStep](#getvariantmetalpricesstep): Retrieve the variant's price using the third-party service.
-- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md): Add the item with the custom price to the cart.
-- [useQueryGraphStep (Retrieve Cart)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the updated cart's details using Query.
-
-`useQueryGraphStep` and `addToCartWorkflow` are available through Medusa's core workflows package. You'll only implement the `getVariantMetalPricesStep`.
-
-### getVariantMetalPricesStep
-
-The `getVariantMetalPricesStep` will retrieve the real-time metal price of a variant received as an input.
-
-To create the step, create the file `src/workflows/steps/get-variant-metal-prices.ts` with the following content:
-
-
-
-```ts title="src/workflows/steps/get-variant-metal-prices.ts"
-import { createStep } from "@medusajs/framework/workflows-sdk"
-import { ProductVariantDTO } from "@medusajs/framework/types"
-import { METAL_PRICES_MODULE } from "../../modules/metal-prices"
-import MetalPricesModuleService from "../../modules/metal-prices/service"
-
-export type GetVariantMetalPricesStepInput = {
- variant: ProductVariantDTO & {
- calculated_price?: {
- calculated_amount: number
- }
- }
- currencyCode: string
- quantity?: number
-}
-
-export const getVariantMetalPricesStep = createStep(
- "get-variant-metal-prices",
- async ({
- variant,
- currencyCode,
- quantity = 1,
- }: GetVariantMetalPricesStepInput, { container }) => {
- const metalPricesModuleService: MetalPricesModuleService =
- container.resolve(METAL_PRICES_MODULE)
-
- // TODO
- }
-)
-```
-
-You create a step with `createStep` from the Workflows SDK. It accepts two parameters:
-
-1. The step's unique name, which is `get-variant-metal-prices`.
-2. An async function that receives two parameters:
- - An input object with the variant, currency code, and quantity. The variant has a `calculated_price` property that holds the variant's fixed price in the Medusa application. This is useful when you want to add a fixed price to the real-time custom price, such as handling fees.
- - The [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of Framework and commerce tools that you can access in the step.
-
-In the step function, so far you only resolve the Metal Prices Module's service from the Medusa container.
-
-Next, you'll validate that the specified variant can have its price calculated. Add the following import at the top of the file:
-
-```ts title="src/workflows/steps/get-variant-metal-prices.ts"
-import { MedusaError } from "@medusajs/framework/utils"
-```
-
-And replace the `TODO` in the step function with the following:
-
-```ts title="src/workflows/steps/get-variant-metal-prices.ts"
-const variantMetal = variant.options.find(
- (option) => option.option?.title === "Metal"
-)?.value
-const metalSymbol = await metalPricesModuleService
- .getMetalSymbol(variantMetal || "")
-
-if (!metalSymbol) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- "Variant doesn't have metal. Make sure the variant's SKU matches a metal symbol."
- )
-}
-
-if (!variant.weight) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- "Variant doesn't have weight. Make sure the variant has weight to calculate its price."
- )
-}
-
-// TODO retrieve custom price
-```
-
-In the code above, you first retrieve the metal option's value from the variant's options, assuming that a variant has metals if it has a `Metal` option. Then, you retrieve the metal symbol of the option's value using the `getMetalSymbol` method of the Metal Prices Module's service.
-
-If the variant doesn't have a metal in its options, the option's value is not valid, or the variant doesn't have a weight, you throw an error. The weight is necessary to calculate the price based on the metal's price per weight.
-
-Next, you'll retrieve the real-time price of the metal using the third-party service. Replace the `TODO` with the following:
-
-```ts title="src/workflows/steps/get-variant-metal-prices.ts"
-let price = variant.calculated_price?.calculated_amount || 0
-const weight = variant.weight
-const { price: metalPrice } = await metalPricesModuleService.getMetalPrice(
- metalSymbol as MetalSymbols, currencyCode
-)
-price += (metalPrice * weight * quantity)
-
-return new StepResponse(price)
-```
-
-In the code above, you first set the price to the variant's fixed price, if it has one. Then, you retrieve the metal's price using the `getMetalPrice` method of the Metal Prices Module's service.
-
-Finally, you calculate the price by multiplying the metal's price by the variant's weight and the quantity to add to the cart, then add the fixed price to it.
-
-Every step must return a `StepResponse` instance. The `StepResponse` constructor accepts the step's output as a parameter, which in this case is the variant's price.
-
-### Create addCustomToCartWorkflow
-
-Now that you have the `getVariantMetalPricesStep`, you can create the workflow that adds the item with custom pricing to the cart.
-
-Create the file `src/workflows/add-custom-to-cart.ts` with the following content:
-
-
-
-```ts title="src/workflows/add-custom-to-cart.ts" highlights={workflowHighlights}
-import { createWorkflow } from "@medusajs/framework/workflows-sdk"
-import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
-import { QueryContext } from "@medusajs/framework/utils"
-
-type AddCustomToCartWorkflowInput = {
- cart_id: string
- item: {
- variant_id: string
- quantity: number
- metadata?: Record
- }
-}
-
-export const addCustomToCartWorkflow = createWorkflow(
- "add-custom-to-cart",
- ({ cart_id, item }: AddCustomToCartWorkflowInput) => {
- // @ts-ignore
- const { data: carts } = useQueryGraphStep({
- entity: "cart",
- filters: { id: cart_id },
- fields: ["id", "currency_code"],
- })
-
- const { data: variants } = useQueryGraphStep({
- entity: "variant",
- fields: [
- "*",
- "options.*",
- "options.option.*",
- "calculated_price.*",
- ],
- filters: {
- id: item.variant_id,
- },
- options: {
- throwIfKeyNotFound: true,
- },
- context: {
- calculated_price: QueryContext({
- currency_code: carts[0].currency_code,
- }),
- },
- }).config({ name: "retrieve-variant" })
-
- // TODO add more steps
- }
-)
-```
-
-You create a workflow with `createWorkflow` from the Workflows SDK. It accepts two parameters:
-
-1. The workflow's unique name, which is `add-custom-to-cart`.
-2. A function that receives an input object with the cart's ID and the item to add to the cart. The item has the variant's ID, quantity, and optional metadata.
-
-In the function, you first retrieve the cart's details using the `useQueryGraphStep` helper step. This step uses [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) which is a Modules SDK tool that retrieves data across modules. You use it to retrieve the cart's ID and currency code.
-
-You also retrieve the variant's details using the `useQueryGraphStep` helper step. You pass the variant's ID to the step's filters and specify the fields to retrieve. To retrieve the variant's price based on the cart's context, you pass the cart's currency code to the `calculated_price` context.
-
-Next, you'll retrieve the variant's real-time price using the `getVariantMetalPricesStep` you created earlier. First, add the following import:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-import {
- getVariantMetalPricesStep,
- GetVariantMetalPricesStepInput,
-} from "./steps/get-variant-metal-prices"
-```
-
-Then, replace the `TODO` in the workflow with the following:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-const price = getVariantMetalPricesStep({
- variant: variants[0],
- currencyCode: carts[0].currency_code,
- quantity: item.quantity,
-} as unknown as GetVariantMetalPricesStepInput)
-
-// TODO add item with custom price to cart
-```
-
-You execute the `getVariantMetalPricesStep` passing it the variant's details, the cart's currency code, and the quantity of the item to add to the cart. The step returns the variant's custom price.
-
-Next, you'll add the item with the custom price to the cart. First, add the following imports at the top of the file:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-import { transform } from "@medusajs/framework/workflows-sdk"
-import { addToCartWorkflow } from "@medusajs/medusa/core-flows"
-```
-
-Then, replace the `TODO` in the workflow with the following:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-const itemToAdd = transform({
- item,
- price,
-}, (data) => {
- return [{
- ...data.item,
- unit_price: data.price,
- }]
-})
-
-addToCartWorkflow.runAsStep({
- input: {
- items: itemToAdd,
- cart_id,
- },
-})
-
-// TODO retrieve and return cart
-```
-
-You prepare the item to add to the cart using `transform` from the Workflows SDK. It allows you to manipulate and create variables in a workflow. After that, you use Medusa's `addToCartWorkflow` to add the item with the custom price to the cart.
-
-A workflow's constructor function has some constraints in implementation, which is why you need to use `transform` for variable manipulation. Learn more about these constraints in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/constructor-constraints/index.html.md).
-
-Lastly, you'll retrieve the cart's details again and return them. Add the following import at the beginning of the file:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-import { WorkflowResponse } from "@medusajs/framework/workflows-sdk"
-```
-
-And replace the last `TODO` in the workflow with the following:
-
-```ts title="src/workflows/add-custom-to-cart.ts"
-// @ts-ignore
-const { data: updatedCarts } = useQueryGraphStep({
- entity: "cart",
- filters: { id: cart_id },
- fields: ["id", "items.*"],
-}).config({ name: "refetch-cart" })
-
-return new WorkflowResponse({
- cart: updatedCarts[0],
-})
-```
-
-In the code above, you retrieve the updated cart's details using the `useQueryGraphStep` helper step. To return data from the workflow, you create and return a `WorkflowResponse` instance. It accepts as a parameter the data to return, which is the updated cart.
-
-In the next step, you'll use the workflow in a custom route to add an item with a custom price to the cart.
-
-***
-
-## Step 4: Create Add Custom Item to Cart API Route
-
-Now that you've implemented the logic to add an item with a custom price to the cart, you'll expose this functionality in an API route.
-
-An API Route is an endpoint that exposes commerce features to external applications and clients, such as storefronts. You'll create an API route at the path `/store/carts/:id/line-items-metals` that executes the workflow from the previous step to add a product variant with custom price to the cart.
-
-Learn more about API routes in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md).
-
-### Create API Route
-
-An API route is created in a `route.ts` file under a sub-directory of the `src/api` directory.
-
-The path of the API route is the file's path relative to `src/api`. So, to create the `/store/carts/:id/line-items-metals` API route, create the file `src/api/store/carts/[id]/line-items-metals/route.ts` with the following content:
-
-
-
-```ts title="src/api/store/carts/[id]/line-items-metals/route.ts"
-import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
-import { HttpTypes } from "@medusajs/framework/types"
-import { addCustomToCartWorkflow } from "../../../../../workflows/add-custom-to-cart"
-
-export const POST = async (
- req: MedusaRequest,
- res: MedusaResponse
-) => {
- const { id } = req.params
- const item = req.validatedBody
-
- const { result } = await addCustomToCartWorkflow(req.scope)
- .run({
- input: {
- cart_id: id,
- item,
- },
- })
-
- res.status(200).json({ cart: result.cart })
-}
-```
-
-Since you export a `POST` function in this file, you're exposing a `POST` API route at `/store/carts/:id/line-items-metals`. The route handler function accepts two parameters:
-
-1. A request object with details and context on the request, such as path and body parameters.
-2. A response object to manipulate and send the response.
-
-In the function, you retrieve the cart's ID from the path parameter, and the item's details from the request body. This API route will accept the same request body parameters as Medusa's [Add Item to Cart API Route](https://docs.medusajs.com/api/store#carts_postcartsidlineitems).
-
-Then, you execute the `addCustomToCartWorkflow` by invoking it, passing it the Medusa container, which is available in the request's `scope` property, then executing its `run` method. You pass the workflow's input object with the cart's ID and the item to add to the cart.
-
-Finally, you return a response with the updated cart's details.
-
-### Add Request Body Validation Middleware
-
-To ensure that the request body contains the required parameters, you'll add a middleware that validates the incoming request's body based on a defined schema.
-
-A middleware is a function executed before the API route when a request is sent to it. You define middlewares in Medusa in the `src/api/middlewares.ts` directory.
-
-Learn more about middlewares in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md).
-
-To add a validation middleware to the custom API route, create the file `src/api/middlewares.ts` with the following content:
-
-
-
-```ts title="src/api/middlewares.ts"
-import {
- defineMiddlewares,
- validateAndTransformBody,
-} from "@medusajs/framework/http"
-import {
- StoreAddCartLineItem,
-} from "@medusajs/medusa/api/store/carts/validators"
-
-export default defineMiddlewares({
- routes: [
- {
- matcher: "/store/carts/:id/line-items-metals",
- method: "POST",
- middlewares: [
- validateAndTransformBody(
- StoreAddCartLineItem
- ),
- ],
- },
- ],
-})
-```
-
-In this file, you export the middlewares definition using `defineMiddlewares` from the Medusa Framework. This function accepts an object having a `routes` property, which is an array of middleware configurations to apply on routes.
-
-You pass in the `routes` array an object having the following properties:
-
-- `matcher`: The route to apply the middleware on.
-- `method`: The HTTP method to apply the middleware on for the specified API route.
-- `middlewares`: An array of the middlewares to apply. You apply the `validateAndTransformBody` middleware, which validates the request body based on the `StoreAddCartLineItem` schema. This validation schema is the same schema used for Medusa's [Add Item to Cart API Route](https://docs.medusajs.com/api/store#carts_postcartsidlineitems).
-
-Any request sent to the `/store/carts/:id/line-items-metals` API route will now fail if it doesn't have the required parameters.
-
-Learn more about API route validation in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/validation/index.html.md).
-
-### Prepare to Test API Route
-
-Before you test the API route, you'll prepare and retrieve the necessary data to add a product variant with a custom price to the cart.
-
-#### Create Product with Metal Variant
-
-You'll first create a product that has a `Metal` option, and variant(s) with values for this option.
-
-Start the Medusa application with the following command:
-
-```bash npm2yarn
-npm run dev
-```
-
-Then, open the Medusa Admin dashboard at `localhost:9000/app` and log in with the email and password you created when you installed the Medusa application in the first step.
-
-Once you log in, click on Products in the sidebar, then click the Create button at the top right.
-
-
-
-Then, in the Create Product form:
-
-1. Enter a name for the product, and optionally enter other details like description.
-2. Enable the "Yes, this is a product with variants" toggle.
-3. Under Product Options, enter "Metal" for the title, and enter "Gold" for the values.
-
-Once you're done, click the Continue button.
-
-
-
-You can skip the next two steps by clicking the Continue button again, then the Publish button.
-
-Once you're done, the product's page will open. You'll now add weight to the product's Gold variant. To do that:
-
-- Scroll to the Variants section and find the Gold variant.
-- Click on the three-dots icon at its right.
-- Choose "Edit" from the dropdown.
-
-
-
-In the side window that opens, find the Weight field, enter the weight, and click the Save button.
-
-
-
-Finally, you need to set fixed prices for the variant, even if they're just `0`. To do that:
-
-1. Click on the three-dots icon at the top right of the Variants section.
-2. Choose "Edit Prices" from the dropdown.
-
-
-
-For each cell in the table, either enter a fixed price for the specified currency or leave it as `0`. Once you're done, click the Save button.
-
-
-
-You'll use this variant to add it to the cart later. You can find its ID by clicking on the variant, opening its details page. Then, on the details page, click on the icon at the right of the JSON section, and copy the ID from the JSON data.
-
-
-
-#### Retrieve Publishable API Key
-
-All requests sent to API routes starting with `/store` must have a publishable API key in the header. This ensures the request's operations are scoped to the publishable API key's associated sales channels. For example, products that aren't available in a cart's sales channel can't be added to it.
-
-To retrieve the publishable API key, on the Medusa Admin:
-
-1. Click on Settings in the sidebar at the bottom left.
-2. Click on Publishable API Keys from the sidebar, then click on a publishable API key in the list.
-
-
-
-3. Click on the publishable API key to copy it.
-
-
-
-You'll use this key when you test the API route.
-
-### Test API Route
-
-To test out the API route, you need to create a cart. A cart must be associated with a region. So, to retrieve the ID of a region in your store, send a `GET` request to the `/store/regions` API route:
-
-```bash
-curl 'localhost:9000/store/regions' \
--H 'x-publishable-api-key: {api_key}'
-```
-
-Make sure to replace `{api_key}` with the publishable API key you copied earlier.
-
-This will return a list of regions. Copy the ID of one of the regions.
-
-Then, send a `POST` request to the `/store/carts` API route to create a cart:
-
-```bash
-curl -X POST 'localhost:9000/store/carts' \
--H 'x-publishable-api-key: {api_key}' \
--H 'Content-Type: application/json' \
---data '{
- "region_id": "{region_id}"
-}'
-```
-
-Make sure to replace `{api_key}` with the publishable API key you copied earlier, and `{region_id}` with the ID of a region from the previous request.
-
-This will return the created cart. Copy the ID of the cart to use it next.
-
-Finally, to add the Gold variant to the cart with a custom price, send a `POST` request to the `/store/carts/:id/line-items-metals` API route:
-
-```bash
-curl -X POST 'localhost:9000/store/carts/{cart_id}/line-items-metals' \
--H 'x-publishable-api-key: {api_key}' \
--H 'Content-Type: application/json' \
---data '{
- "variant_id": "{variant_id}",
- "quantity": 1
-}'
-```
-
-Make sure to replace:
-
-- `{api_key}` with the publishable API key you copied earlier.
-- `{cart_id}` with the ID of the cart you created.
-- `{variant_id}` with the ID of the Gold variant you created.
-
-This will return the cart's details, where you can see in its `items` array the item with the custom price:
-
-```json title="Example Response"
-{
- "cart": {
- "items": [
- {
- "variant_id": "{variant_id}",
- "quantity": 1,
- "is_custom_price": true,
- // example custom price
- "unit_price": 2000
- }
- ]
- }
-}
-```
-
-The price will be the result of the calculation you've implemented earlier, which is the fixed price of the variant plus the real-time price of the metal, multiplied by the weight of the variant and the quantity added to the cart.
-
-This price will be reflected in the cart's total price, and you can proceed to checkout with the custom-priced item.
-
-***
-
-## Next Steps
-
-You've now implemented custom item pricing in Medusa. You can also customize the [storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md) to use the new API route to add custom-priced items to the cart.
-
-If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more.
-
-To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md).
-
-
# Implement Quote Management in Medusa
In this guide, you'll learn how to implement quote management in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box.
By default, the Medusa application provides standard commerce features for orders and carts. However, Medusa's customization capabilities facilitate extending existing features to implement quote-management features.
@@ -39842,6 +39054,795 @@ If you're new to Medusa, check out the [main documentation](https://docs.medusaj
To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md).
+# Implement Custom Line Item Pricing in Medusa
+
+In this guide, you'll learn how to add line items with custom prices to a cart in Medusa.
+
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box. These features include managing carts and adding line items to them.
+
+By default, you can add product variants to the cart, where the price of its associated line item is based on the product variant's price. However, you can build customizations to add line items with custom prices to the cart. This is useful when integrating an Enterprise Resource Planning (ERP), Product Information Management (PIM), or other third-party services that provide real-time prices for your products.
+
+To showcase how to add line items with custom prices to the cart, this guide uses [GoldAPI.io](https://www.goldapi.io) as an example of a third-party system that you can integrate for real-time prices. You can follow the same approach for other third-party integrations that provide custom pricing.
+
+You can follow this guide whether you're new to Medusa or an advanced Medusa developer.
+
+### Summary
+
+This guide will teach you how to:
+
+- Install and set up Medusa.
+- Integrate the third-party service [GoldAPI.io](https://www.goldapi.io) that retrieves real-time prices for metals like Gold and Silver.
+- Add an API route to add a product variant that has metals, such as a gold ring, to the cart with the real-time price retrieved from the third-party service.
+
+
+
+- [Custom Item Price Repository](https://github.com/medusajs/examples/tree/main/custom-item-price): Find the full code for this guide in this repository.
+- [OpenApi Specs for Postman](https://res.cloudinary.com/dza7lstvk/raw/upload/v1738246728/OpenApi/Custom_Item_Price_gdfnl3.yaml): Import this OpenApi Specs file into tools like Postman.
+
+***
+
+## Step 1: Install a Medusa Application
+
+### Prerequisites
+
+- [Node.js v20+](https://nodejs.org/en/download)
+- [Git CLI tool](https://git-scm.com/downloads)
+- [PostgreSQL](https://www.postgresql.org/download/)
+
+Start by installing the Medusa application on your machine with the following command:
+
+```bash
+npx create-medusa-app@latest
+```
+
+You'll first be asked for the project's name. You can also optionally choose to install the [Next.js starter storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md).
+
+Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name. If you chose to install the Next.js starter, it'll be installed in a separate directory with the `{project-name}-storefront` name.
+
+The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more about Medusa's architecture in [this documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md).
+
+Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credentials and submit the form. Afterwards, you can log in with the new user and explore the dashboard.
+
+Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help.
+
+***
+
+## Step 2: Integrate GoldAPI.io
+
+### Prerequisites
+
+- [GoldAPI.io Account. You can create a free account.](https://www.goldapi.io)
+
+To integrate third-party services into Medusa, you create a custom module. A module is a reusable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup.
+
+In this step, you'll create a Metal Price Module that uses the GoldAPI.io service to retrieve real-time prices for metals like Gold and Silver. You'll use this module later to retrieve the real-time price of a product variant based on the metals in it, and add it to the cart with that custom price.
+
+Learn more about modules in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md).
+
+### Create Module Directory
+
+A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/metal-prices`.
+
+
+
+### Create Module's Service
+
+You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to the database, which is useful if your module defines tables in the database, or connect to a third-party service.
+
+In this section, you'll create the Metal Prices Module's service that connects to the GoldAPI.io service to retrieve real-time prices for metals.
+
+Start by creating the file `src/modules/metal-prices/service.ts` with the following content:
+
+
+
+```ts title="src/modules/metal-prices/service.ts"
+type Options = {
+ accessToken: string
+ sandbox?: boolean
+}
+
+export default class MetalPricesModuleService {
+ protected options_: Options
+
+ constructor({}, options: Options) {
+ this.options_ = options
+ }
+}
+```
+
+A module can accept options that are passed to its service. You define an `Options` type that indicates the options the module accepts. It accepts two options:
+
+- `accessToken`: The access token for the GoldAPI.io service.
+- `sandbox`: A boolean that indicates whether to simulate sending requests to the GoldAPI.io service. This is useful when running in a test environment.
+
+The service's constructor receives the module's options as a second parameter. You store the options in the service's `options_` property.
+
+A module has a container of Medusa Framework tools and local resources in the module that you can access in the service constructor's first parameter. Learn more in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/container/index.html.md).
+
+#### Add Method to Retrieve Metal Prices
+
+Next, you'll add the method to retrieve the metal prices from the third-party service.
+
+First, add the following types at the beginning of `src/modules/metal-prices/service.ts`:
+
+```ts title="src/modules/metal-prices/service.ts"
+export enum MetalSymbols {
+ Gold = "XAU",
+ Silver = "XAG",
+ Platinum = "XPT",
+ Palladium = "XPD"
+}
+
+export type PriceResponse = {
+ metal: MetalSymbols
+ currency: string
+ exchange: string
+ symbol: string
+ price: number
+ [key: string]: unknown
+}
+
+```
+
+The `MetalSymbols` enum defines the symbols for metals like Gold, Silver, Platinum, and Palladium. The `PriceResponse` type defines the structure of the response from the GoldAPI.io's endpoint.
+
+Next, add the method `getMetalPrices` to the `MetalPricesModuleService` class:
+
+```ts title="src/modules/metal-prices/service.ts"
+import { MedusaError } from "@medusajs/framework/utils"
+
+// ...
+
+export default class MetalPricesModuleService {
+ // ...
+ async getMetalPrice(
+ symbol: MetalSymbols,
+ currency: string
+ ): Promise {
+ const upperCaseSymbol = symbol.toUpperCase()
+ const upperCaseCurrency = currency.toUpperCase()
+
+ return fetch(`https://www.goldapi.io/api/${upperCaseSymbol}/${upperCaseCurrency}`, {
+ headers: {
+ "x-access-token": this.options_.accessToken,
+ "Content-Type": "application/json",
+ },
+ redirect: "follow",
+ }).then((response) => response.json())
+ .then((response) => {
+ if (response.error) {
+ throw new MedusaError(
+ MedusaError.Types.INVALID_DATA,
+ response.error
+ )
+ }
+
+ return response
+ })
+ }
+}
+```
+
+The `getMetalPrice` method accepts the metal symbol and currency as parameters. You send a request to GoldAPI.io's `/api/{symbol}/{currency}` endpoint to retrieve the metal's price, also passing the access token in the request's headers.
+
+If the response contains an error, you throw a `MedusaError` with the error message. Otherwise, you return the response, which is of type `PriceResponse`.
+
+#### Add Helper Methods
+
+You'll also add two helper methods to the `MetalPricesModuleService`. The first one is `getMetalSymbols` that returns the metal symbols as an array of strings:
+
+```ts title="src/modules/metal-prices/service.ts"
+export default class MetalPricesModuleService {
+ // ...
+ async getMetalSymbols(): Promise {
+ return Object.values(MetalSymbols)
+ }
+}
+```
+
+The second is `getMetalSymbol` that receives a name like `gold` and returns the corresponding metal symbol:
+
+```ts title="src/modules/metal-prices/service.ts"
+export default class MetalPricesModuleService {
+ // ...
+ async getMetalSymbol(name: string): Promise {
+ const formattedName = name.charAt(0).toUpperCase() + name.slice(1).toLowerCase()
+ return MetalSymbols[formattedName as keyof typeof MetalSymbols]
+ }
+}
+```
+
+You'll use these methods in later steps.
+
+### Export Module Definition
+
+The final piece to a module is its definition, which you export in an `index.ts` file at its root directory. This definition tells Medusa the name of the module and its service.
+
+So, create the file `src/modules/metal-prices/index.ts` with the following content:
+
+
+
+```ts title="src/modules/metal-prices/index.ts"
+import { Module } from "@medusajs/framework/utils"
+import MetalPricesModuleService from "./service"
+
+export const METAL_PRICES_MODULE = "metal-prices"
+
+export default Module(METAL_PRICES_MODULE, {
+ service: MetalPricesModuleService,
+})
+```
+
+You use the `Module` function from the Modules SDK to create the module's definition. It accepts two parameters:
+
+1. The module's name, which is `metal-prices`.
+2. An object with a required property `service` indicating the module's service.
+
+### Add Module to Medusa's Configurations
+
+Once you finish building the module, add it to Medusa's configurations to start using it.
+
+In `medusa-config.ts`, add a `modules` property and pass an array with your custom module:
+
+```ts title="medusa-config.ts"
+module.exports = defineConfig({
+ // ...
+ modules: [
+ {
+ resolve: "./src/modules/metal-prices",
+ options: {
+ accessToken: process.env.GOLD_API_TOKEN,
+ sandbox: process.env.GOLD_API_SANDBOX === "true",
+ },
+ },
+ ],
+})
+```
+
+Each object in the `modules` array has a `resolve` property, whose value is either a path to the module's directory, or an `npm` package’s name.
+
+The object also has an `options` property that accepts the module's options. You set the `accessToken` and `sandbox` options based on environment variables.
+
+You'll find the access token at the top of your GoldAPI.io dashboard.
+
+
+
+Set the access token as an environment variable in `.env`:
+
+```bash
+GOLD_API_TOKEN=
+```
+
+You'll start using the module in the next steps.
+
+***
+
+## Step 3: Add Custom Item to Cart Workflow
+
+In this section, you'll implement the logic to retrieve the real-time price of a variant based on the metals in it, then add the variant to the cart with the custom price. You'll implement this logic in a workflow.
+
+A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow like you construct a function, but it's a special function that allows you to track its executions' progress, define roll-back logic, and configure other advanced features. Then, you execute the workflow from other customizations, such as in an endpoint.
+
+Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md)
+
+The workflow you'll implement in this section has the following steps:
+
+- [useQueryGraphStep (Retrieve Cart)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the cart's ID and currency using Query.
+- [useQueryGraphStep (Retrieve Variant)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the variant's details using Query
+- [getVariantMetalPricesStep](#getvariantmetalpricesstep): Retrieve the variant's price using the third-party service.
+- [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md): Add the item with the custom price to the cart.
+- [useQueryGraphStep (Retrieve Cart)](https://docs.medusajs.com/references/helper-steps/useQueryGraphStep/index.html.md): Retrieve the updated cart's details using Query.
+
+`useQueryGraphStep` and `addToCartWorkflow` are available through Medusa's core workflows package. You'll only implement the `getVariantMetalPricesStep`.
+
+### getVariantMetalPricesStep
+
+The `getVariantMetalPricesStep` will retrieve the real-time metal price of a variant received as an input.
+
+To create the step, create the file `src/workflows/steps/get-variant-metal-prices.ts` with the following content:
+
+
+
+```ts title="src/workflows/steps/get-variant-metal-prices.ts"
+import { createStep } from "@medusajs/framework/workflows-sdk"
+import { ProductVariantDTO } from "@medusajs/framework/types"
+import { METAL_PRICES_MODULE } from "../../modules/metal-prices"
+import MetalPricesModuleService from "../../modules/metal-prices/service"
+
+export type GetVariantMetalPricesStepInput = {
+ variant: ProductVariantDTO & {
+ calculated_price?: {
+ calculated_amount: number
+ }
+ }
+ currencyCode: string
+ quantity?: number
+}
+
+export const getVariantMetalPricesStep = createStep(
+ "get-variant-metal-prices",
+ async ({
+ variant,
+ currencyCode,
+ quantity = 1,
+ }: GetVariantMetalPricesStepInput, { container }) => {
+ const metalPricesModuleService: MetalPricesModuleService =
+ container.resolve(METAL_PRICES_MODULE)
+
+ // TODO
+ }
+)
+```
+
+You create a step with `createStep` from the Workflows SDK. It accepts two parameters:
+
+1. The step's unique name, which is `get-variant-metal-prices`.
+2. An async function that receives two parameters:
+ - An input object with the variant, currency code, and quantity. The variant has a `calculated_price` property that holds the variant's fixed price in the Medusa application. This is useful when you want to add a fixed price to the real-time custom price, such as handling fees.
+ - The [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md), which is a registry of Framework and commerce tools that you can access in the step.
+
+In the step function, so far you only resolve the Metal Prices Module's service from the Medusa container.
+
+Next, you'll validate that the specified variant can have its price calculated. Add the following import at the top of the file:
+
+```ts title="src/workflows/steps/get-variant-metal-prices.ts"
+import { MedusaError } from "@medusajs/framework/utils"
+```
+
+And replace the `TODO` in the step function with the following:
+
+```ts title="src/workflows/steps/get-variant-metal-prices.ts"
+const variantMetal = variant.options.find(
+ (option) => option.option?.title === "Metal"
+)?.value
+const metalSymbol = await metalPricesModuleService
+ .getMetalSymbol(variantMetal || "")
+
+if (!metalSymbol) {
+ throw new MedusaError(
+ MedusaError.Types.INVALID_DATA,
+ "Variant doesn't have metal. Make sure the variant's SKU matches a metal symbol."
+ )
+}
+
+if (!variant.weight) {
+ throw new MedusaError(
+ MedusaError.Types.INVALID_DATA,
+ "Variant doesn't have weight. Make sure the variant has weight to calculate its price."
+ )
+}
+
+// TODO retrieve custom price
+```
+
+In the code above, you first retrieve the metal option's value from the variant's options, assuming that a variant has metals if it has a `Metal` option. Then, you retrieve the metal symbol of the option's value using the `getMetalSymbol` method of the Metal Prices Module's service.
+
+If the variant doesn't have a metal in its options, the option's value is not valid, or the variant doesn't have a weight, you throw an error. The weight is necessary to calculate the price based on the metal's price per weight.
+
+Next, you'll retrieve the real-time price of the metal using the third-party service. Replace the `TODO` with the following:
+
+```ts title="src/workflows/steps/get-variant-metal-prices.ts"
+let price = variant.calculated_price?.calculated_amount || 0
+const weight = variant.weight
+const { price: metalPrice } = await metalPricesModuleService.getMetalPrice(
+ metalSymbol as MetalSymbols, currencyCode
+)
+price += (metalPrice * weight * quantity)
+
+return new StepResponse(price)
+```
+
+In the code above, you first set the price to the variant's fixed price, if it has one. Then, you retrieve the metal's price using the `getMetalPrice` method of the Metal Prices Module's service.
+
+Finally, you calculate the price by multiplying the metal's price by the variant's weight and the quantity to add to the cart, then add the fixed price to it.
+
+Every step must return a `StepResponse` instance. The `StepResponse` constructor accepts the step's output as a parameter, which in this case is the variant's price.
+
+### Create addCustomToCartWorkflow
+
+Now that you have the `getVariantMetalPricesStep`, you can create the workflow that adds the item with custom pricing to the cart.
+
+Create the file `src/workflows/add-custom-to-cart.ts` with the following content:
+
+
+
+```ts title="src/workflows/add-custom-to-cart.ts" highlights={workflowHighlights}
+import { createWorkflow } from "@medusajs/framework/workflows-sdk"
+import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
+import { QueryContext } from "@medusajs/framework/utils"
+
+type AddCustomToCartWorkflowInput = {
+ cart_id: string
+ item: {
+ variant_id: string
+ quantity: number
+ metadata?: Record
+ }
+}
+
+export const addCustomToCartWorkflow = createWorkflow(
+ "add-custom-to-cart",
+ ({ cart_id, item }: AddCustomToCartWorkflowInput) => {
+ // @ts-ignore
+ const { data: carts } = useQueryGraphStep({
+ entity: "cart",
+ filters: { id: cart_id },
+ fields: ["id", "currency_code"],
+ })
+
+ const { data: variants } = useQueryGraphStep({
+ entity: "variant",
+ fields: [
+ "*",
+ "options.*",
+ "options.option.*",
+ "calculated_price.*",
+ ],
+ filters: {
+ id: item.variant_id,
+ },
+ options: {
+ throwIfKeyNotFound: true,
+ },
+ context: {
+ calculated_price: QueryContext({
+ currency_code: carts[0].currency_code,
+ }),
+ },
+ }).config({ name: "retrieve-variant" })
+
+ // TODO add more steps
+ }
+)
+```
+
+You create a workflow with `createWorkflow` from the Workflows SDK. It accepts two parameters:
+
+1. The workflow's unique name, which is `add-custom-to-cart`.
+2. A function that receives an input object with the cart's ID and the item to add to the cart. The item has the variant's ID, quantity, and optional metadata.
+
+In the function, you first retrieve the cart's details using the `useQueryGraphStep` helper step. This step uses [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) which is a Modules SDK tool that retrieves data across modules. You use it to retrieve the cart's ID and currency code.
+
+You also retrieve the variant's details using the `useQueryGraphStep` helper step. You pass the variant's ID to the step's filters and specify the fields to retrieve. To retrieve the variant's price based on the cart's context, you pass the cart's currency code to the `calculated_price` context.
+
+Next, you'll retrieve the variant's real-time price using the `getVariantMetalPricesStep` you created earlier. First, add the following import:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+import {
+ getVariantMetalPricesStep,
+ GetVariantMetalPricesStepInput,
+} from "./steps/get-variant-metal-prices"
+```
+
+Then, replace the `TODO` in the workflow with the following:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+const price = getVariantMetalPricesStep({
+ variant: variants[0],
+ currencyCode: carts[0].currency_code,
+ quantity: item.quantity,
+} as unknown as GetVariantMetalPricesStepInput)
+
+// TODO add item with custom price to cart
+```
+
+You execute the `getVariantMetalPricesStep` passing it the variant's details, the cart's currency code, and the quantity of the item to add to the cart. The step returns the variant's custom price.
+
+Next, you'll add the item with the custom price to the cart. First, add the following imports at the top of the file:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+import { transform } from "@medusajs/framework/workflows-sdk"
+import { addToCartWorkflow } from "@medusajs/medusa/core-flows"
+```
+
+Then, replace the `TODO` in the workflow with the following:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+const itemToAdd = transform({
+ item,
+ price,
+}, (data) => {
+ return [{
+ ...data.item,
+ unit_price: data.price,
+ }]
+})
+
+addToCartWorkflow.runAsStep({
+ input: {
+ items: itemToAdd,
+ cart_id,
+ },
+})
+
+// TODO retrieve and return cart
+```
+
+You prepare the item to add to the cart using `transform` from the Workflows SDK. It allows you to manipulate and create variables in a workflow. After that, you use Medusa's `addToCartWorkflow` to add the item with the custom price to the cart.
+
+A workflow's constructor function has some constraints in implementation, which is why you need to use `transform` for variable manipulation. Learn more about these constraints in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/constructor-constraints/index.html.md).
+
+Lastly, you'll retrieve the cart's details again and return them. Add the following import at the beginning of the file:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+import { WorkflowResponse } from "@medusajs/framework/workflows-sdk"
+```
+
+And replace the last `TODO` in the workflow with the following:
+
+```ts title="src/workflows/add-custom-to-cart.ts"
+// @ts-ignore
+const { data: updatedCarts } = useQueryGraphStep({
+ entity: "cart",
+ filters: { id: cart_id },
+ fields: ["id", "items.*"],
+}).config({ name: "refetch-cart" })
+
+return new WorkflowResponse({
+ cart: updatedCarts[0],
+})
+```
+
+In the code above, you retrieve the updated cart's details using the `useQueryGraphStep` helper step. To return data from the workflow, you create and return a `WorkflowResponse` instance. It accepts as a parameter the data to return, which is the updated cart.
+
+In the next step, you'll use the workflow in a custom route to add an item with a custom price to the cart.
+
+***
+
+## Step 4: Create Add Custom Item to Cart API Route
+
+Now that you've implemented the logic to add an item with a custom price to the cart, you'll expose this functionality in an API route.
+
+An API Route is an endpoint that exposes commerce features to external applications and clients, such as storefronts. You'll create an API route at the path `/store/carts/:id/line-items-metals` that executes the workflow from the previous step to add a product variant with custom price to the cart.
+
+Learn more about API routes in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md).
+
+### Create API Route
+
+An API route is created in a `route.ts` file under a sub-directory of the `src/api` directory.
+
+The path of the API route is the file's path relative to `src/api`. So, to create the `/store/carts/:id/line-items-metals` API route, create the file `src/api/store/carts/[id]/line-items-metals/route.ts` with the following content:
+
+
+
+```ts title="src/api/store/carts/[id]/line-items-metals/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
+import { HttpTypes } from "@medusajs/framework/types"
+import { addCustomToCartWorkflow } from "../../../../../workflows/add-custom-to-cart"
+
+export const POST = async (
+ req: MedusaRequest,
+ res: MedusaResponse
+) => {
+ const { id } = req.params
+ const item = req.validatedBody
+
+ const { result } = await addCustomToCartWorkflow(req.scope)
+ .run({
+ input: {
+ cart_id: id,
+ item,
+ },
+ })
+
+ res.status(200).json({ cart: result.cart })
+}
+```
+
+Since you export a `POST` function in this file, you're exposing a `POST` API route at `/store/carts/:id/line-items-metals`. The route handler function accepts two parameters:
+
+1. A request object with details and context on the request, such as path and body parameters.
+2. A response object to manipulate and send the response.
+
+In the function, you retrieve the cart's ID from the path parameter, and the item's details from the request body. This API route will accept the same request body parameters as Medusa's [Add Item to Cart API Route](https://docs.medusajs.com/api/store#carts_postcartsidlineitems).
+
+Then, you execute the `addCustomToCartWorkflow` by invoking it, passing it the Medusa container, which is available in the request's `scope` property, then executing its `run` method. You pass the workflow's input object with the cart's ID and the item to add to the cart.
+
+Finally, you return a response with the updated cart's details.
+
+### Add Request Body Validation Middleware
+
+To ensure that the request body contains the required parameters, you'll add a middleware that validates the incoming request's body based on a defined schema.
+
+A middleware is a function executed before the API route when a request is sent to it. You define middlewares in Medusa in the `src/api/middlewares.ts` directory.
+
+Learn more about middlewares in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/middlewares/index.html.md).
+
+To add a validation middleware to the custom API route, create the file `src/api/middlewares.ts` with the following content:
+
+
+
+```ts title="src/api/middlewares.ts"
+import {
+ defineMiddlewares,
+ validateAndTransformBody,
+} from "@medusajs/framework/http"
+import {
+ StoreAddCartLineItem,
+} from "@medusajs/medusa/api/store/carts/validators"
+
+export default defineMiddlewares({
+ routes: [
+ {
+ matcher: "/store/carts/:id/line-items-metals",
+ method: "POST",
+ middlewares: [
+ validateAndTransformBody(
+ StoreAddCartLineItem
+ ),
+ ],
+ },
+ ],
+})
+```
+
+In this file, you export the middlewares definition using `defineMiddlewares` from the Medusa Framework. This function accepts an object having a `routes` property, which is an array of middleware configurations to apply on routes.
+
+You pass in the `routes` array an object having the following properties:
+
+- `matcher`: The route to apply the middleware on.
+- `method`: The HTTP method to apply the middleware on for the specified API route.
+- `middlewares`: An array of the middlewares to apply. You apply the `validateAndTransformBody` middleware, which validates the request body based on the `StoreAddCartLineItem` schema. This validation schema is the same schema used for Medusa's [Add Item to Cart API Route](https://docs.medusajs.com/api/store#carts_postcartsidlineitems).
+
+Any request sent to the `/store/carts/:id/line-items-metals` API route will now fail if it doesn't have the required parameters.
+
+Learn more about API route validation in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/validation/index.html.md).
+
+### Prepare to Test API Route
+
+Before you test the API route, you'll prepare and retrieve the necessary data to add a product variant with a custom price to the cart.
+
+#### Create Product with Metal Variant
+
+You'll first create a product that has a `Metal` option, and variant(s) with values for this option.
+
+Start the Medusa application with the following command:
+
+```bash npm2yarn
+npm run dev
+```
+
+Then, open the Medusa Admin dashboard at `localhost:9000/app` and log in with the email and password you created when you installed the Medusa application in the first step.
+
+Once you log in, click on Products in the sidebar, then click the Create button at the top right.
+
+
+
+Then, in the Create Product form:
+
+1. Enter a name for the product, and optionally enter other details like description.
+2. Enable the "Yes, this is a product with variants" toggle.
+3. Under Product Options, enter "Metal" for the title, and enter "Gold" for the values.
+
+Once you're done, click the Continue button.
+
+
+
+You can skip the next two steps by clicking the Continue button again, then the Publish button.
+
+Once you're done, the product's page will open. You'll now add weight to the product's Gold variant. To do that:
+
+- Scroll to the Variants section and find the Gold variant.
+- Click on the three-dots icon at its right.
+- Choose "Edit" from the dropdown.
+
+
+
+In the side window that opens, find the Weight field, enter the weight, and click the Save button.
+
+
+
+Finally, you need to set fixed prices for the variant, even if they're just `0`. To do that:
+
+1. Click on the three-dots icon at the top right of the Variants section.
+2. Choose "Edit Prices" from the dropdown.
+
+
+
+For each cell in the table, either enter a fixed price for the specified currency or leave it as `0`. Once you're done, click the Save button.
+
+
+
+You'll use this variant to add it to the cart later. You can find its ID by clicking on the variant, opening its details page. Then, on the details page, click on the icon at the right of the JSON section, and copy the ID from the JSON data.
+
+
+
+#### Retrieve Publishable API Key
+
+All requests sent to API routes starting with `/store` must have a publishable API key in the header. This ensures the request's operations are scoped to the publishable API key's associated sales channels. For example, products that aren't available in a cart's sales channel can't be added to it.
+
+To retrieve the publishable API key, on the Medusa Admin:
+
+1. Click on Settings in the sidebar at the bottom left.
+2. Click on Publishable API Keys from the sidebar, then click on a publishable API key in the list.
+
+
+
+3. Click on the publishable API key to copy it.
+
+
+
+You'll use this key when you test the API route.
+
+### Test API Route
+
+To test out the API route, you need to create a cart. A cart must be associated with a region. So, to retrieve the ID of a region in your store, send a `GET` request to the `/store/regions` API route:
+
+```bash
+curl 'localhost:9000/store/regions' \
+-H 'x-publishable-api-key: {api_key}'
+```
+
+Make sure to replace `{api_key}` with the publishable API key you copied earlier.
+
+This will return a list of regions. Copy the ID of one of the regions.
+
+Then, send a `POST` request to the `/store/carts` API route to create a cart:
+
+```bash
+curl -X POST 'localhost:9000/store/carts' \
+-H 'x-publishable-api-key: {api_key}' \
+-H 'Content-Type: application/json' \
+--data '{
+ "region_id": "{region_id}"
+}'
+```
+
+Make sure to replace `{api_key}` with the publishable API key you copied earlier, and `{region_id}` with the ID of a region from the previous request.
+
+This will return the created cart. Copy the ID of the cart to use it next.
+
+Finally, to add the Gold variant to the cart with a custom price, send a `POST` request to the `/store/carts/:id/line-items-metals` API route:
+
+```bash
+curl -X POST 'localhost:9000/store/carts/{cart_id}/line-items-metals' \
+-H 'x-publishable-api-key: {api_key}' \
+-H 'Content-Type: application/json' \
+--data '{
+ "variant_id": "{variant_id}",
+ "quantity": 1
+}'
+```
+
+Make sure to replace:
+
+- `{api_key}` with the publishable API key you copied earlier.
+- `{cart_id}` with the ID of the cart you created.
+- `{variant_id}` with the ID of the Gold variant you created.
+
+This will return the cart's details, where you can see in its `items` array the item with the custom price:
+
+```json title="Example Response"
+{
+ "cart": {
+ "items": [
+ {
+ "variant_id": "{variant_id}",
+ "quantity": 1,
+ "is_custom_price": true,
+ // example custom price
+ "unit_price": 2000
+ }
+ ]
+ }
+}
+```
+
+The price will be the result of the calculation you've implemented earlier, which is the fixed price of the variant plus the real-time price of the metal, multiplied by the weight of the variant and the quantity added to the cart.
+
+This price will be reflected in the cart's total price, and you can proceed to checkout with the custom-priced item.
+
+***
+
+## Next Steps
+
+You've now implemented custom item pricing in Medusa. You can also customize the [storefront](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter/index.html.md) to use the new API route to add custom-priced items to the cart.
+
+If you're new to Medusa, check out the [main documentation](https://docs.medusajs.com/docs/learn/index.html.md), where you'll get a more in-depth learning of all the concepts you've used in this guide and more.
+
+To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md).
+
+
# How-to & Tutorials
In this section of the documentation, you'll find how-to guides and tutorials that will help you customize the Medusa server and admin. These guides are useful after you've learned Medusa's main concepts in the [Get Started](https://docs.medusajs.com/docs/learn/index.html.md) section of the documentation.
@@ -39867,7 +39868,7 @@ In this tutorial, you'll learn how to implement a loyalty points system in Medus
Medusa Cloud provides a beta Store Credits feature that facilitates building a loyalty point system. [Get in touch](https://medusajs.com/contact) for early access.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md), which are available out-of-the-box. These features include management capabilities related to carts, orders, promotions, and more.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md), which are available out-of-the-box. These features include management capabilities related to carts, orders, promotions, and more.
A loyalty point system allows customers to earn points for purchases, which can be redeemed for discounts or rewards. In this tutorial, you'll learn how to customize the Medusa application to implement a loyalty points system.
@@ -41656,7 +41657,7 @@ To learn more about the commerce features that Medusa provides, check out Medusa
In this tutorial, you'll learn how to implement product reviews in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box. The features include product-management features.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md) which are available out-of-the-box. The features include product-management features.
Medusa doesn't provide product reviews out-of-the-box, but the Medusa Framework facilitates implementing customizations like product reviews. In this tutorial, you'll learn how to customize the Medusa server, Admin dashboard, and Next.js Starter Storefront to implement product reviews.
@@ -43526,9 +43527,9 @@ To learn more about the commerce features that Medusa provides, check out Medusa
In this tutorial, you will learn how to send notifications to customers who have abandoned their carts.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md), which are available out-of-the-box. These features include cart-management capabilities.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md), which are available out-of-the-box. These features include cart-management capabilities.
-Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/notification/index.html.md) allows you to send notifications to users or customers, such as password reset emails, order confirmation SMS, or other types of notifications.
+Medusa's [Notification Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/index.html.md) allows you to send notifications to users or customers, such as password reset emails, order confirmation SMS, or other types of notifications.
In this tutorial, you will use the Notification Module to send an email to customers who have abandoned their carts. The email will contain a link to recover the customer's cart, encouraging them to complete their purchase. You will use SendGrid to send the emails, but you can also use other email providers.
@@ -43583,9 +43584,9 @@ Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednas
Medusa's Notification Module provides the general functionality to send notifications, but the sending logic is implemented in a module provider. This allows you to integrate the email provider of your choice.
-To send the cart-abandonment emails, you will use SendGrid. Medusa provides a [SendGrid Notification Module Provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/notification/sendgrid/index.html.md) that you can use to send emails.
+To send the cart-abandonment emails, you will use SendGrid. Medusa provides a [SendGrid Notification Module Provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification/sendgrid/index.html.md) that you can use to send emails.
-Alternatively, you can use [other Notification Module Providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/notification#what-is-a-notification-module-provider/index.html.md) or [create a custom provider](https://docs.medusajs.com/references/notification-provider-module/index.html.md).
+Alternatively, you can use [other Notification Module Providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/notification#what-is-a-notification-module-provider/index.html.md) or [create a custom provider](https://docs.medusajs.com/references/notification-provider-module/index.html.md).
To set up SendGrid, add the SendGrid Notification Module Provider to `medusa-config.ts`:
@@ -44194,7 +44195,7 @@ To learn about the general approach of integrating an ERP with Medusa and the di
A File Module Provider uploads and manages assets, such as product images, on a third-party service.
-- [AWS S3 (and Compatible APIs)](https://docs.medusajs.com/architectural-modules/file/s3/index.html.md)
+- [AWS S3 (and Compatible APIs)](https://docs.medusajs.com/infrastructure-modules/file/s3/index.html.md)
Learn how to integrate a custom third-party file or storage provider in [this guide](https://docs.medusajs.com/references/file-provider-module/index.html.md).
@@ -44214,7 +44215,7 @@ Learn how to create a Fulfillment Provider Module [this guide](https://docs.medu
A Notification Module Provider sends messages to users and customers in your Medusa application using a third-party service.
-- [SendGrid](https://docs.medusajs.com/architectural-modules/notification/sendgrid/index.html.md)
+- [SendGrid](https://docs.medusajs.com/infrastructure-modules/notification/sendgrid/index.html.md)
- [Resend](https://docs.medusajs.com/integrations/guides/resend/index.html.md)
Learn how to create a notification provider in [this guide](https://docs.medusajs.com/references/notification-provider-module/index.html.md).
@@ -44825,7 +44826,7 @@ Since you export a `POST` route handler function, you expose an `API` route at `
1. A request object with details and context on the request, such as body parameters or authenticated user details.
2. A response object to manipulate and send the response.
-In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/event/index.html.md). This module manages events and their subscribers.
+In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/event/index.html.md). This module manages events and their subscribers.
Then, you emit the `algolia.sync` event using the Event Module's `emit` method, passing it the event name.
@@ -46053,7 +46054,7 @@ Where:
- `MAGENTO_PASSWORD`: The password of the Magento admin user.
- `MAGENTO_IMAGE_BASE_URL`: The base URL to use for product images. Magento stores product images in the `pub/media/catalog/product` directory, so you can reference them directly or use a CDN URL. If the URLs of product images in the Medusa server already have a different base URL, you can omit this option.
-Medusa supports integrating third-party services, such as [S3](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/file/s3/index.html.md), in a File Module Provider. Refer to the [File Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/architectural-modules/file/index.html.md) documentation to find other module providers and how to create a custom provider.
+Medusa supports integrating third-party services, such as [S3](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/s3/index.html.md), in a File Module Provider. Refer to the [File Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/infrastructure-modules/file/index.html.md) documentation to find other module providers and how to create a custom provider.
You can now use the Magento Module to migrate data, which you'll do in the next steps.
@@ -46500,1245 +46501,6 @@ If you're new to Medusa, check out the [main documentation](https://docs.medusaj
To learn more about the commerce features that Medusa provides, check out Medusa's [Commerce Modules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/index.html.md).
-# Integrate Medusa with ShipStation (Fulfillment)
-
-In this guide, you'll learn how to integrate Medusa with ShipStation.
-
-Refer your technical team to this guide to integrate ShipStation with your Medusa application. You can then enable it using the Medusa Admin as explained in [this user guide](https://docs.medusajs.com/user-guide/settings/locations-and-shipping/locations#manage-fulfillment-providers/index.html.md).
-
-When you install a Medusa application, you get a fully-fledged commerce platform with support for customizations. Medusa's [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md) provides fulfillment-related resources and functionalities in your store, but it delegates the processing and shipment of order fulfillments to providers that you can integrate.
-
-[ShipStation](https://shipstation.com/) is a shipping toolbox that connects all your shipping providers within one platform. By integrating it with Medusa, you can allow customers to choose from different providers like DHL and FedEx and view price rates retrieved from ShipStation. Admin users will also process the order fulfillment using the ShipStation integration.
-
-This guide will teach you how to:
-
-- Install and set up Medusa.
-- Set up a ShipStation account.
-- Integrate ShipStation as a fulfillment provider in Medusa.
-
-You can follow this guide whether you're new to Medusa or an advanced Medusa developer.
-
-[Example Repository](https://github.com/medusajs/examples/tree/main/shipstation-integration): Find the full code of the guide in this repository.
-
-***
-
-## Step 1: Install a Medusa Application
-
-### Prerequisites
-
-- [Node.js v20+](https://nodejs.org/en/download)
-- [Git CLI tool](https://git-scm.com/downloads)
-- [PostgreSQL](https://www.postgresql.org/download/)
-
-Start by installing the Medusa application on your machine with the following command:
-
-```bash
-npx create-medusa-app@latest
-```
-
-You'll first be asked for the project's name. Then, when you're asked whether you want to install the Next.js storefront, choose `Y` for yes.
-
-Afterwards, the installation process will start, which will install the Medusa application in a directory with your project's name, and the Next.js storefront in a directory with the `{project-name}-storefront` name.
-
-The Medusa application is composed of a headless Node.js server and an admin dashboard. The storefront is installed or custom-built separately and connects to the Medusa application through its REST endpoints, called [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md). Learn more about Medusa's architecture in [this documentation](https://docs.medusajs.com/docs/learn/introduction/architecture/index.html.md).
-
-Once the installation finishes successfully, the Medusa Admin dashboard will open with a form to create a new user. Enter the user's credential and submit the form.
-
-Afterwards, you can login with the new user and explore the dashboard. The Next.js storefront is also running at `http://localhost:8000`.
-
-Check out the [troubleshooting guides](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/troubleshooting/create-medusa-app-errors/index.html.md) for help.
-
-***
-
-## Step 2: Prepare ShipStation Account
-
-In this step, you'll prepare your ShipStation account before integrating it into Medusa. If you don't have an account, create one [here](https://www.shipstation.com/start-a-free-trial).
-
-### Enable Carriers
-
-To create labels for your shipments, you need to enable carriers. This requires you to enter payment and address details.
-
-To enable carriers:
-
-1. On the Onboard page, in the "Enable carriers & see rates" section, click on the "Enable Carriers" button.
-
-
-
-2. In the pop-up that opens, click on Continue Setup.
-
-
-
-3. In the next section of the form, you have to enter your payment details and billing address. Once done, click on Continue Setup.
-4. After that, click the checkboxes on the Terms of Service section, then click the Finish Setup button.
-
-
-
-5. Once you're done, you can optionally add funds to your account. If you're not US-based, make sure to disable ParcelGuard insurance. Otherwise, an error will occur while retrieving rates later.
-
-### Add Carriers
-
-You must have at least one carrier (shipping provider) added in your ShipStation account. You'll later provide shipping options for each of these carriers in your Medusa application.
-
-To add carriers:
-
-1. On the Onboard page, in the "Enable carriers & see rates" section, click on the "Add your carrier accounts" link.
-
-
-
-2. Click on a provider from the pop-up window.
-
-
-
-Based on the provider you chose, you'll have to enter your account details, then submit the form.
-
-### Activate Shipping API
-
-To integrate ShipStation using their API, you must enable the Shipping API Add-On. To do that:
-
-1. Go to Add-Ons from the navigation bar.
-2. Find Shipping API and activate it.
-
-You'll later retrieve your API key.
-
-***
-
-## Step 3: Create ShipStation Module Provider
-
-To integrate third-party services into Medusa, you create a custom module. A module is a re-usable package with functionalities related to a single feature or domain. Medusa integrates the module into your application without implications or side effects on your setup.
-
-Medusa's Fulfillment Module delegates processing fulfillments and shipments to other modules, called module providers. In this step, you'll create a ShipStation Module Provider that implements all functionalities required for fulfillment. In later steps, you'll add into Medusa shipping options for ShipStation, and allow customers to choose it during checkout.
-
-Learn more about modules in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md).
-
-### Create Module Directory
-
-A module is created under the `src/modules` directory of your Medusa application. So, create the directory `src/modules/shipstation`.
-
-
-
-### Create Service
-
-You define a module's functionalities in a service. A service is a TypeScript or JavaScript class that the module exports. In the service's methods, you can connect to the database, which is useful if your module defines tables in the database, or connect to a third-party service.
-
-In this section, you'll create the ShipStation Module Provider's service and the methods necessary to handle fulfillment.
-
-Start by creating the file `src/modules/shipstation/service.ts` with the following content:
-
-
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights1}
-import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
-
-export type ShipStationOptions = {
- api_key: string
-}
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- static identifier = "shipstation"
- protected options_: ShipStationOptions
-
- constructor({}, options: ShipStationOptions) {
- super()
-
- this.options_ = options
- }
-
- // TODO add methods
-}
-
-export default ShipStationProviderService
-```
-
-A Fulfillment Module Provider service must extend the `AbstractFulfillmentProviderService` class. You'll implement the abstract methods of this class in the upcoming sections.
-
-The service must have an `identifier` static property, which is a unique identifier for the provider. You set the identifier to `shipstation`.
-
-A module can receive options that are set when you later add the module to Medusa's configurations. These options allow you to safely store secret values outside of your code.
-
-The ShipStation module requires an `api_key` option, indicating your ShipStation's API key. You receive the options as a second parameter of the service's constructor.
-
-### Create Client
-
-To send requests to ShipStation, you'll create a client class that provides the methods to send requests. You'll then use that class in your service.
-
-Create the file `src/modules/shipstation/client.ts` with the following content:
-
-
-
-```ts title="src/modules/shipstation/client.ts" highlights={clientHighlights1}
-import { ShipStationOptions } from "./service"
-import { MedusaError } from "@medusajs/framework/utils"
-
-export class ShipStationClient {
- options: ShipStationOptions
-
- constructor(options) {
- this.options = options
- }
-
- private async sendRequest(url: string, data?: RequestInit): Promise {
- return fetch(`https://api.shipstation.com/v2${url}`, {
- ...data,
- headers: {
- ...data?.headers,
- "api-key": this.options.api_key,
- "Content-Type": "application/json",
- },
- }).then((resp) => {
- const contentType = resp.headers.get("content-type")
- if (!contentType?.includes("application/json")) {
- return resp.text()
- }
-
- return resp.json()
- })
- .then((resp) => {
- if (typeof resp !== "string" && resp.errors?.length) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- `An error occured while sending a request to ShipStation: ${
- resp.errors.map((error) => error.message)
- }`
- )
- }
-
- return resp
- })
- }
-}
-```
-
-The `ShipStationClient` class accepts the ShipStation options in its constructor and sets those options in the `options` property.
-
-You also add a private `sendRequest` method that accepts a path to send a request to and the request's configurations. In the method, you send a request using the Fetch API, passing the API key from the options in the request header. You also parse the response body based on its content type, and check if there are any errors to be thrown before returning the parsed response.
-
-You'll add more methods to send requests in the upcoming steps.
-
-To use the client in `ShipStationProviderService`, add it as a class property and initialize it in the constructor:
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights2}
-// imports...
-import { ShipStationClient } from "./client"
-
-// ...
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // properties...
- protected client: ShipStationClient
-
- constructor({}, options: ShipStationOptions) {
- // ...
- this.client = new ShipStationClient(options)
- }
-}
-```
-
-You import `ShipStationClient` and add a new `client` property in `ShipStationProviderService`. In the class's constructor, you set the `client` property by initializing `ShipStationProviderService`, passing it the module's options.
-
-You'll use the `client` property when implementing the service's methods.
-
-### Implement Service Methods
-
-In this section, you'll go back to the `ShipStationProviderService` method to implement the abstract methods of `AbstractFulfillmentProviderService`.
-
-Refer to [this guide](https://docs.medusajs.com/references/fulfillment/provider/index.html.md) for a full reference of all methods, their parameters and return types.
-
-#### getFulfillmentOptions
-
-The `getFulfillmentOptions` method returns the options that this fulfillment provider supports. When admin users add shipping options later in the Medusa Admin, they'll select one of these options.
-
-Learn more about shipping options in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/shipping-option/index.html.md).
-
-ShipStation requires that a shipment must be associated with a carrier and one of its services. So, in this method, you'll retrieve the list of carriers from ShipStation and return them as fulfillment options. Shipping options created from these fulfillment options will always have access to the option's carrier and service.
-
-Before you start implementing methods, you'll add the expected carrier types returned by ShipStation. Create the file `src/modules/shipstation/types.ts` with the following content:
-
-
-
-```ts title="src/modules/shipstation/types.ts"
-export type Carrier = {
- carrier_id: string
- disabled_by_billing_plan: boolean
- friendly_name: string
- services: {
- service_code: string
- name: string
- }[]
- packages: {
- package_code: string
- }[]
- [k: string]: unknown
-}
-
-export type CarriersResponse = {
- carriers: Carrier[]
-}
-```
-
-You define a `Carrier` type that holds a carrier's details, and a `CarriersResponse` type, which is the response returned by ShipStation.
-
-A carrier has more fields that you can use. Refer to [ShipStation's documentation](https://docs.shipstation.com/openapi/carriers/list_carriers#carriers/list_carriers/t=response\&c=200\&path=carriers) for all carrier fields.
-
-Next, you'll add in `ShipStationClient` the method to retrieve the carriers from ShipStation. So, add to the class defined in `src/modules/shipstation/client.ts` a new method:
-
-```ts title="src/modules/shipstation/client.ts" highlights={clientHighlights2}
-// other imports...
-import {
- CarriersResponse,
-} from "./types"
-
-export class ShipStationClient {
- // ...
- async getCarriers(): Promise {
- return await this.sendRequest("/carriers")
- }
-}
-```
-
-You added a new `getCarriers` method that uses the `sendRequest` method to send a request to the [ShipStation's List Carriers endpoint](https://docs.shipstation.com/openapi/carriers/list_carriers). The method returns `CarriersResponse` that you defined earlier.
-
-Finally, add the `getFulfillmentOptions` method to `ShipStationProviderService`:
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights3}
-// other imports...
-import {
- FulfillmentOption,
-} from "@medusajs/framework/types"
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // ...
- async getFulfillmentOptions(): Promise {
- const { carriers } = await this.client.getCarriers()
- const fulfillmentOptions: FulfillmentOption[] = []
-
- carriers
- .filter((carrier) => !carrier.disabled_by_billing_plan)
- .forEach((carrier) => {
- carrier.services.forEach((service) => {
- fulfillmentOptions.push({
- id: `${carrier.carrier_id}__${service.service_code}`,
- name: service.name,
- carrier_id: carrier.carrier_id,
- carrier_service_code: service.service_code,
- })
- })
- })
-
- return fulfillmentOptions
- }
-}
-```
-
-In the `getFulfillmentOptions` method, you retrieve the carriers from ShipStation. You then filter out the carriers disabled by your ShipStation billing plan, and loop over the remaining carriers and their services.
-
-You return an array of fulfillment-option objects, where each object represents a carrier and service pairing. Each object has the following properties:
-
-- an `id` property, which you set to a combination of the carrier ID and the service code.
-- a `name` property, which you set to the service's `name`. The admin user will see this name when they create a shipping option for the ShipStation provider.
-- You can pass other data, such as `carrier_id` and `carrier_service_code`, and Medusa will store the fulfillment option in the `data` property of shipping options created later.
-
-Learn more about the shipping option's `data` property in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/shipping-option/index.html.md).
-
-You'll see this method in action later when you create a shipping option.
-
-#### canCalculate
-
-When an admin user creates a shipping option for your provider, they can choose whether the price is flat rate or calculated during checkout.
-
-If the user chooses calculated, Medusa validates that your fulfillment provider supports calculated prices using the `canCalculate` method of your provider's service.
-
-This method accepts the shipping option's `data` field, which will hold the data of an option returned by `getFulfillmentOptions`. It returns a boolean value indicating whether the shipping option can have a calculated price.
-
-Add the method to `ShipStationProviderService` in `src/modules/shipstation/service.ts`:
-
-```ts title="src/modules/shipstation/service.ts"
-// other imports...
-import {
- // ...
- CreateShippingOptionDTO,
-} from "@medusajs/framework/types"
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // ...
- async canCalculate(data: CreateShippingOptionDTO): Promise {
- return true
- }
-}
-```
-
-Since all shipping option prices can be calculated with ShipStation based on the chosen carrier and service zone, you always return `true` in this method.
-
-You'll implement the calculation mechanism in a later method.
-
-#### calculatePrice
-
-When the customer views available shipping options during checkout, the Medusa application requests the calculated price from your fulfillment provider using its `calculatePrice` method.
-
-To retrieve shipping prices with ShipStation, you create a shipment first then get its rates. So, in the `calculatePrice` method, you'll either:
-
-- Send a request to [ShipStation's get shipping rates endpoint](https://docs.shipstation.com/openapi/rates/calculate_rates) that creates a shipment and returns its prices;
-- Or, if a shipment was already created before, you'll retrieve its prices using [ShipStation's get shipment rates endpoint](https://docs.shipstation.com/openapi/shipments/list_shipment_rates).
-
-First, add the following types to `src/modules/shipstation/types.ts`:
-
-```ts title="src/modules/shipstation/types.ts" highlights={typesHighlights1}
-export type ShipStationAddress = {
- name: string
- phone: string
- email?: string | null
- company_name?: string | null
- address_line1: string
- address_line2?: string | null
- address_line3?: string | null
- city_locality: string
- state_province: string
- postal_code: string
- country_code: string
- address_residential_indicator: "unknown" | "yes" | "no"
- instructions?: string | null
- geolocation?: {
- type?: string
- value?: string
- }[]
-}
-
-export type Rate = {
- rate_id: string
- shipping_amount: {
- currency: string
- amount: number
- }
- insurance_amount: {
- currency: string
- amount: number
- }
- confirmation_amount: {
- currency: string
- amount: number
- }
- other_amount: {
- currency: string
- amount: number
- }
- tax_amount: {
- currency: string
- amount: number
- }
-}
-
-export type RateResponse = {
- rates: Rate[]
-}
-
-export type GetShippingRatesRequest = {
- shipment_id?: string
- shipment?: Omit
- rate_options: {
- carrier_ids: string[]
- service_codes: string[]
- preferred_currency: string
- }
-}
-
-export type GetShippingRatesResponse = {
- shipment_id: string
- carrier_id?: string
- service_code?: string
- external_order_id?: string
- rate_response: RateResponse
-}
-
-export type Shipment = {
- shipment_id: string
- carrier_id: string
- service_code: string
- ship_to: ShipStationAddress
- return_to?: ShipStationAddress
- is_return?: boolean
- ship_from: ShipStationAddress
- items?: [
- {
- name?: string
- quantity?: number
- sku?: string
- }
- ]
- warehouse_id?: string
- shipment_status: "pending" | "processing" | "label_purchased" | "cancelled"
- [k: string]: unknown
-}
-
-```
-
-You add the following types:
-
-- `ShipStationAddress`: an address to ship from or to.
-- `Rate`: a price rate for a specified carrier and service zone.
-- `RateResponse`: The response when retrieving rates.
-- `GetShippingRatesRequest`: The request body data for [ShipStation's get shipping rates endpoint](https://docs.shipstation.com/openapi/rates/calculate_rates). You can refer to their API reference for other accepted parameters.
-- `GetShippingRatesResponse`: The response of the [ShipStation's get shipping rates endpoint](https://docs.shipstation.com/openapi/rates/calculate_rates). You can refer to their API reference for other response fields.
-- `Shipment`: A shipment's details.
-
-Then, add the following methods to `ShipStationClient`:
-
-```ts title="src/modules/shipstation/client.ts" highlights={serviceHighlights7}
-// other imports...
-import {
- // ...
- GetShippingRatesRequest,
- GetShippingRatesResponse,
- RateResponse,
-} from "./types"
-
-export class ShipStationClient {
- // ...
- async getShippingRates(
- data: GetShippingRatesRequest
- ): Promise {
- return await this.sendRequest("/rates", {
- method: "POST",
- body: JSON.stringify(data),
- }).then((resp) => {
- if (resp.rate_response.errors?.length) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- `An error occured while retrieving rates from ShipStation: ${
- resp.rate_response.errors.map((error) => error.message)
- }`
- )
- }
-
- return resp
- })
- }
-
- async getShipmentRates(id: string): Promise {
- return await this.sendRequest(`/shipments/${id}/rates`)
- }
-}
-```
-
-The `getShippingRates` method accepts as a parameter the data to create a shipment and retrieve its rate. In the method, you send the request using the `sendRequest` method, and throw any errors in the rate retrieval before returning the response.
-
-The `getShipmentRates` method accepts the ID of the shipment as a parameter, sends the request using the `sendRequest` method and returns its response holding the shipment's rates.
-
-Next, add to `ShipStationProviderService` a private method that'll be used to create a shipment in ShipStation and get its rates:
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights8}
-// other imports...
-import {
- // ...
- MedusaError,
-} from "@medusajs/framework/utils"
-import {
- // ...
- CalculateShippingOptionPriceDTO,
-} from "@medusajs/framework/types"
-import {
- GetShippingRatesResponse,
- ShipStationAddress,
-} from "./types"
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // ...
- private async createShipment({
- carrier_id,
- carrier_service_code,
- from_address,
- to_address,
- items,
- currency_code,
- }: {
- carrier_id: string
- carrier_service_code: string
- from_address?: {
- name?: string
- address?: Omit<
- StockLocationAddressDTO, "created_at" | "updated_at" | "deleted_at"
- >
- },
- to_address?: Omit<
- CartAddressDTO, "created_at" | "updated_at" | "deleted_at" | "id"
- >,
- items: CartLineItemDTO[] | OrderLineItemDTO[],
- currency_code: string
- }): Promise {
- if (!from_address?.address) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- "from_location.address is required to calculate shipping rate"
- )
- }
- const ship_from: ShipStationAddress = {
- name: from_address?.name || "",
- phone: from_address?.address?.phone || "",
- address_line1: from_address?.address?.address_1 || "",
- city_locality: from_address?.address?.city || "",
- state_province: from_address?.address?.province || "",
- postal_code: from_address?.address?.postal_code || "",
- country_code: from_address?.address?.country_code || "",
- address_residential_indicator: "unknown",
- }
- if (!to_address) {
- throw new MedusaError(
- MedusaError.Types.INVALID_DATA,
- "shipping_address is required to calculate shipping rate"
- )
- }
-
- const ship_to: ShipStationAddress = {
- name: `${to_address.first_name} ${to_address.last_name}`,
- phone: to_address.phone || "",
- address_line1: to_address.address_1 || "",
- city_locality: to_address.city || "",
- state_province: to_address.province || "",
- postal_code: to_address.postal_code || "",
- country_code: to_address.country_code || "",
- address_residential_indicator: "unknown",
- }
-
- // TODO create shipment
- }
-}
-```
-
-The `createShipment` method accepts as a parameter an object having the following properties:
-
-- `carrier_id`: The ID of the carrier to create the shipment for.
-- `carrier_service_code`: The code of the carrier's service.
-- `from_address`: The address to ship items from, which is the address of the stock location associated with a shipping option.
-- `to_address`: The address to ship items to, which is the customer's address.
-- `items`: An array of the items in the cart or order (for fulfilling the order later).
-- `currency_code`: The currency code of the cart or order.
-
-In the `createShipment` method, so far you only prepare the data to be sent to ShipStation. ShipStation requires the addresses to ship the items from and to.
-
-To send the request, replace the `TODO` with the following:
-
-```ts title="src/modules/shipstation/service.ts"
-// Sum the package's weight
-// You can instead create different packages for each item
-const packageWeight = items.reduce((sum, item) => {
- // @ts-ignore
- return sum + (item.variant.weight || 0)
-}, 0)
-
-return await this.client.getShippingRates({
- shipment: {
- carrier_id: carrier_id,
- service_code: carrier_service_code,
- ship_to,
- ship_from,
- validate_address: "no_validation",
- items: items?.map((item) => ({
- name: item.title,
- quantity: item.quantity,
- sku: item.variant_sku || "",
- })),
- packages: [{
- weight: {
- value: packageWeight,
- unit: "kilogram",
- },
- }],
- customs: {
- contents: "merchandise",
- non_delivery: "return_to_sender",
- },
- },
- rate_options: {
- carrier_ids: [carrier_id],
- service_codes: [carrier_service_code],
- preferred_currency: currency_code as string,
- },
-})
-```
-
-You create a shipment and get its rates using the `getShippingRates` method you added to the client. You pass the method the expected request body parameters by [ShipStation's get shipping rates endpoint](https://docs.shipstation.com/openapi/rates/calculate_rates), including the carrier ID, the items to be shipped, and more.
-
-The above snippet assumes all items are sent in a single package. You can instead pass a package for each item, specifying its weight and optionally its height, width, and length.
-
-Finally, add the `calculatePrice` method to `ShipStationProviderService`:
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights5}
-// other imports...
-import {
- // ...
- CalculatedShippingOptionPrice,
-} from "@medusajs/framework/types"
-
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // ...
- async calculatePrice(
- optionData: CalculateShippingOptionPriceDTO["optionData"],
- data: CalculateShippingOptionPriceDTO["data"],
- context: CalculateShippingOptionPriceDTO["context"]
- ): Promise {
- const { shipment_id } = data as {
- shipment_id?: string
- } || {}
- const { carrier_id, carrier_service_code } = optionData as {
- carrier_id: string
- carrier_service_code: string
- }
- let rate: Rate | undefined
-
- if (!shipment_id) {
- const shipment = await this.createShipment({
- carrier_id,
- carrier_service_code,
- from_address: {
- name: context.from_location?.name,
- address: context.from_location?.address,
- },
- to_address: context.shipping_address,
- items: context.items || [],
- currency_code: context.currency_code as string,
- })
- rate = shipment.rate_response.rates[0]
- } else {
- const rateResponse = await this.client.getShipmentRates(shipment_id)
- rate = rateResponse[0].rates[0]
- }
-
- const calculatedPrice = !rate ? 0 : rate.shipping_amount.amount + rate.insurance_amount.amount +
- rate.confirmation_amount.amount + rate.other_amount.amount +
- (rate.tax_amount?.amount || 0)
-
- return {
- calculated_amount: calculatedPrice,
- is_calculated_price_tax_inclusive: !!rate?.tax_amount,
- }
- }
-}
-```
-
-The `calculatePrice` method accepts the following parameters:
-
-1. The `data` property of the chosen shipping option during checkout.
-2. The `data` property of the shipping method, which will hold the ID of the shipment in ShipStation.
-3. An object of the checkout's context, including the cart's items, the location associated with the shipping option, and more.
-
-In the method, you first check if a `shipment_id` is already stored in the shipping method's `data` property. If so, you retrieve the shipment's rates using the client's `getShipmentRates` method. Otherwise, you use the `createShipment` method to create the shipment and get its rates.
-
-A rate returned by ShipStation has four properties that, when added up, make up the full price: `shipping_amount`, `insurance_amount`, `confirmation_amount`, and `other_amount`. It may have a `tax_amount` property, which is the amount for applied taxes.
-
-Learn more about these fields in [ShipStation's documentation](https://docs.shipstation.com/rate-shopping#about-the-response).
-
-The method returns an object having the following properties:
-
-- `calculated_amount`: The shipping method's price calculated by adding the four rate properties with the tax property, if available.
-- `is_calculated_price_tax_inclusive`: Whether the price includes taxes, which is inferred from whether the `tax_amount` property is set in the rate.
-
-Customers will now see the calculated price of a ShipStation shipping option during checkout.
-
-#### validateFulfillmentData
-
-When a customer chooses a shipping option during checkout, Medusa creates a shipping method from that option. A shipping method has a `data` property to store data relevant for later processing of the method and its fulfillments.
-
-So, in the `validateFulfillmentData` method of your provider, you'll create a shipment in ShipStation if it wasn't already created using their [get shipping rates endpoint](https://docs.shipstation.com/openapi/rates/calculate_rates), and store the ID of that shipment in the created shipping method's `data` property.
-
-Add the `validateFulfillmentData` method to `ShipStationProviderService`:
-
-```ts title="src/modules/shipstation/service.ts" highlights={serviceHighlights4}
-class ShipStationProviderService extends AbstractFulfillmentProviderService {
- // ...
- async validateFulfillmentData(
- optionData: Record,
- data: Record,
- context: Record
- ): Promise {
- let { shipment_id } = data as {
- shipment_id?: string
- }
-
- if (!shipment_id) {
- const { carrier_id, carrier_service_code } = optionData as {
- carrier_id: string
- carrier_service_code: string
- }
- const shipment = await this.createShipment({
- carrier_id,
- carrier_service_code,
- from_address: {
- // @ts-ignore
- name: context.from_location?.name,
- // @ts-ignore
- address: context.from_location?.address,
- },
- // @ts-ignore
- to_address: context.shipping_address,
- // @ts-ignore
- items: context.items || [],
- // @ts-ignore
- currency_code: context.currency_code,
- })
- shipment_id = shipment.shipment_id
- }
-
- return {
- ...data,
- shipment_id,
- }
- }
-}
-```
-
-The `validateFulfillmentData` method accepts the following parameters:
-
-1. The `data` property of the chosen shipping option during checkout. It will hold the carrier ID and its service code.
-2. The `data` property of the shipping method to be created. This can hold custom data sent in the [Add Shipping Method API route](https://docs.medusajs.com/api/store#carts_postcartsidshippingmethods).
-3. An object of the checkout's context, including the cart's items, the location associated with the shipping option, and more.
-
-In the method, you try to retrieve the shipment ID from the shipping method's `data` parameter if it was already created. If not, you create the shipment in ShipStation using the `createShipment` method.
-
-Finally, you return the object to be stored in the shipping method's `data` property. You include in it the ID of the shipment in ShipStation.
-
-#### createFulfillment
-
-After the customer places the order, the admin user can manage its fulfillments. When the admin user creates a fulfillment for the order, Medusa uses the `createFulfillment` method of the associated provider to handle any processing in the third-party provider.
-
-This method supports creating split fulfillments, meaning you can partially fulfill and order's items. So, you'll create a new shipment, then purchase a label for that shipment. You'll use the existing shipment to retrieve details like the address to ship from and to.
-
-First, add a new type to `src/modules/shipstation/types.ts`:
-
-```ts title="src/modules/shipstation/types.ts"
-export type Label = {
- label_id: string
- status: "processing" | "completed" | "error" | "voided"
- shipment_id: string
- ship_date: Date
- shipment_cost: {
- currency: string
- amount: number
- }
- insurance_cost: {
- currency: string
- amount: number
- }
- confirmation_amount: {
- currency: string
- amount: number
- }
- tracking_number: string
- is_return_label: boolean
- carrier_id: string
- service_code: string
- trackable: string
- tracking_status: "unknown" | "in_transit" | "error" | "delivered"
- label_download: {
- href: string
- pdf: string
- png: string
- zpl: string
- }
-}
-```
-
-You add the `Label` type for the details in a label object. You can find more properties in [ShipStation's documentation](https://docs.shipstation.com/openapi/labels/create_label#labels/create_label/response\&c=200/body).
-
-Then, add the following methods to the `ShipStationClient`:
-
-```ts title="src/modules/shipstation/client.ts"
-// other imports...
-import {
- // ...
- Label,
- Shipment,
-} from "./types"
-
-export class ShipStationClient {
- // ...
-
- async getShipment(id: string): Promise {
- return await this.sendRequest(`/shipments/${id}`)
- }
-
- async purchaseLabelForShipment(id: string): Promise
-Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this API Key Module.
+Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this API Key Module.
@@ -33,7 +33,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the API Key Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/auth/page.mdx b/www/apps/resources/app/commerce-modules/auth/page.mdx
index 377ea69f0b..af64a53563 100644
--- a/www/apps/resources/app/commerce-modules/auth/page.mdx
+++ b/www/apps/resources/app/commerce-modules/auth/page.mdx
@@ -8,7 +8,7 @@ export const metadata = {
In this section of the documentation, you will find resources to learn more about the Auth Module and how to use it in your application.
-Medusa has auth related features available out-of-the-box through the Auth Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Auth Module.
+Medusa has auth related features available out-of-the-box through the Auth Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Auth Module.
@@ -27,7 +27,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Auth Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/auth/reset-password/page.mdx b/www/apps/resources/app/commerce-modules/auth/reset-password/page.mdx
index bdfb553154..4703ae6965 100644
--- a/www/apps/resources/app/commerce-modules/auth/reset-password/page.mdx
+++ b/www/apps/resources/app/commerce-modules/auth/reset-password/page.mdx
@@ -28,7 +28,7 @@ You'll create a subscriber that listens to the event. When the event is emitted,
items={[
{
text: "A notification provider module, such as SendGrid",
- link: "/architectural-modules/notification/sendgrid"
+ link: "/infrastructure-modules/notification/sendgrid"
}
]}
/>
diff --git a/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/page.mdx
index 150ced9899..6f1072c0c5 100644
--- a/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/cart/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Cart Module and other commerce modules.
+This document showcases the module links defined between the Cart Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/cart/page.mdx b/www/apps/resources/app/commerce-modules/cart/page.mdx
index be6c558cbf..b44b6d4e6a 100644
--- a/www/apps/resources/app/commerce-modules/cart/page.mdx
+++ b/www/apps/resources/app/commerce-modules/cart/page.mdx
@@ -8,7 +8,7 @@ export const metadata = {
In this section of the documentation, you will find resources to learn more about the Cart Module and how to use it in your application.
-Medusa has cart related features available out-of-the-box through the Cart Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Cart Module.
+Medusa has cart related features available out-of-the-box through the Cart Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Cart Module.
@@ -21,13 +21,13 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
- [Cart Management](./concepts/page.mdx): Store and manage carts, including their addresses, line items, shipping methods, and more.
- [Apply Promotion Adjustments](./promotions/page.mdx): Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals.
- [Apply Tax Lines](./tax-lines/page.mdx): Apply tax lines to line items and shipping methods.
-- [Cart Scoping](./links-to-other-modules/page.mdx): When used in the Medusa application, Medusa creates links to other commerce modules, scoping a cart to a sales channel, region, and a customer.
+- [Cart Scoping](./links-to-other-modules/page.mdx): When used in the Medusa application, Medusa creates links to other Commerce Modules, scoping a cart to a sales channel, region, and a customer.
---
## How to Use the Cart Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx
index 5776669c5c..50375c3f17 100644
--- a/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Currency Module and other commerce modules.
+This document showcases the module links defined between the Currency Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/currency/page.mdx b/www/apps/resources/app/commerce-modules/currency/page.mdx
index 0af2ff17f5..69cc95153f 100644
--- a/www/apps/resources/app/commerce-modules/currency/page.mdx
+++ b/www/apps/resources/app/commerce-modules/currency/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/store) to learn how
-Medusa has currency related features available out-of-the-box through the Currency Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Currency Module.
+Medusa has currency related features available out-of-the-box through the Currency Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Currency Module.
@@ -25,13 +25,13 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## Currency Features
- [Currency Management and Retrieval](/references/currency/listAndCountCurrencies): This module adds all common currencies to your application and allows you to retrieve them.
-- [Support Currencies in Modules](./links-to-other-modules/page.mdx): Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
+- [Support Currencies in Modules](./links-to-other-modules/page.mdx): Other Commerce Modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
---
## How to Use the Currency Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx
index f1af4726eb..1c7c3e4c37 100644
--- a/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/customer/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Customer Module and other commerce modules.
+This document showcases the module links defined between the Customer Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/customer/page.mdx b/www/apps/resources/app/commerce-modules/customer/page.mdx
index 742019b187..8fc3558316 100644
--- a/www/apps/resources/app/commerce-modules/customer/page.mdx
+++ b/www/apps/resources/app/commerce-modules/customer/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/customers) to learn how to m
-Medusa has customer related features available out-of-the-box through the Customer Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Customer Module.
+Medusa has customer related features available out-of-the-box through the Customer Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Customer Module.
@@ -31,7 +31,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Customer Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/fulfillment/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/fulfillment/links-to-other-modules/page.mdx
index c0d6f0983f..ef9c800b16 100644
--- a/www/apps/resources/app/commerce-modules/fulfillment/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/fulfillment/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Fulfillment Module and other commerce modules.
+This document showcases the module links defined between the Fulfillment Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/fulfillment/page.mdx b/www/apps/resources/app/commerce-modules/fulfillment/page.mdx
index 24a1ca24ed..99e1d1f640 100644
--- a/www/apps/resources/app/commerce-modules/fulfillment/page.mdx
+++ b/www/apps/resources/app/commerce-modules/fulfillment/page.mdx
@@ -17,7 +17,7 @@ Refer to the Medusa Admin User Guide to learn how to use the dashboard to:
-Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Fulfillment Module.
+Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Fulfillment Module.
@@ -36,7 +36,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Fulfillment Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/inventory/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/inventory/links-to-other-modules/page.mdx
index f633b618c4..3385f0798c 100644
--- a/www/apps/resources/app/commerce-modules/inventory/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/inventory/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Inventory Module and other commerce modules.
+This document showcases the module links defined between the Inventory Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/inventory/page.mdx b/www/apps/resources/app/commerce-modules/inventory/page.mdx
index 1dae948607..461b123451 100644
--- a/www/apps/resources/app/commerce-modules/inventory/page.mdx
+++ b/www/apps/resources/app/commerce-modules/inventory/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/inventory) to learn how to m
-Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Inventory Module.
+Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Inventory Module.
@@ -34,7 +34,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Inventory Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/order/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/order/links-to-other-modules/page.mdx
index abc6b0744e..a6296fbd40 100644
--- a/www/apps/resources/app/commerce-modules/order/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/order/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Order Module and other commerce modules.
+This document showcases the module links defined between the Order Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/order/page.mdx b/www/apps/resources/app/commerce-modules/order/page.mdx
index a51b9a2911..dc9aa02ac3 100644
--- a/www/apps/resources/app/commerce-modules/order/page.mdx
+++ b/www/apps/resources/app/commerce-modules/order/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/orders) to learn how to mana
-Medusa has order related features available out-of-the-box through the Order Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Order Module.
+Medusa has order related features available out-of-the-box through the Order Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Order Module.
@@ -34,7 +34,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Order Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/page.mdx b/www/apps/resources/app/commerce-modules/page.mdx
index bd0d8d0689..2064039497 100644
--- a/www/apps/resources/app/commerce-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/page.mdx
@@ -6,11 +6,11 @@ export const metadata = {
# {metadata.title}
-In this section of the documentation, you'll find guides and references related to Medusa's commerce modules.
+In this section of the documentation, you'll find guides and references related to Medusa's Commerce Modules.
-A commerce module provides features for a commerce domain within its service. The Medusa application exposes these features in its API routes to clients.
+A Commerce Module provides features for a commerce domain within its service. The Medusa application exposes these features in its API routes to clients.
-A commerce module also defines data models, representing tables in the database. The Medusa Framework and tools allow you to extend these data models to add custom fields.
+A Commerce Module also defines data models, representing tables in the database. The Medusa Framework and tools allow you to extend these data models to add custom fields.
## Commerce Modules List
diff --git a/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx
index 67cd9c78d5..439220ddb6 100644
--- a/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Payment Module and other commerce modules.
+This document showcases the module links defined between the Payment Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/payment/page.mdx b/www/apps/resources/app/commerce-modules/payment/page.mdx
index af3a739408..0829663cb4 100644
--- a/www/apps/resources/app/commerce-modules/payment/page.mdx
+++ b/www/apps/resources/app/commerce-modules/payment/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/orders/payments) to learn ho
-Medusa has payment related features available out-of-the-box through the Payment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Payment Module.
+Medusa has payment related features available out-of-the-box through the Payment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Payment Module.
@@ -34,7 +34,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Payment Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx
index d530e62be8..6e97b9ed3e 100644
--- a/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Pricing Module and other commerce modules.
+This document showcases the module links defined between the Pricing Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/pricing/page.mdx b/www/apps/resources/app/commerce-modules/pricing/page.mdx
index 6441a91ba7..a2320b18f7 100644
--- a/www/apps/resources/app/commerce-modules/pricing/page.mdx
+++ b/www/apps/resources/app/commerce-modules/pricing/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/price-lists) to learn how to
-Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Pricing Module.
+Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Pricing Module.
@@ -34,7 +34,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Pricing Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/product/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/product/links-to-other-modules/page.mdx
index a4a27ca048..9da28c0ea8 100644
--- a/www/apps/resources/app/commerce-modules/product/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/product/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Product Module and other commerce modules.
+This document showcases the module links defined between the Product Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/product/page.mdx b/www/apps/resources/app/commerce-modules/product/page.mdx
index ce27580888..021f78bbe2 100644
--- a/www/apps/resources/app/commerce-modules/product/page.mdx
+++ b/www/apps/resources/app/commerce-modules/product/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/products) to learn how to ma
-Medusa has product related features available out-of-the-box through the Product Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Product Module.
+Medusa has product related features available out-of-the-box through the Product Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Product Module.
@@ -32,7 +32,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Product Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/promotion/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/promotion/links-to-other-modules/page.mdx
index fdddd1e86a..a4bc4bbf80 100644
--- a/www/apps/resources/app/commerce-modules/promotion/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/promotion/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Promotion Module and other commerce modules.
+This document showcases the module links defined between the Promotion Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/promotion/page.mdx b/www/apps/resources/app/commerce-modules/promotion/page.mdx
index fb1cc299c9..b815bd0422 100644
--- a/www/apps/resources/app/commerce-modules/promotion/page.mdx
+++ b/www/apps/resources/app/commerce-modules/promotion/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/promotions) to learn how to
-Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Promotion Module.
+Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Promotion Module.
@@ -33,7 +33,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use the Promotion Module
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx
index 12d4bf8cfc..53b4283c27 100644
--- a/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Region Module and other commerce modules.
+This document showcases the module links defined between the Region Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/region/page.mdx b/www/apps/resources/app/commerce-modules/region/page.mdx
index 870e3c71ed..789df05e2a 100644
--- a/www/apps/resources/app/commerce-modules/region/page.mdx
+++ b/www/apps/resources/app/commerce-modules/region/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/regions) to learn h
-Medusa has region related features available out-of-the-box through the Region Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Region Module.
+Medusa has region related features available out-of-the-box through the Region Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Region Module.
@@ -34,7 +34,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use Region Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx
index 02adf1e836..c1a224652a 100644
--- a/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Sales Channel Module and other commerce modules.
+This document showcases the module links defined between the Sales Channel Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/sales-channel/page.mdx b/www/apps/resources/app/commerce-modules/sales-channel/page.mdx
index 6bbc447d73..3207e85ccf 100644
--- a/www/apps/resources/app/commerce-modules/sales-channel/page.mdx
+++ b/www/apps/resources/app/commerce-modules/sales-channel/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/sales-channels) to
-Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Sales Channel Module.
+Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Sales Channel Module.
@@ -45,7 +45,7 @@ Some use case examples for using a sales channel:
## How to Use Sales Channel Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx
index b1bc926412..31d60e73cb 100644
--- a/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Stock Location Module and other commerce modules.
+This document showcases the module links defined between the Stock Location Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/stock-location/page.mdx b/www/apps/resources/app/commerce-modules/stock-location/page.mdx
index 9fcdc1c373..0ebb399d16 100644
--- a/www/apps/resources/app/commerce-modules/stock-location/page.mdx
+++ b/www/apps/resources/app/commerce-modules/stock-location/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/locations-and-shipp
-Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Stock Location Module.
+Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Stock Location Module.
@@ -31,7 +31,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use Stock Location Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx b/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx
index cd6d21b538..3bc2c0bbf6 100644
--- a/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx
+++ b/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx
@@ -6,7 +6,7 @@ export const metadata = {
# {metadata.title}
-This document showcases the module links defined between the Store Module and other commerce modules.
+This document showcases the module links defined between the Store Module and other Commerce Modules.
## Summary
diff --git a/www/apps/resources/app/commerce-modules/store/page.mdx b/www/apps/resources/app/commerce-modules/store/page.mdx
index 8d6312da88..fdc21a7b14 100644
--- a/www/apps/resources/app/commerce-modules/store/page.mdx
+++ b/www/apps/resources/app/commerce-modules/store/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/store) to learn how
-Medusa has store related features available out-of-the-box through the Store Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Store Module.
+Medusa has store related features available out-of-the-box through the Store Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Store Module.
@@ -31,7 +31,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use Store Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/tax/page.mdx b/www/apps/resources/app/commerce-modules/tax/page.mdx
index 9141f9afb2..284b9b3260 100644
--- a/www/apps/resources/app/commerce-modules/tax/page.mdx
+++ b/www/apps/resources/app/commerce-modules/tax/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/tax-regions) to lea
-Medusa has tax related features available out-of-the-box through the Tax Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Tax Module.
+Medusa has tax related features available out-of-the-box through the Tax Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this Tax Module.
@@ -32,7 +32,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use Tax Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/commerce-modules/user/page.mdx b/www/apps/resources/app/commerce-modules/user/page.mdx
index 39b99da1c4..0713837ba7 100644
--- a/www/apps/resources/app/commerce-modules/user/page.mdx
+++ b/www/apps/resources/app/commerce-modules/user/page.mdx
@@ -14,7 +14,7 @@ Refer to the [Medusa Admin User Guide](!user-guide!/settings/users) to learn how
-Medusa has user related features available out-of-the-box through the User Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this User Module.
+Medusa has user related features available out-of-the-box through the User Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in Commerce Modules, such as this User Module.
@@ -31,7 +31,7 @@ Learn more about why modules are isolated in [this documentation](!docs!/learn/f
## How to Use User Module's Service
-In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
+In your Medusa application, you build flows around Commerce Modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
diff --git a/www/apps/resources/app/deployment/medusa-application/railway/page.mdx b/www/apps/resources/app/deployment/medusa-application/railway/page.mdx
index f0c91ba719..23d47206a3 100644
--- a/www/apps/resources/app/deployment/medusa-application/railway/page.mdx
+++ b/www/apps/resources/app/deployment/medusa-application/railway/page.mdx
@@ -110,11 +110,11 @@ So, add the following script in `package.json`:
By default, your Medusa application uses modules and providers useful for development, such as the In-Memory Cache Module or the Local File Module Provider. It’s highly recommended to instead use modules and providers suitable for production, including:
-- [Redis Cache Module](../../../architectural-modules/cache/redis/page.mdx)
-- [Redis Event Bus Module](../../../architectural-modules/event/redis/page.mdx)
-- [Workflow Engine Redis Module](../../../architectural-modules/workflow-engine/redis/page.mdx)
-- [S3 File Module Provider](../../../architectural-modules/file/s3/page.mdx) (or other file module providers production-ready).
-- [SendGrid Notification Module Provider](../../../architectural-modules/notification/sendgrid/page.mdx) (or other notification module providers production-ready).
+- [Redis Cache Module](../../../infrastructure-modules/cache/redis/page.mdx)
+- [Redis Event Bus Module](../../../infrastructure-modules/event/redis/page.mdx)
+- [Workflow Engine Redis Module](../../../infrastructure-modules/workflow-engine/redis/page.mdx)
+- [S3 File Module Provider](../../../infrastructure-modules/file/s3/page.mdx) (or other file module providers production-ready).
+- [SendGrid Notification Module Provider](../../../infrastructure-modules/notification/sendgrid/page.mdx) (or other notification module providers production-ready).
For example, add the following modules to `medusa-config.ts`:
@@ -150,7 +150,7 @@ module.exports = defineConfig({
-Check out the [Integrations](../../../integrations/page.mdx) and [Architectural Modules](../../../architectural-modules/page.mdx) documentation for other modules and providers to use.
+Check out the [Integrations](../../../integrations/page.mdx) and [Infrastructure Modules](../../../infrastructure-modules/page.mdx) documentation for other modules and providers to use.
diff --git a/www/apps/resources/app/examples/guides/custom-item-price/page.mdx b/www/apps/resources/app/examples/guides/custom-item-price/page.mdx
index 03a79afb57..4e7aa8021b 100644
--- a/www/apps/resources/app/examples/guides/custom-item-price/page.mdx
+++ b/www/apps/resources/app/examples/guides/custom-item-price/page.mdx
@@ -40,7 +40,7 @@ export const metadata = {
In this guide, you'll learn how to add line items with custom prices to a cart in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. These features include managing carts and adding line items to them.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. These features include managing carts and adding line items to them.
By default, you can add product variants to the cart, where the price of its associated line item is based on the product variant's price. However, you can build customizations to add line items with custom prices to the cart. This is useful when integrating an Enterprise Resource Planning (ERP), Product Information Management (PIM), or other third-party services that provide real-time prices for your products.
diff --git a/www/apps/resources/app/examples/guides/quote-management/page.mdx b/www/apps/resources/app/examples/guides/quote-management/page.mdx
index 9a358d1998..59b78c27a8 100644
--- a/www/apps/resources/app/examples/guides/quote-management/page.mdx
+++ b/www/apps/resources/app/examples/guides/quote-management/page.mdx
@@ -40,7 +40,7 @@ export const metadata = {
In this guide, you'll learn how to implement quote management in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx) which are available out-of-the-box.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx) which are available out-of-the-box.
By default, the Medusa application provides standard commerce features for orders and carts. However, Medusa's customization capabilities facilitate extending existing features to implement quote-management features.
diff --git a/www/apps/resources/app/examples/page.mdx b/www/apps/resources/app/examples/page.mdx
index 29c224021c..eaf9b15d02 100644
--- a/www/apps/resources/app/examples/page.mdx
+++ b/www/apps/resources/app/examples/page.mdx
@@ -1694,7 +1694,7 @@ A module link forms an association between two data models of different modules,
### Define a Link
-To define a link between your custom module and a commerce module, such as the Product Module:
+To define a link between your custom module and a Commerce Module, such as the Product Module:
1. Create the file `src/links/blog-product.ts` with the following content:
@@ -2533,7 +2533,7 @@ const { transaction } = await myLongRunningWorkflow(req.scope)
.run()
```
-2. In an API route, workflow, or other resource, change a step's status to successful using the [Worfklow Engine Module](../architectural-modules/workflow-engine/page.mdx):
+2. In an API route, workflow, or other resource, change a step's status to successful using the [Worfklow Engine Module](../infrastructure-modules/workflow-engine/page.mdx):
export const stepSuccessHighlights = [
["5", "setStepSuccess", "Change a step's status to success"],
@@ -2561,7 +2561,7 @@ await workflowEngineService.setStepSuccess({
})
```
-3. In an API route, workflow, or other resource, change a step's status to failure using the [Worfklow Engine Module](../architectural-modules/workflow-engine/page.mdx):
+3. In an API route, workflow, or other resource, change a step's status to failure using the [Worfklow Engine Module](../infrastructure-modules/workflow-engine/page.mdx):
export const stepFailureHighlights = [
["5", "setStepFailure", "Change a step's status to failure"],
@@ -3341,7 +3341,7 @@ npm run test:modules
## Commerce Modules
-Medusa provides all its commerce features as separate commerce modules, such as the Product or Order modules.
+Medusa provides all its commerce features as separate Commerce Modules, such as the Product or Order modules.
diff --git a/www/apps/resources/app/how-to-tutorials/tutorials/abandoned-cart/page.mdx b/www/apps/resources/app/how-to-tutorials/tutorials/abandoned-cart/page.mdx
index 6ca48c6b23..952f8bab95 100644
--- a/www/apps/resources/app/how-to-tutorials/tutorials/abandoned-cart/page.mdx
+++ b/www/apps/resources/app/how-to-tutorials/tutorials/abandoned-cart/page.mdx
@@ -41,9 +41,9 @@ export const metadata = {
In this tutorial, you will learn how to send notifications to customers who have abandoned their carts.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx), which are available out-of-the-box. These features include cart-management capabilities.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx), which are available out-of-the-box. These features include cart-management capabilities.
-Medusa's [Notification Module](../../../architectural-modules/notification/page.mdx) allows you to send notifications to users or customers, such as password reset emails, order confirmation SMS, or other types of notifications.
+Medusa's [Notification Module](../../../infrastructure-modules/notification/page.mdx) allows you to send notifications to users or customers, such as password reset emails, order confirmation SMS, or other types of notifications.
In this tutorial, you will use the Notification Module to send an email to customers who have abandoned their carts. The email will contain a link to recover the customer's cart, encouraging them to complete their purchase. You will use SendGrid to send the emails, but you can also use other email providers.
@@ -129,9 +129,9 @@ Check out the [troubleshooting guides](../../../troubleshooting/create-medusa-ap
Medusa's Notification Module provides the general functionality to send notifications, but the sending logic is implemented in a module provider. This allows you to integrate the email provider of your choice.
-To send the cart-abandonment emails, you will use SendGrid. Medusa provides a [SendGrid Notification Module Provider](../../../architectural-modules/notification/sendgrid/page.mdx) that you can use to send emails.
+To send the cart-abandonment emails, you will use SendGrid. Medusa provides a [SendGrid Notification Module Provider](../../../infrastructure-modules/notification/sendgrid/page.mdx) that you can use to send emails.
-Alternatively, you can use [other Notification Module Providers](../../../architectural-modules/notification/page.mdx#what-is-a-notification-module-provider) or [create a custom provider](/references/notification-provider-module).
+Alternatively, you can use [other Notification Module Providers](../../../infrastructure-modules/notification/page.mdx#what-is-a-notification-module-provider) or [create a custom provider](/references/notification-provider-module).
To set up SendGrid, add the SendGrid Notification Module Provider to `medusa-config.ts`:
diff --git a/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx b/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx
index 907d91cbc0..37f239d4c9 100644
--- a/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx
+++ b/www/apps/resources/app/how-to-tutorials/tutorials/loyalty-points/page.mdx
@@ -30,7 +30,7 @@ Medusa Cloud provides a beta Store Credits feature that facilitates building a l
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx), which are available out-of-the-box. These features include management capabilities related to carts, orders, promotions, and more.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx), which are available out-of-the-box. These features include management capabilities related to carts, orders, promotions, and more.
A loyalty point system allows customers to earn points for purchases, which can be redeemed for discounts or rewards. In this tutorial, you'll learn how to customize the Medusa application to implement a loyalty points system.
diff --git a/www/apps/resources/app/how-to-tutorials/tutorials/product-reviews/page.mdx b/www/apps/resources/app/how-to-tutorials/tutorials/product-reviews/page.mdx
index 868d9a7571..0322f5088b 100644
--- a/www/apps/resources/app/how-to-tutorials/tutorials/product-reviews/page.mdx
+++ b/www/apps/resources/app/how-to-tutorials/tutorials/product-reviews/page.mdx
@@ -40,7 +40,7 @@ export const metadata = {
In this tutorial, you'll learn how to implement product reviews in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. The features include product-management features.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. The features include product-management features.
Medusa doesn't provide product reviews out-of-the-box, but the Medusa Framework facilitates implementing customizations like product reviews. In this tutorial, you'll learn how to customize the Medusa server, Admin dashboard, and Next.js Starter Storefront to implement product reviews.
diff --git a/www/apps/resources/app/architectural-modules/cache/create/page.mdx b/www/apps/resources/app/infrastructure-modules/cache/create/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/cache/create/page.mdx
rename to www/apps/resources/app/infrastructure-modules/cache/create/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/cache/in-memory/page.mdx b/www/apps/resources/app/infrastructure-modules/cache/in-memory/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/cache/in-memory/page.mdx
rename to www/apps/resources/app/infrastructure-modules/cache/in-memory/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/cache/page.mdx b/www/apps/resources/app/infrastructure-modules/cache/page.mdx
similarity index 96%
rename from www/apps/resources/app/architectural-modules/cache/page.mdx
rename to www/apps/resources/app/infrastructure-modules/cache/page.mdx
index 325343fafd..8d4445cf10 100644
--- a/www/apps/resources/app/architectural-modules/cache/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/cache/page.mdx
@@ -68,7 +68,7 @@ Medusa provides the following Cache Modules. You can use one of them, or [Create
items={[
{
title: "In-Memory",
- href: "/architectural-modules/cache/in-memory",
+ href: "/infrastructure-modules/cache/in-memory",
badge: {
variant: "neutral",
children: "For Development"
@@ -76,7 +76,7 @@ Medusa provides the following Cache Modules. You can use one of them, or [Create
},
{
title: "Redis",
- href: "/architectural-modules/cache/redis",
+ href: "/infrastructure-modules/cache/redis",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/cache/redis/page.mdx b/www/apps/resources/app/infrastructure-modules/cache/redis/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/cache/redis/page.mdx
rename to www/apps/resources/app/infrastructure-modules/cache/redis/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/event/create/page.mdx b/www/apps/resources/app/infrastructure-modules/event/create/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/event/create/page.mdx
rename to www/apps/resources/app/infrastructure-modules/event/create/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/event/local/page.mdx b/www/apps/resources/app/infrastructure-modules/event/local/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/event/local/page.mdx
rename to www/apps/resources/app/infrastructure-modules/event/local/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/event/page.mdx b/www/apps/resources/app/infrastructure-modules/event/page.mdx
similarity index 96%
rename from www/apps/resources/app/architectural-modules/event/page.mdx
rename to www/apps/resources/app/infrastructure-modules/event/page.mdx
index d2dbf39e61..cd8b572b86 100644
--- a/www/apps/resources/app/architectural-modules/event/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/event/page.mdx
@@ -80,7 +80,7 @@ Medusa provides the following Event Modules. You can use one of them, or [Create
items={[
{
title: "Local",
- href: "/architectural-modules/event/local",
+ href: "/infrastructure-modules/event/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -88,7 +88,7 @@ Medusa provides the following Event Modules. You can use one of them, or [Create
},
{
title: "Redis",
- href: "/architectural-modules/event/redis",
+ href: "/infrastructure-modules/event/redis",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/event/redis/page.mdx b/www/apps/resources/app/infrastructure-modules/event/redis/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/event/redis/page.mdx
rename to www/apps/resources/app/infrastructure-modules/event/redis/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/file/local/page.mdx b/www/apps/resources/app/infrastructure-modules/file/local/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/file/local/page.mdx
rename to www/apps/resources/app/infrastructure-modules/file/local/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/file/page.mdx b/www/apps/resources/app/infrastructure-modules/file/page.mdx
similarity index 96%
rename from www/apps/resources/app/architectural-modules/file/page.mdx
rename to www/apps/resources/app/infrastructure-modules/file/page.mdx
index 5e05597b08..d75a730b5d 100644
--- a/www/apps/resources/app/architectural-modules/file/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/file/page.mdx
@@ -78,7 +78,7 @@ This is useful for development. However, for production, it’s highly recommend
items={[
{
title: "Local",
- href: "/architectural-modules/file/local",
+ href: "/infrastructure-modules/file/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -86,7 +86,7 @@ This is useful for development. However, for production, it’s highly recommend
},
{
title: "AWS S3 (and Compatible APIs)",
- href: "/architectural-modules/file/s3",
+ href: "/infrastructure-modules/file/s3",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/file/s3/page.mdx b/www/apps/resources/app/infrastructure-modules/file/s3/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/file/s3/page.mdx
rename to www/apps/resources/app/infrastructure-modules/file/s3/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/locking/page.mdx b/www/apps/resources/app/infrastructure-modules/locking/page.mdx
similarity index 98%
rename from www/apps/resources/app/architectural-modules/locking/page.mdx
rename to www/apps/resources/app/infrastructure-modules/locking/page.mdx
index bebf61511f..311d04ea7a 100644
--- a/www/apps/resources/app/architectural-modules/locking/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/locking/page.mdx
@@ -146,7 +146,7 @@ Medusa provides the following Locking Module Providers. You can use one of them,
items={[
{
title: "Redis",
- href: "/architectural-modules/locking/redis",
+ href: "/infrastructure-modules/locking/redis",
badge: {
variant: "green",
children: "Recommended"
@@ -154,7 +154,7 @@ Medusa provides the following Locking Module Providers. You can use one of them,
},
{
title: "PostgreSQL",
- href: "/architectural-modules/locking/postgres",
+ href: "/infrastructure-modules/locking/postgres",
}
]}
/>
diff --git a/www/apps/resources/app/architectural-modules/locking/postgres/page.mdx b/www/apps/resources/app/infrastructure-modules/locking/postgres/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/locking/postgres/page.mdx
rename to www/apps/resources/app/infrastructure-modules/locking/postgres/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/locking/redis/page.mdx b/www/apps/resources/app/infrastructure-modules/locking/redis/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/locking/redis/page.mdx
rename to www/apps/resources/app/infrastructure-modules/locking/redis/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/notification/local/page.mdx b/www/apps/resources/app/infrastructure-modules/notification/local/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/notification/local/page.mdx
rename to www/apps/resources/app/infrastructure-modules/notification/local/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/notification/page.mdx b/www/apps/resources/app/infrastructure-modules/notification/page.mdx
similarity index 97%
rename from www/apps/resources/app/architectural-modules/notification/page.mdx
rename to www/apps/resources/app/infrastructure-modules/notification/page.mdx
index bfd293fb2a..a39a834872 100644
--- a/www/apps/resources/app/architectural-modules/notification/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/notification/page.mdx
@@ -73,7 +73,7 @@ Medusa provides other Notification Modules that actually send notifications, suc
items={[
{
title: "Local",
- href: "/architectural-modules/notification/local",
+ href: "/infrastructure-modules/notification/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -81,7 +81,7 @@ Medusa provides other Notification Modules that actually send notifications, suc
},
{
title: "SendGrid",
- href: "/architectural-modules/notification/sendgrid",
+ href: "/infrastructure-modules/notification/sendgrid",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx b/www/apps/resources/app/infrastructure-modules/notification/send-notification/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx
rename to www/apps/resources/app/infrastructure-modules/notification/send-notification/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/notification/sendgrid/page.mdx b/www/apps/resources/app/infrastructure-modules/notification/sendgrid/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/notification/sendgrid/page.mdx
rename to www/apps/resources/app/infrastructure-modules/notification/sendgrid/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/page.mdx b/www/apps/resources/app/infrastructure-modules/page.mdx
similarity index 81%
rename from www/apps/resources/app/architectural-modules/page.mdx
rename to www/apps/resources/app/infrastructure-modules/page.mdx
index a3835b6987..63b519ec96 100644
--- a/www/apps/resources/app/architectural-modules/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/page.mdx
@@ -1,16 +1,16 @@
import { CardList } from "docs-ui"
export const metadata = {
- title: `Architectural Modules`,
+ title: `Infrastructure Modules`,
}
# {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 Infrastructure Modules. An Infrastructure 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 in [the Architecture documentation](!docs!/learn/introduction/architecture).
-This section of the documentation showcases Medusa's Architectural Modules, how they work, and how to use them in your Medusa application.
+This section of the documentation showcases Medusa's Infrastructure Modules, how they work, and how to use them in your Medusa application.
## Cache Module
@@ -22,7 +22,7 @@ The following Cache modules are provided by Medusa. You can also create your own
items={[
{
title: "In-Memory",
- href: "/architectural-modules/cache/in-memory",
+ href: "/infrastructure-modules/cache/in-memory",
badge: {
variant: "neutral",
children: "For Development"
@@ -30,7 +30,7 @@ The following Cache modules are provided by Medusa. You can also create your own
},
{
title: "Redis",
- href: "/architectural-modules/cache/redis",
+ href: "/infrastructure-modules/cache/redis",
badge: {
variant: "green",
children: "For Production"
@@ -51,7 +51,7 @@ The following Event modules are provided by Medusa. You can also create your own
items={[
{
title: "Local",
- href: "/architectural-modules/event/local",
+ href: "/infrastructure-modules/event/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -59,7 +59,7 @@ The following Event modules are provided by Medusa. You can also create your own
},
{
title: "Redis",
- href: "/architectural-modules/event/redis",
+ href: "/infrastructure-modules/event/redis",
badge: {
variant: "green",
children: "For Production"
@@ -80,7 +80,7 @@ The File Module has module providers that implement the underlying logic of hand
items={[
{
title: "Local",
- href: "/architectural-modules/file/local",
+ href: "/infrastructure-modules/file/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -88,7 +88,7 @@ The File Module has module providers that implement the underlying logic of hand
},
{
title: "AWS S3 (and Compatible APIs)",
- href: "/architectural-modules/file/s3",
+ href: "/infrastructure-modules/file/s3",
badge: {
variant: "green",
children: "For Production"
@@ -109,7 +109,7 @@ The Locking Module uses module providers that implement the underlying logic of
items={[
{
title: "Redis",
- href: "/architectural-modules/locking/redis",
+ href: "/infrastructure-modules/locking/redis",
badge: {
variant: "green",
children: "Recommended"
@@ -117,7 +117,7 @@ The Locking Module uses module providers that implement the underlying logic of
},
{
title: "PostgreSQL",
- href: "/architectural-modules/locking/postgres",
+ href: "/infrastructure-modules/locking/postgres",
}
]}
/>
@@ -134,7 +134,7 @@ The Notification Module has module providers that implement the underlying logic
items={[
{
title: "Local",
- href: "/architectural-modules/notification/local",
+ href: "/infrastructure-modules/notification/local",
badge: {
variant: "neutral",
children: "For Development"
@@ -142,7 +142,7 @@ The Notification Module has module providers that implement the underlying logic
},
{
title: "SendGrid",
- href: "/architectural-modules/notification/sendgrid",
+ href: "/infrastructure-modules/notification/sendgrid",
badge: {
variant: "green",
children: "For Production"
@@ -157,7 +157,7 @@ The Notification Module has module providers that implement the underlying logic
items={[
{
title: "Send Notification",
- href: "/architectural-modules/notification/send-notification"
+ href: "/infrastructure-modules/notification/send-notification"
},
{
title: "Create Notification Provider",
@@ -186,7 +186,7 @@ The following Workflow Engine modules are provided by Medusa.
items={[
{
title: "In-Memory",
- href: "/architectural-modules/workflow-engine/in-memory",
+ href: "/infrastructure-modules/workflow-engine/in-memory",
badge: {
variant: "neutral",
children: "For Development"
@@ -194,7 +194,7 @@ The following Workflow Engine modules are provided by Medusa.
},
{
title: "Redis",
- href: "/architectural-modules/workflow-engine/redis",
+ href: "/infrastructure-modules/workflow-engine/redis",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/workflow-engine/how-to-use/page.mdx b/www/apps/resources/app/infrastructure-modules/workflow-engine/how-to-use/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/workflow-engine/how-to-use/page.mdx
rename to www/apps/resources/app/infrastructure-modules/workflow-engine/how-to-use/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/workflow-engine/in-memory/page.mdx b/www/apps/resources/app/infrastructure-modules/workflow-engine/in-memory/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/workflow-engine/in-memory/page.mdx
rename to www/apps/resources/app/infrastructure-modules/workflow-engine/in-memory/page.mdx
diff --git a/www/apps/resources/app/architectural-modules/workflow-engine/page.mdx b/www/apps/resources/app/infrastructure-modules/workflow-engine/page.mdx
similarity index 95%
rename from www/apps/resources/app/architectural-modules/workflow-engine/page.mdx
rename to www/apps/resources/app/infrastructure-modules/workflow-engine/page.mdx
index 5e3dbca192..5c61b7a264 100644
--- a/www/apps/resources/app/architectural-modules/workflow-engine/page.mdx
+++ b/www/apps/resources/app/infrastructure-modules/workflow-engine/page.mdx
@@ -74,7 +74,7 @@ Medusa provides the following Workflow Engine Modules.
items={[
{
title: "In-Memory",
- href: "/architectural-modules/workflow-engine/in-memory",
+ href: "/infrastructure-modules/workflow-engine/in-memory",
badge: {
variant: "neutral",
children: "For Development"
@@ -82,7 +82,7 @@ Medusa provides the following Workflow Engine Modules.
},
{
title: "Redis",
- href: "/architectural-modules/workflow-engine/redis",
+ href: "/infrastructure-modules/workflow-engine/redis",
badge: {
variant: "green",
children: "For Production"
diff --git a/www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx b/www/apps/resources/app/infrastructure-modules/workflow-engine/redis/page.mdx
similarity index 100%
rename from www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx
rename to www/apps/resources/app/infrastructure-modules/workflow-engine/redis/page.mdx
diff --git a/www/apps/resources/app/integrations/guides/algolia/page.mdx b/www/apps/resources/app/integrations/guides/algolia/page.mdx
index 7c791c755f..e8878ab997 100644
--- a/www/apps/resources/app/integrations/guides/algolia/page.mdx
+++ b/www/apps/resources/app/integrations/guides/algolia/page.mdx
@@ -690,7 +690,7 @@ Since you export a `POST` route handler function, you expose an `API` route at `
1. A request object with details and context on the request, such as body parameters or authenticated user details.
2. A response object to manipulate and send the response.
-In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](../../../architectural-modules/event/page.mdx). This module manages events and their subscribers.
+In the route handler, you use the Medusa container that is available in the request object to resolve the [Event Module](../../../infrastructure-modules/event/page.mdx). This module manages events and their subscribers.
Then, you emit the `algolia.sync` event using the Event Module's `emit` method, passing it the event name.
diff --git a/www/apps/resources/app/integrations/guides/magento/page.mdx b/www/apps/resources/app/integrations/guides/magento/page.mdx
index 2ae8e9eb72..8e47865ca6 100644
--- a/www/apps/resources/app/integrations/guides/magento/page.mdx
+++ b/www/apps/resources/app/integrations/guides/magento/page.mdx
@@ -646,7 +646,7 @@ Where:
-Medusa supports integrating third-party services, such as [S3](../../../architectural-modules/file/s3/page.mdx), in a File Module Provider. Refer to the [File Module](../../../architectural-modules/file/page.mdx) documentation to find other module providers and how to create a custom provider.
+Medusa supports integrating third-party services, such as [S3](../../../infrastructure-modules/file/s3/page.mdx), in a File Module Provider. Refer to the [File Module](../../../infrastructure-modules/file/page.mdx) documentation to find other module providers and how to create a custom provider.
diff --git a/www/apps/resources/app/integrations/guides/sanity/page.mdx b/www/apps/resources/app/integrations/guides/sanity/page.mdx
index 015e717c94..21d2f0a5a4 100644
--- a/www/apps/resources/app/integrations/guides/sanity/page.mdx
+++ b/www/apps/resources/app/integrations/guides/sanity/page.mdx
@@ -1684,9 +1684,9 @@ Earlier in this guide when introducing workflows, you learned that you can track
### Retrieve Sync Executions API Route
-Medusa has a [workflow engine](../../../architectural-modules/workflow-engine/page.mdx) that manages workflow executions, roll-backs, and other functionalities under the hood.
+Medusa has a [workflow engine](../../../infrastructure-modules/workflow-engine/page.mdx) that manages workflow executions, roll-backs, and other functionalities under the hood.
-The workflow engine is an [architectural module](!docs!/learn/fundamentals/modules/architectural-modules), which can be replaced with a [Redis Workflow Engine](../../../architectural-modules/workflow-engine/redis/page.mdx), or a custom one of your choice, allowing you to take ownership of your application's tooling.
+The workflow engine is an [Infrastructure Module](!docs!/learn/fundamentals/modules/infrastructure-modules), which can be replaced with a [Redis Workflow Engine](../../../infrastructure-modules/workflow-engine/redis/page.mdx), or a custom one of your choice, allowing you to take ownership of your application's tooling.
In your customizations, you can resolve the workflow engine from the container and manage executions of a workflow, such as retrieve them and check their progress.
diff --git a/www/apps/resources/app/integrations/page.mdx b/www/apps/resources/app/integrations/page.mdx
index f435c50d7f..9f5096c85b 100644
--- a/www/apps/resources/app/integrations/page.mdx
+++ b/www/apps/resources/app/integrations/page.mdx
@@ -91,7 +91,7 @@ A File Module Provider uploads and manages assets, such as product images, on a
- For custom modules, the registration name is the key of the module in the `modules` configuration in `medusa-config.ts`.
- - For Medusa's commerce modules, use the `Modules` enum imported from `@medusajs/framework/utils`.
+ - For Medusa's Commerce Modules, use the `Modules` enum imported from `@medusajs/framework/utils`.
diff --git a/www/apps/resources/app/plugins/guides/wishlist/page.mdx b/www/apps/resources/app/plugins/guides/wishlist/page.mdx
index d9e38a4e1f..78bf5ba3c3 100644
--- a/www/apps/resources/app/plugins/guides/wishlist/page.mdx
+++ b/www/apps/resources/app/plugins/guides/wishlist/page.mdx
@@ -39,7 +39,7 @@ export const metadata = {
In this guide, you'll learn how to build a wishlist [plugin](!docs!/learn/fundamentals/plugins) in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx) which are available out-of-the-box.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx) which are available out-of-the-box.
Customers browsing your store may be interested in a product but not ready to buy it yet. They may want to save the product for later or share it with friends and family. A wishlist feature allows customers to save products they like and access them later.
@@ -396,7 +396,7 @@ The tables of the Wishlist Module's data models are now created in the database.
## Step 5: Link Wishlist Data Models with Core Models
-The Wishlist Module's data models store IDs of records in data models implemented in Medusa's core commerce modules, such as the ID of a customer or a product variant.
+The Wishlist Module's data models store IDs of records in data models implemented in Medusa's core Commerce Modules, such as the ID of a customer or a product variant.
However, modules are [isolated](!docs!/learn/fundamentals/modules/isolation) to ensure they're re-usable and don't have side effects when integrated into the Medusa application. So, to build associations between modules, you define [module links](!docs!/learn/fundamentals/module-links). A Module link associates two modules' data models while maintaining module isolation.
diff --git a/www/apps/resources/app/recipes/b2b/page.mdx b/www/apps/resources/app/recipes/b2b/page.mdx
index 39a0b60415..1707ad8028 100644
--- a/www/apps/resources/app/recipes/b2b/page.mdx
+++ b/www/apps/resources/app/recipes/b2b/page.mdx
@@ -18,7 +18,7 @@ Medusa has a ready-to-use B2B starter that you install and use in [this GitHub r
In a B2B store, you provide different types of customers with relevant pricing, products, shopping experience, and more.
-Medusa’s commerce modules, including Sales Channel, Customer, and Pricing modules facilitate implementing this setup. Medusa’s architecture and extendible nature allow you to customize your store based on your use case.
+Medusa’s Commerce Modules, including Sales Channel, Customer, and Pricing modules facilitate implementing this setup. Medusa’s architecture and extendible nature allow you to customize your store based on your use case.
diff --git a/www/apps/resources/app/recipes/commerce-automation/page.mdx b/www/apps/resources/app/recipes/commerce-automation/page.mdx
index ede0cfc9d5..7ce9ed50d8 100644
--- a/www/apps/resources/app/recipes/commerce-automation/page.mdx
+++ b/www/apps/resources/app/recipes/commerce-automation/page.mdx
@@ -37,7 +37,7 @@ You can use the Notification Module to send notifications when an action is trig
-The [Events reference](../../events-reference/page.mdx) shows an extensive list of events triggered for each commerce module.
+The [Events reference](../../events-reference/page.mdx) shows an extensive list of events triggered for each Commerce Module.
@@ -45,7 +45,7 @@ Medusa also provides Notification Module Providers that integrate with third-par
@@ -132,7 +132,7 @@ Medusa's commerce features are geared towards automating RMA flows and ensuring
Businesses use customer segmentation to organize customers into different groups and then apply different price rules to these groups.
-Medusa's commerce modules provide the necessary features to implement this use case:
+Medusa's Commerce Modules provide the necessary features to implement this use case:
- The Customer Module provides a customer groups feature to organize customers into customer groups.
- The Pricing Module provides the features to specify prices based on a condition, such as the group of the customer.
@@ -164,7 +164,7 @@ For example, to group customers with over twenty orders:
{
href: "/events-reference",
title: "Events Reference",
- text: "Check out triggered events by each commerce module.",
+ text: "Check out triggered events by each Commerce Module.",
icon: AcademicCapSolid,
},
]} />
diff --git a/www/apps/resources/app/recipes/commerce-automation/restock-notification/page.mdx b/www/apps/resources/app/recipes/commerce-automation/restock-notification/page.mdx
index 8286b9bc24..737335b308 100644
--- a/www/apps/resources/app/recipes/commerce-automation/restock-notification/page.mdx
+++ b/www/apps/resources/app/recipes/commerce-automation/restock-notification/page.mdx
@@ -31,7 +31,7 @@ export const metadata = {
In this guide, you'll learn how to notify customers when a variant is restocked in Medusa.
-When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [commerce modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. These features include managing the inventory of product variants in different stock locations and sales channels.
+When you install a Medusa application, you get a fully-fledged commerce platform with a Framework for customization. The Medusa application's commerce features are built around [Commerce Modules](../../../commerce-modules/page.mdx) which are available out-of-the-box. These features include managing the inventory of product variants in different stock locations and sales channels.
Customers browsing your store may be interested in a product that is currently out of stock. To keep the customer interested in your store and encourage them to purchase the product in the future, you can build customizations around Medusa's commerce features to subscribe customers to receive a notification when the product is restocked.
@@ -1088,9 +1088,9 @@ The `useQueryGraphStep` is from Medusa's workflows. So, you'll only implement th
### Optional Prerequisite: Notification Module Provider
-Within this workflow, you'll use Medusa's [Notification Module](../../../architectural-modules/notification/page.mdx) to send an email to the customer.
+Within this workflow, you'll use Medusa's [Notification Module](../../../infrastructure-modules/notification/page.mdx) to send an email to the customer.
-The module delegates the email sending to a module provider, such as [SendGrid](../../../architectural-modules/notification/sendgrid/page.mdx) or [Resend](../../../integrations/guides/resend/page.mdx). You can refer to their linked guides to set up either module providers.
+The module delegates the email sending to a module provider, such as [SendGrid](../../../infrastructure-modules/notification/sendgrid/page.mdx) or [Resend](../../../integrations/guides/resend/page.mdx). You can refer to their linked guides to set up either module providers.
Alternatively, for development and debugging purposes, you can use the default Notification Module Provider that only logs a message in the terminal instead of sending an email. To do that, add the following to the `modules` array in `medusa-config.ts`:
diff --git a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx
index 322a0db40c..9eaaea0e39 100644
--- a/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx
+++ b/www/apps/resources/app/recipes/digital-products/examples/standard/page.mdx
@@ -258,7 +258,7 @@ So, you can't have relations between data models in modules. Instead, you define
Links are relations between data models of different modules that maintain the isolation between the modules.
-In this step, you’ll define links between your module’s data models and data models from Medusa’s commerce modules:
+In this step, you’ll define links between your module’s data models and data models from Medusa’s Commerce Modules:
1. Link between the `DigitalProduct` model and the Product Module's `ProductVariant` model.
2. Link between the `DigitalProductOrder` model and the Order Module's `Order` model.
@@ -791,7 +791,7 @@ To upload the digital product media files, use Medusa’s File Module.
-Your Medusa application uses the local file module provider by default, which uploads files to a local directory. However, you can use other file module providers, such as the [S3 module provider](../../../../architectural-modules/file/s3/page.mdx).
+Your Medusa application uses the local file module provider by default, which uploads files to a local directory. However, you can use other file module providers, such as the [S3 module provider](../../../../infrastructure-modules/file/s3/page.mdx).
diff --git a/www/apps/resources/app/recipes/digital-products/page.mdx b/www/apps/resources/app/recipes/digital-products/page.mdx
index d2643dc8bf..10872b6c78 100644
--- a/www/apps/resources/app/recipes/digital-products/page.mdx
+++ b/www/apps/resources/app/recipes/digital-products/page.mdx
@@ -26,7 +26,7 @@ During development, you can use the Local File Module Provider, which is install
diff --git a/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx b/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx
index fd0e80bcb5..e1787b50bb 100644
--- a/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx
+++ b/www/apps/resources/app/recipes/subscriptions/examples/standard/page.mdx
@@ -306,7 +306,7 @@ So, you can't have relations between data models in modules. Instead, you define
Links are relations between data models of different modules that maintain the isolation between the modules.
-In this step, you’ll define links between the Subscription Module’s `Subscription` data model and the data models of Medusa’s commerce modules:
+In this step, you’ll define links between the Subscription Module’s `Subscription` data model and the data models of Medusa’s Commerce Modules:
1. Link between the `Subscription` data model and the Cart Module's `Cart` model.
2. Link between the `Subscription` data model and the Customer Module's `Customer` model.
diff --git a/www/apps/resources/app/storefront-development/guides/express-checkout/page.mdx b/www/apps/resources/app/storefront-development/guides/express-checkout/page.mdx
index b733f3bb79..a3d4183d47 100644
--- a/www/apps/resources/app/storefront-development/guides/express-checkout/page.mdx
+++ b/www/apps/resources/app/storefront-development/guides/express-checkout/page.mdx
@@ -2730,7 +2730,7 @@ While both the Medusa application and the Next.js storefront are running, either
By following this guide, you now have an express checkout storefront built with Next.js that connects to a Medusa commerce application.
-You can add more features to your storefront or commerce application. For example, you can integrate a [Notification Module Provider](../../../architectural-modules/notification/page.mdx) to send the customer a confirmation email when they place their order.
+You can add more features to your storefront or commerce application. For example, you can integrate a [Notification Module Provider](../../../infrastructure-modules/notification/page.mdx) to send the customer a confirmation email when they place their order.
If you're new to Medusa, check out the [main documentation](!docs!/learn), where you'll get a more in-depth learning of all the concepts you've used in this guide and more.
diff --git a/www/apps/resources/app/troubleshooting/_sections/s3/cloudflare-checksum.mdx b/www/apps/resources/app/troubleshooting/_sections/s3/cloudflare-checksum.mdx
index f619817b78..c86f06d196 100644
--- a/www/apps/resources/app/troubleshooting/_sections/s3/cloudflare-checksum.mdx
+++ b/www/apps/resources/app/troubleshooting/_sections/s3/cloudflare-checksum.mdx
@@ -1,4 +1,4 @@
-If you're using the [S3 File Module Provider](../../../architectural-modules/file/s3/page.mdx) to store files in Cloudflare and you get a checksum error similar to the following:
+If you're using the [S3 File Module Provider](../../../infrastructure-modules/file/s3/page.mdx) to store files in Cloudflare and you get a checksum error similar to the following:
```bash
Header 'x-amz-checksum-crc32' with value '0wpgCA==' not implemented
diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs
index c704984bb0..1bcd78b98f 100644
--- a/www/apps/resources/generated/edit-dates.mjs
+++ b/www/apps/resources/generated/edit-dates.mjs
@@ -4,20 +4,20 @@ export const generatedEditDates = {
"app/commerce-modules/auth/authentication-route/page.mdx": "2025-03-04T09:13:45.919Z",
"app/commerce-modules/auth/examples/page.mdx": "2024-10-15T15:02:13.794Z",
"app/commerce-modules/auth/module-options/page.mdx": "2025-03-11T08:56:10.338Z",
- "app/commerce-modules/auth/page.mdx": "2025-01-09T13:41:05.476Z",
+ "app/commerce-modules/auth/page.mdx": "2025-04-17T08:48:17.286Z",
"app/commerce-modules/cart/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/cart/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/cart/concepts/page.mdx": "2024-10-08T07:49:03.737Z",
"app/commerce-modules/cart/promotions/page.mdx": "2024-10-08T07:54:31.120Z",
"app/commerce-modules/cart/tax-lines/page.mdx": "2024-10-08T07:57:19.168Z",
- "app/commerce-modules/cart/page.mdx": "2025-01-09T13:41:05.278Z",
+ "app/commerce-modules/cart/page.mdx": "2025-04-17T08:48:27.822Z",
"app/commerce-modules/currency/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/currency/_events/page.mdx": "2024-07-03T19:27:13+03:00",
- "app/commerce-modules/currency/page.mdx": "2025-02-26T11:18:00.338Z",
+ "app/commerce-modules/currency/page.mdx": "2025-04-17T08:48:30.550Z",
"app/commerce-modules/customer/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/customer/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/customer/customer-accounts/page.mdx": "2025-02-26T11:17:42.327Z",
- "app/commerce-modules/customer/page.mdx": "2025-02-26T11:18:00.342Z",
+ "app/commerce-modules/customer/page.mdx": "2025-04-17T08:48:31.918Z",
"app/commerce-modules/fulfillment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/fulfillment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/fulfillment/concepts/page.mdx": "2024-06-19T13:02:16+00:00",
@@ -25,12 +25,12 @@ export const generatedEditDates = {
"app/commerce-modules/fulfillment/item-fulfillment/page.mdx": "2024-10-08T14:38:15.496Z",
"app/commerce-modules/fulfillment/module-options/page.mdx": "2024-10-15T12:51:56.118Z",
"app/commerce-modules/fulfillment/shipping-option/page.mdx": "2024-10-08T14:36:02.660Z",
- "app/commerce-modules/fulfillment/page.mdx": "2025-02-26T11:18:00.354Z",
+ "app/commerce-modules/fulfillment/page.mdx": "2025-04-17T08:48:19.367Z",
"app/commerce-modules/inventory/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/inventory/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/inventory/concepts/page.mdx": "2025-02-13T12:37:28.918Z",
"app/commerce-modules/inventory/inventory-in-flows/page.mdx": "2025-01-08T12:21:12.157Z",
- "app/commerce-modules/inventory/page.mdx": "2025-02-26T11:18:00.353Z",
+ "app/commerce-modules/inventory/page.mdx": "2025-04-17T08:48:24.991Z",
"app/commerce-modules/order/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/order/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/order/claim/page.mdx": "2025-02-26T11:23:55.003Z",
@@ -41,7 +41,7 @@ export const generatedEditDates = {
"app/commerce-modules/order/return/page.mdx": "2025-02-26T11:22:49.675Z",
"app/commerce-modules/order/tax-lines/page.mdx": "2024-10-09T10:22:49.335Z",
"app/commerce-modules/order/transactions/page.mdx": "2024-10-09T10:23:36.485Z",
- "app/commerce-modules/order/page.mdx": "2025-02-26T11:18:00.361Z",
+ "app/commerce-modules/order/page.mdx": "2025-04-17T08:48:15.314Z",
"app/commerce-modules/payment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/payment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/payment/module-options/page.mdx": "2024-10-15T12:51:40.574Z",
@@ -52,76 +52,76 @@ export const generatedEditDates = {
"app/commerce-modules/payment/payment-provider/page.mdx": "2025-02-26T11:25:50.645Z",
"app/commerce-modules/payment/payment-session/page.mdx": "2024-10-09T10:58:00.960Z",
"app/commerce-modules/payment/webhook-events/page.mdx": "2024-11-19T11:45:02.167Z",
- "app/commerce-modules/payment/page.mdx": "2025-02-26T11:18:00.361Z",
+ "app/commerce-modules/payment/page.mdx": "2025-04-17T08:48:11.702Z",
"app/commerce-modules/pricing/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/pricing/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/pricing/concepts/page.mdx": "2024-10-09T13:37:25.678Z",
"app/commerce-modules/pricing/price-calculation/page.mdx": "2024-10-09T13:43:14.038Z",
"app/commerce-modules/pricing/price-rules/page.mdx": "2024-10-09T13:38:47.112Z",
"app/commerce-modules/pricing/tax-inclusive-pricing/page.mdx": "2024-10-09T13:48:23.261Z",
- "app/commerce-modules/pricing/page.mdx": "2025-02-26T11:18:00.362Z",
+ "app/commerce-modules/pricing/page.mdx": "2025-04-17T08:48:29.165Z",
"app/commerce-modules/product/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/product/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/product/guides/price/page.mdx": "2024-12-25T15:10:37.730Z",
"app/commerce-modules/product/guides/price-with-taxes/page.mdx": "2024-12-25T15:10:40.879Z",
- "app/commerce-modules/product/page.mdx": "2025-02-26T11:18:00.365Z",
+ "app/commerce-modules/product/page.mdx": "2025-04-17T08:48:20.755Z",
"app/commerce-modules/promotion/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/promotion/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/promotion/actions/page.mdx": "2024-10-09T14:49:01.645Z",
"app/commerce-modules/promotion/application-method/page.mdx": "2024-06-26T07:55:59+00:00",
"app/commerce-modules/promotion/campaign/page.mdx": "2025-02-26T11:32:24.484Z",
"app/commerce-modules/promotion/concepts/page.mdx": "2025-02-26T11:31:54.391Z",
- "app/commerce-modules/promotion/page.mdx": "2025-02-26T11:18:00.366Z",
+ "app/commerce-modules/promotion/page.mdx": "2025-04-17T08:48:14.643Z",
"app/commerce-modules/region/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/region/_events/page.mdx": "2024-07-03T19:27:13+03:00",
- "app/commerce-modules/region/page.mdx": "2025-02-26T11:18:00.368Z",
+ "app/commerce-modules/region/page.mdx": "2025-04-17T08:48:22.808Z",
"app/commerce-modules/sales-channel/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/sales-channel/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/sales-channel/publishable-api-keys/page.mdx": "2025-02-26T11:33:40.415Z",
- "app/commerce-modules/sales-channel/page.mdx": "2025-02-26T11:18:00.368Z",
+ "app/commerce-modules/sales-channel/page.mdx": "2025-04-17T08:48:22.065Z",
"app/commerce-modules/stock-location/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/stock-location/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/stock-location/concepts/page.mdx": "2024-10-15T14:32:21.875Z",
- "app/commerce-modules/stock-location/page.mdx": "2025-02-26T11:18:00.382Z",
+ "app/commerce-modules/stock-location/page.mdx": "2025-04-17T08:48:26.441Z",
"app/commerce-modules/store/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/store/_events/page.mdx": "2024-07-03T19:27:13+03:00",
- "app/commerce-modules/store/page.mdx": "2025-02-26T11:18:00.382Z",
+ "app/commerce-modules/store/page.mdx": "2025-04-17T08:48:34.141Z",
"app/commerce-modules/tax/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/tax/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/tax/module-options/page.mdx": "2024-10-15T14:35:46.117Z",
"app/commerce-modules/tax/tax-calculation-with-provider/page.mdx": "2024-10-15T14:43:00.700Z",
"app/commerce-modules/tax/tax-rates-and-rules/page.mdx": "2025-02-26T11:35:15.214Z",
"app/commerce-modules/tax/tax-region/page.mdx": "2025-02-26T11:34:21.259Z",
- "app/commerce-modules/tax/page.mdx": "2025-02-26T11:18:00.382Z",
+ "app/commerce-modules/tax/page.mdx": "2025-04-17T08:48:12.479Z",
"app/commerce-modules/user/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/user/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/user/module-options/page.mdx": "2024-09-30T08:43:53.171Z",
"app/commerce-modules/user/user-creation-flows/page.mdx": "2025-02-26T11:35:54.685Z",
- "app/commerce-modules/user/page.mdx": "2025-02-26T11:18:00.382Z",
- "app/commerce-modules/page.mdx": "2025-03-10T13:51:30.338Z",
+ "app/commerce-modules/user/page.mdx": "2025-04-17T08:48:17.980Z",
+ "app/commerce-modules/page.mdx": "2025-04-17T08:48:34.855Z",
"app/create-medusa-app/page.mdx": "2025-01-16T10:00:25.975Z",
"app/deployment/admin/vercel/page.mdx": "2024-10-16T08:10:29.377Z",
- "app/deployment/medusa-application/railway/page.mdx": "2025-03-11T08:56:02.100Z",
+ "app/deployment/medusa-application/railway/page.mdx": "2025-04-17T08:28:58.981Z",
"app/deployment/storefront/vercel/page.mdx": "2025-03-21T07:19:24.818Z",
"app/deployment/page.mdx": "2024-11-25T14:31:45.277Z",
- "app/integrations/page.mdx": "2025-02-26T11:37:17.771Z",
+ "app/integrations/page.mdx": "2025-04-17T08:29:01.365Z",
"app/medusa-cli/page.mdx": "2024-08-28T11:25:32.382Z",
- "app/medusa-container-resources/page.mdx": "2025-01-06T11:19:35.623Z",
+ "app/medusa-container-resources/page.mdx": "2025-04-17T08:48:10.255Z",
"app/medusa-workflows-reference/page.mdx": "2025-01-20T08:21:29.962Z",
"app/nextjs-starter/page.mdx": "2025-02-26T11:37:47.137Z",
- "app/recipes/b2b/page.mdx": "2025-02-26T12:36:59.567Z",
- "app/recipes/commerce-automation/page.mdx": "2025-02-26T12:38:15.404Z",
- "app/recipes/digital-products/examples/standard/page.mdx": "2025-02-13T15:24:15.868Z",
- "app/recipes/digital-products/page.mdx": "2025-02-26T12:37:12.721Z",
+ "app/recipes/b2b/page.mdx": "2025-04-17T08:48:38.369Z",
+ "app/recipes/commerce-automation/page.mdx": "2025-04-17T08:48:37.663Z",
+ "app/recipes/digital-products/examples/standard/page.mdx": "2025-04-17T08:48:36.289Z",
+ "app/recipes/digital-products/page.mdx": "2025-04-17T08:29:01.230Z",
"app/recipes/ecommerce/page.mdx": "2025-02-26T12:20:52.092Z",
"app/recipes/marketplace/examples/vendors/page.mdx": "2025-03-18T15:28:32.122Z",
- "app/recipes/marketplace/page.mdx": "2024-10-03T13:07:44.153Z",
+ "app/recipes/marketplace/page.mdx": "2025-04-17T08:48:36.942Z",
"app/recipes/multi-region-store/page.mdx": "2025-02-26T12:38:50.292Z",
"app/recipes/omnichannel/page.mdx": "2025-02-26T12:22:08.331Z",
- "app/recipes/oms/page.mdx": "2025-02-26T12:41:00.030Z",
+ "app/recipes/oms/page.mdx": "2025-04-17T08:48:35.570Z",
"app/recipes/personalized-products/page.mdx": "2025-02-26T12:41:48.547Z",
"app/recipes/pos/page.mdx": "2025-02-26T12:42:52.949Z",
- "app/recipes/subscriptions/examples/standard/page.mdx": "2025-03-03T15:02:56.854Z",
+ "app/recipes/subscriptions/examples/standard/page.mdx": "2025-04-17T08:48:32.667Z",
"app/recipes/subscriptions/page.mdx": "2025-02-26T12:31:49.933Z",
"app/recipes/page.mdx": "2025-03-07T07:48:28.203Z",
"app/service-factory-reference/methods/create/page.mdx": "2024-07-31T17:01:33+03:00",
@@ -192,32 +192,32 @@ export const generatedEditDates = {
"app/commerce-modules/auth/auth-flows/page.mdx": "2025-01-13T11:31:35.361Z",
"app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"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/api-key/page.mdx": "2025-04-17T08:48:16.605Z",
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-25T13:26:27.176Z",
- "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-03-24T07:01:32.437Z",
+ "app/infrastructure-modules/page.mdx": "2025-04-17T08:29:00.388Z",
+ "app/infrastructure-modules/workflow-engine/redis/page.mdx": "2024-11-19T16:37:47.262Z",
+ "app/infrastructure-modules/notification/sendgrid/page.mdx": "2025-03-27T17:39:10.840Z",
"app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z",
- "app/architectural-modules/workflow-engine/page.mdx": "2025-03-12T11:55:18.789Z",
+ "app/infrastructure-modules/workflow-engine/page.mdx": "2025-04-17T08:29:00.881Z",
"app/_events-reference/page.mdx": "2024-07-03T19:27:13+03:00",
- "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/infrastructure-modules/cache/page.mdx": "2025-04-17T08:29:00.741Z",
+ "app/infrastructure-modules/file/s3/page.mdx": "2025-02-11T16:57:46.709Z",
"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": "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-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/infrastructure-modules/cache/redis/page.mdx": "2024-11-19T16:37:47.261Z",
+ "app/infrastructure-modules/event/redis/page.mdx": "2025-03-27T14:53:13.310Z",
+ "app/infrastructure-modules/event/local/page.mdx": "2025-03-27T14:53:13.309Z",
+ "app/infrastructure-modules/workflow-engine/in-memory/page.mdx": "2024-11-19T16:37:47.262Z",
+ "app/infrastructure-modules/cache/in-memory/page.mdx": "2024-11-19T16:37:47.261Z",
+ "app/infrastructure-modules/notification/local/page.mdx": "2024-11-19T16:37:47.262Z",
+ "app/infrastructure-modules/file/local/page.mdx": "2024-11-19T16:37:47.261Z",
+ "app/infrastructure-modules/notification/send-notification/page.mdx": "2025-03-27T14:53:13.311Z",
+ "app/infrastructure-modules/file/page.mdx": "2025-04-17T08:29:00.672Z",
+ "app/infrastructure-modules/event/page.mdx": "2025-04-17T08:29:00.488Z",
+ "app/infrastructure-modules/cache/create/page.mdx": "2025-03-27T14:53:13.309Z",
"app/admin-widget-injection-zones/page.mdx": "2024-12-24T08:48:36.154Z",
- "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",
+ "app/infrastructure-modules/notification/page.mdx": "2025-04-17T08:29:00.814Z",
+ "app/infrastructure-modules/event/create/page.mdx": "2025-03-27T14:53:13.309Z",
"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",
"references/core_flows/Order/functions/core_flows.Order.updateOrderEditItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:59.121Z",
@@ -425,7 +425,7 @@ export const generatedEditDates = {
"references/helper_steps/functions/helper_steps.updateRemoteLinksStep/page.mdx": "2025-01-17T16:43:22.238Z",
"references/helper_steps/types/helper_steps.DismissRemoteLinksStepInput/page.mdx": "2024-08-28T00:11:31.042Z",
"references/medusa_config/interfaces/medusa_config.AdminOptions/page.mdx": "2025-02-24T10:48:34.425Z",
- "references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx": "2025-02-24T10:48:34.448Z",
+ "references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx": "2025-04-17T08:50:17.065Z",
"references/medusa_config/interfaces/medusa_config.ProjectConfigOptions/page.mdx": "2025-02-11T11:36:46.417Z",
"references/modules/auth/page.mdx": "2024-11-25T17:49:28.795Z",
"references/modules/modules_sdk/page.mdx": "2025-02-24T10:48:34.485Z",
@@ -477,7 +477,7 @@ export const generatedEditDates = {
"references/stock_location_next/interfaces/stock_location_next.ShippingOptionTypeDTO/page.mdx": "2024-12-23T13:57:07.696Z",
"references/stock_location_next/interfaces/stock_location_next.ShippingProfileDTO/page.mdx": "2024-12-23T13:57:07.704Z",
"references/types/CommonTypes/interfaces/types.CommonTypes.AdminOptions/page.mdx": "2025-02-24T10:48:36.584Z",
- "references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx": "2025-02-24T10:48:36.591Z",
+ "references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx": "2025-04-17T08:50:17.064Z",
"references/types/CommonTypes/interfaces/types.CommonTypes.ProjectConfigOptions/page.mdx": "2025-02-11T11:36:48.560Z",
"references/types/DmlTypes/interfaces/types.DmlTypes.IDmlEntity/page.mdx": "2025-01-13T18:05:54.049Z",
"references/types/DmlTypes/types/types.DmlTypes.InferTypeOf/page.mdx": "2024-12-09T13:21:33.021Z",
@@ -1741,10 +1741,10 @@ export const generatedEditDates = {
"references/core_flows/Customer_Group/Workflows_Customer_Group/functions/core_flows.Customer_Group.Workflows_Customer_Group.updateCustomerGroupsWorkflow/page.mdx": "2025-03-04T13:33:40.865Z",
"references/core_flows/Defaults/Steps_Defaults/functions/core_flows.Defaults.Steps_Defaults.createDefaultStoreStep/page.mdx": "2025-01-13T17:30:23.305Z",
"references/core_flows/Defaults/Workflows_Defaults/functions/core_flows.Defaults.Workflows_Defaults.createDefaultsWorkflow/page.mdx": "2025-03-04T13:33:40.896Z",
- "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx": "2025-01-13T17:30:23.311Z",
- "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx": "2025-04-11T09:04:36.166Z",
- "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx": "2025-03-04T13:33:40.913Z",
- "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx": "2025-04-11T09:04:36.171Z",
+ "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx": "2025-04-17T08:42:13.668Z",
+ "references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx": "2025-04-17T08:42:17.200Z",
+ "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx": "2025-04-17T08:42:22.928Z",
+ "references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx": "2025-04-17T08:42:27.243Z",
"references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.cancelFulfillmentStep/page.mdx": "2025-01-13T17:30:23.344Z",
"references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createFulfillmentSets/page.mdx": "2025-04-11T09:04:36.199Z",
"references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.createServiceZonesStep/page.mdx": "2025-04-11T09:04:36.257Z",
@@ -1871,7 +1871,7 @@ export const generatedEditDates = {
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.deleteProductTypesStep/page.mdx": "2025-01-13T17:30:25.727Z",
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.deleteProductVariantsStep/page.mdx": "2025-01-13T17:30:25.696Z",
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.deleteProductsStep/page.mdx": "2025-01-13T17:30:25.698Z",
- "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx": "2025-04-11T09:04:40.828Z",
+ "references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx": "2025-04-17T08:42:38.910Z",
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.getAllProductsStep/page.mdx": "2025-02-11T11:36:43.882Z",
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.getProductsStep/page.mdx": "2025-04-11T09:04:40.771Z",
"references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.groupProductsForBatchStep/page.mdx": "2025-02-24T10:48:31.999Z",
@@ -1887,8 +1887,8 @@ export const generatedEditDates = {
"references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchLinkProductsToCollectionWorkflow/page.mdx": "2025-03-04T13:33:45.249Z",
"references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductVariantsWorkflow/page.mdx": "2025-03-04T13:33:45.257Z",
"references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.batchProductsWorkflow/page.mdx": "2025-04-11T09:04:40.887Z",
- "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx": "2025-03-04T13:33:45.391Z",
- "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx": "2025-03-04T13:33:45.397Z",
+ "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx": "2025-04-17T08:42:40.742Z",
+ "references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx": "2025-04-17T08:42:45.102Z",
"references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.upsertVariantPricesWorkflow/page.mdx": "2025-03-04T13:33:45.416Z",
"references/core_flows/Product_Category/Steps_Product_Category/functions/core_flows.Product_Category.Steps_Product_Category.createProductCategoriesStep/page.mdx": "2025-04-11T09:04:41.068Z",
"references/core_flows/Product_Category/Steps_Product_Category/functions/core_flows.Product_Category.Steps_Product_Category.deleteProductCategoriesStep/page.mdx": "2025-01-13T17:30:25.836Z",
@@ -2103,23 +2103,23 @@ export const generatedEditDates = {
"app/admin-components/layouts/single-column/page.mdx": "2024-10-07T11:16:06.435Z",
"app/admin-components/layouts/two-column/page.mdx": "2024-10-07T11:16:10.092Z",
"app/admin-components/components/forms/page.mdx": "2024-10-09T12:48:04.229Z",
- "app/commerce-modules/auth/reset-password/page.mdx": "2025-02-26T11:18:00.391Z",
+ "app/commerce-modules/auth/reset-password/page.mdx": "2025-04-17T08:29:01.697Z",
"app/storefront-development/customers/reset-password/page.mdx": "2025-03-27T14:46:51.424Z",
- "app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2025-03-14T14:33:38.886Z",
+ "app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2025-04-17T08:48:15.961Z",
"app/commerce-modules/cart/extend/page.mdx": "2024-12-25T12:48:59.149Z",
- "app/commerce-modules/cart/links-to-other-modules/page.mdx": "2025-03-14T14:33:26.754Z",
+ "app/commerce-modules/cart/links-to-other-modules/page.mdx": "2025-04-17T08:48:27.142Z",
"app/commerce-modules/customer/extend/page.mdx": "2024-12-25T15:54:37.789Z",
- "app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2025-03-14T14:37:12.681Z",
- "app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2025-03-14T14:38:15.246Z",
- "app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2025-03-14T14:41:14.683Z",
+ "app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2025-04-17T08:48:18.671Z",
+ "app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2025-04-17T08:48:24.291Z",
+ "app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2025-04-17T08:48:28.484Z",
"app/commerce-modules/product/extend/page.mdx": "2024-12-11T09:07:25.252Z",
- "app/commerce-modules/product/links-to-other-modules/page.mdx": "2025-03-14T15:07:30.349Z",
+ "app/commerce-modules/product/links-to-other-modules/page.mdx": "2025-04-17T08:48:20.042Z",
"app/commerce-modules/promotion/extend/page.mdx": "2024-12-11T09:07:24.137Z",
- "app/commerce-modules/promotion/links-to-other-modules/page.mdx": "2025-03-17T06:48:23.706Z",
+ "app/commerce-modules/promotion/links-to-other-modules/page.mdx": "2025-04-17T08:48:13.200Z",
"app/commerce-modules/order/edit/page.mdx": "2025-02-26T11:24:28.852Z",
- "app/commerce-modules/order/links-to-other-modules/page.mdx": "2025-03-14T14:39:37.366Z",
+ "app/commerce-modules/order/links-to-other-modules/page.mdx": "2025-04-17T08:48:13.934Z",
"app/commerce-modules/order/order-change/page.mdx": "2024-10-09T09:59:40.745Z",
- "app/commerce-modules/payment/links-to-other-modules/page.mdx": "2025-03-14T14:40:25.733Z",
+ "app/commerce-modules/payment/links-to-other-modules/page.mdx": "2025-04-17T08:48:10.982Z",
"references/core_flows/Common/Steps_Common/functions/core_flows.Common.Steps_Common.useQueryGraphStep/page.mdx": "2025-04-11T09:04:35.943Z",
"references/core_flows/Payment/Workflows_Payment/functions/core_flows.Payment.Workflows_Payment.processPaymentWorkflow/page.mdx": "2025-03-04T13:33:44.884Z",
"references/helper_steps/functions/helper_steps.useQueryGraphStep/page.mdx": "2025-01-17T16:43:22.242Z",
@@ -2167,11 +2167,11 @@ export const generatedEditDates = {
"references/fulfillment/interfaces/fulfillment.IFulfillmentModuleService/page.mdx": "2024-12-17T16:57:25.097Z",
"references/types/CommonTypes/interfaces/types.CommonTypes.RequestQueryFields/page.mdx": "2024-12-09T13:21:32.865Z",
"references/utils/utils.ProductUtils/page.mdx": "2025-02-24T10:48:43.141Z",
- "app/commerce-modules/region/links-to-other-modules/page.mdx": "2025-03-14T15:08:31.803Z",
- "app/commerce-modules/sales-channel/links-to-other-modules/page.mdx": "2025-03-14T15:09:08.416Z",
- "app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2025-03-14T15:04:43.112Z",
- "app/commerce-modules/store/links-to-other-modules/page.mdx": "2025-03-17T06:52:04.187Z",
- "app/examples/page.mdx": "2025-03-18T15:19:46.808Z",
+ "app/commerce-modules/region/links-to-other-modules/page.mdx": "2025-04-17T08:48:23.504Z",
+ "app/commerce-modules/sales-channel/links-to-other-modules/page.mdx": "2025-04-17T08:48:21.417Z",
+ "app/commerce-modules/stock-location/links-to-other-modules/page.mdx": "2025-04-17T08:48:25.729Z",
+ "app/commerce-modules/store/links-to-other-modules/page.mdx": "2025-04-17T08:48:33.388Z",
+ "app/examples/page.mdx": "2025-04-17T08:50:17.036Z",
"app/medusa-cli/commands/build/page.mdx": "2024-11-11T11:00:49.665Z",
"app/js-sdk/page.mdx": "2025-03-28T09:50:07.617Z",
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.apiKey/page.mdx": "2025-04-11T09:04:55.016Z",
@@ -3140,7 +3140,7 @@ export const generatedEditDates = {
"references/product/interfaces/product.FilterableProductProps/page.mdx": "2025-02-24T10:48:41.629Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminBatchProductVariantRequest/page.mdx": "2024-12-09T13:21:34.309Z",
"references/types/WorkflowTypes/ProductWorkflow/interfaces/types.WorkflowTypes.ProductWorkflow.ExportProductsDTO/page.mdx": "2025-02-11T11:36:51.281Z",
- "app/integrations/guides/sanity/page.mdx": "2025-03-05T10:58:58.807Z",
+ "app/integrations/guides/sanity/page.mdx": "2025-04-17T08:28:58.986Z",
"references/api_key/types/api_key.FindConfigOrder/page.mdx": "2024-11-25T17:49:28.715Z",
"references/auth/types/auth.FindConfigOrder/page.mdx": "2024-11-25T17:49:28.887Z",
"references/cart/types/cart.FindConfigOrder/page.mdx": "2024-11-25T17:49:29.455Z",
@@ -5575,7 +5575,7 @@ export const generatedEditDates = {
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-17T16:57:19.922Z",
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
- "app/recipes/commerce-automation/restock-notification/page.mdx": "2025-03-17T07:36:21.511Z",
+ "app/recipes/commerce-automation/restock-notification/page.mdx": "2025-04-17T08:48:39.058Z",
"app/integrations/guides/shipstation/page.mdx": "2025-02-26T11:21:46.879Z",
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-25T14:48:55.877Z",
"references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2025-03-04T13:33:40.584Z",
@@ -5723,8 +5723,8 @@ export const generatedEditDates = {
"app/commerce-modules/store/admin-widget-zones/page.mdx": "2024-12-24T08:46:28.646Z",
"app/commerce-modules/tax/admin-widget-zones/page.mdx": "2024-12-24T08:47:13.176Z",
"app/commerce-modules/user/admin-widget-zones/page.mdx": "2024-12-24T08:48:14.186Z",
- "app/commerce-modules/currency/links-to-other-modules/page.mdx": "2025-03-17T06:41:29.161Z",
- "app/commerce-modules/customer/links-to-other-modules/page.mdx": "2025-04-07T07:31:46.046Z",
+ "app/commerce-modules/currency/links-to-other-modules/page.mdx": "2025-04-17T08:48:29.866Z",
+ "app/commerce-modules/customer/links-to-other-modules/page.mdx": "2025-04-17T08:48:31.197Z",
"app/commerce-modules/fulfillment/events/page.mdx": "2024-12-31T09:37:49.253Z",
"app/commerce-modules/payment/events/page.mdx": "2024-12-31T09:41:56.582Z",
"references/core_flows/Payment/Steps_Payment/functions/core_flows.Payment.Steps_Payment.refundPaymentsStep/page.mdx": "2025-04-11T09:04:40.443Z",
@@ -5747,7 +5747,7 @@ export const generatedEditDates = {
"references/types/StockLocationTypes/interfaces/types.StockLocationTypes.FilterableStockLocationAddressProps/page.mdx": "2025-01-13T18:05:55.410Z",
"references/types/StockLocationTypes/types/types.StockLocationTypes.UpdateStockLocationAddressInput/page.mdx": "2025-01-07T12:54:23.057Z",
"references/types/StockLocationTypes/types/types.StockLocationTypes.UpsertStockLocationAddressInput/page.mdx": "2025-01-07T12:54:23.058Z",
- "app/storefront-development/guides/express-checkout/page.mdx": "2025-03-27T14:47:14.309Z",
+ "app/storefront-development/guides/express-checkout/page.mdx": "2025-04-17T08:29:01.027Z",
"app/commerce-modules/inventory/inventory-kit/page.mdx": "2025-02-26T11:18:00.353Z",
"app/commerce-modules/api-key/workflows/page.mdx": "2025-01-09T13:41:46.573Z",
"app/commerce-modules/api-key/js-sdk/page.mdx": "2025-01-09T12:04:39.787Z",
@@ -5852,7 +5852,7 @@ export const generatedEditDates = {
"references/core_flows/types/core_flows.ThrowUnlessPaymentCollectionNotePaidInput/page.mdx": "2025-01-17T16:43:25.819Z",
"references/core_flows/types/core_flows.ValidatePaymentsRefundStepInput/page.mdx": "2025-01-17T16:43:26.128Z",
"references/core_flows/types/core_flows.ValidateRefundStepInput/page.mdx": "2025-04-11T09:04:43.171Z",
- "app/plugins/guides/wishlist/page.mdx": "2025-03-19T06:30:47.430Z",
+ "app/plugins/guides/wishlist/page.mdx": "2025-04-17T08:48:07.059Z",
"app/plugins/page.mdx": "2025-02-26T11:39:25.709Z",
"app/admin-components/components/data-table/page.mdx": "2025-03-03T14:55:58.556Z",
"references/order_models/variables/order_models.Order/page.mdx": "2025-01-27T11:43:58.788Z",
@@ -5893,7 +5893,7 @@ export const generatedEditDates = {
"app/commerce-modules/payment/account-holder/page.mdx": "2025-04-07T07:31:20.235Z",
"app/troubleshooting/test-errors/page.mdx": "2025-01-31T13:08:42.639Z",
"app/commerce-modules/product/variant-inventory/page.mdx": "2025-02-26T11:21:20.075Z",
- "app/examples/guides/custom-item-price/page.mdx": "2025-03-05T11:30:19.896Z",
+ "app/examples/guides/custom-item-price/page.mdx": "2025-04-17T08:50:17.040Z",
"references/core_flows/Cart/Steps_Cart/functions/core_flows.Cart.Steps_Cart.validateShippingStep/page.mdx": "2025-04-11T09:04:35.729Z",
"references/core_flows/Cart/Steps_Cart/variables/core_flows.Cart.Steps_Cart.validateShippingStepId/page.mdx": "2025-02-11T11:36:39.228Z",
"references/core_flows/Payment_Collection/Steps_Payment_Collection/functions/core_flows.Payment_Collection.Steps_Payment_Collection.createPaymentAccountHolderStep/page.mdx": "2025-02-24T10:48:31.714Z",
@@ -6019,7 +6019,7 @@ export const generatedEditDates = {
"references/core_flows/types/core_flows.UpdateRequestItemReturnValidationStepInput/page.mdx": "2025-04-11T09:04:43.005Z",
"references/core_flows/types/core_flows.UpdateReturnShippingMethodValidationStepInput/page.mdx": "2025-04-11T09:04:43.015Z",
"references/core_flows/types/core_flows.UpdateReturnValidationStepInput/page.mdx": "2025-04-11T09:04:43.024Z",
- "app/examples/guides/quote-management/page.mdx": "2025-03-25T13:51:49.716Z",
+ "app/examples/guides/quote-management/page.mdx": "2025-04-17T08:50:17.061Z",
"references/cart/interfaces/cart.CartCreditLineDTO/page.mdx": "2025-03-04T13:33:48.207Z",
"references/cart/interfaces/cart.UpdateLineItemWithoutSelectorDTO/page.mdx": "2025-03-04T13:33:48.254Z",
"references/cart_models/variables/cart_models.CreditLine/page.mdx": "2025-04-11T09:04:53.262Z",
@@ -6041,14 +6041,14 @@ export const generatedEditDates = {
"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/architectural-modules/locking/page.mdx": "2025-03-14T08:32:57.875Z",
- "app/architectural-modules/locking/postgres/page.mdx": "2025-03-14T08:32:57.875Z",
- "app/architectural-modules/locking/redis/page.mdx": "2025-03-14T08:32:57.876Z",
+ "app/infrastructure-modules/locking/page.mdx": "2025-04-17T08:29:00.564Z",
+ "app/infrastructure-modules/locking/postgres/page.mdx": "2025-03-27T14:53:13.310Z",
+ "app/infrastructure-modules/locking/redis/page.mdx": "2025-03-27T14:53:13.310Z",
"references/locking/interfaces/locking.ILockingModule/page.mdx": "2025-04-11T09:04:49.191Z",
"references/locking/interfaces/locking.ILockingProvider/page.mdx": "2025-04-11T09:04:49.183Z",
"references/modules/locking/page.mdx": "2025-03-12T12:28:30.419Z",
- "references/cache/interfaces/cache.ICacheService/page.mdx": "2025-03-17T15:24:02.574Z",
- "references/event/interfaces/event.IEventBusModuleService/page.mdx": "2025-04-11T09:04:44.802Z",
+ "references/cache/interfaces/cache.ICacheService/page.mdx": "2025-04-17T08:41:49.104Z",
+ "references/event/interfaces/event.IEventBusModuleService/page.mdx": "2025-04-17T08:41:49.516Z",
"references/file_service/interfaces/file_service.IFileModuleService/page.mdx": "2025-04-11T09:04:44.817Z",
"references/modules/cache/page.mdx": "2025-03-17T15:24:02.572Z",
"references/modules/event/page.mdx": "2025-03-17T15:24:03.021Z",
@@ -6058,7 +6058,7 @@ export const generatedEditDates = {
"app/nextjs-starter/guides/revalidate-cache/page.mdx": "2025-03-18T08:47:59.628Z",
"app/storefront-development/cart/totals/page.mdx": "2025-03-27T14:47:14.252Z",
"app/storefront-development/checkout/order-confirmation/page.mdx": "2025-03-27T14:29:45.669Z",
- "app/how-to-tutorials/tutorials/product-reviews/page.mdx": "2025-04-09T14:06:19.147Z",
+ "app/how-to-tutorials/tutorials/product-reviews/page.mdx": "2025-04-17T08:48:08.716Z",
"app/troubleshooting/api-routes/page.mdx": "2025-03-21T07:17:56.248Z",
"app/troubleshooting/data-models/default-fields/page.mdx": "2025-03-21T06:59:06.775Z",
"app/troubleshooting/medusa-admin/blocked-request/page.mdx": "2025-03-21T06:53:34.854Z",
@@ -6069,11 +6069,11 @@ export const generatedEditDates = {
"app/troubleshooting/storefront-pak-sc/page.mdx": "2025-03-21T07:08:57.546Z",
"app/troubleshooting/workflow-errors/step-x-defined/page.mdx": "2025-03-21T07:09:02.741Z",
"app/troubleshooting/workflow-errors/when-then/page.mdx": "2025-03-21T08:35:45.145Z",
- "app/how-to-tutorials/tutorials/abandoned-cart/page.mdx": "2025-03-27T17:39:52.123Z",
- "app/integrations/guides/algolia/page.mdx": "2025-03-27T18:00:49.330Z",
- "app/integrations/guides/magento/page.mdx": "2025-03-28T06:46:40.800Z",
+ "app/how-to-tutorials/tutorials/abandoned-cart/page.mdx": "2025-04-17T08:48:09.516Z",
+ "app/integrations/guides/algolia/page.mdx": "2025-04-17T08:29:01.433Z",
+ "app/integrations/guides/magento/page.mdx": "2025-04-17T08:29:01.500Z",
"app/js-sdk/auth/overview/page.mdx": "2025-03-28T08:05:32.622Z",
- "app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-04-16T07:10:11.789Z",
+ "app/how-to-tutorials/tutorials/loyalty-points/page.mdx": "2025-04-17T08:48:07.930Z",
"references/js_sdk/admin/Admin/properties/js_sdk.admin.Admin.plugin/page.mdx": "2025-04-11T09:04:55.084Z",
"references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.createAddress/page.mdx": "2025-04-11T09:04:54.010Z",
"references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.deleteAddress/page.mdx": "2025-04-11T09:04:54.015Z",
@@ -6144,5 +6144,7 @@ export const generatedEditDates = {
"references/store/types/store.Message/page.mdx": "2025-04-11T09:04:52.459Z",
"references/tax/types/tax.Message/page.mdx": "2025-04-11T09:04:52.576Z",
"references/types/interfaces/types.IFulfillmentProvider/page.mdx": "2025-04-11T09:04:46.137Z",
- "references/user/types/user.Message/page.mdx": "2025-04-11T09:04:52.767Z"
+ "references/user/types/user.Message/page.mdx": "2025-04-11T09:04:52.767Z",
+ "app/infrastructure-modules/workflow-engine/how-to-use/page.mdx": "2025-03-27T14:53:13.311Z",
+ "references/core_flows/Notification/Steps_Notification/functions/core_flows.Notification.Steps_Notification.notifyOnFailureStep/page.mdx": "2025-04-17T08:42:33.837Z"
}
\ No newline at end of file
diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs
index d71f060f07..70e69861f4 100644
--- a/www/apps/resources/generated/files-map.mjs
+++ b/www/apps/resources/generated/files-map.mjs
@@ -47,98 +47,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/admin-widget-injection-zones/page.mdx",
"pathname": "/admin-widget-injection-zones"
},
- {
- "filePath": "/www/apps/resources/app/architectural-modules/cache/create/page.mdx",
- "pathname": "/architectural-modules/cache/create"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/cache/in-memory/page.mdx",
- "pathname": "/architectural-modules/cache/in-memory"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/cache/page.mdx",
- "pathname": "/architectural-modules/cache"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/cache/redis/page.mdx",
- "pathname": "/architectural-modules/cache/redis"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/event/create/page.mdx",
- "pathname": "/architectural-modules/event/create"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/event/local/page.mdx",
- "pathname": "/architectural-modules/event/local"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/event/page.mdx",
- "pathname": "/architectural-modules/event"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/event/redis/page.mdx",
- "pathname": "/architectural-modules/event/redis"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/file/local/page.mdx",
- "pathname": "/architectural-modules/file/local"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/file/page.mdx",
- "pathname": "/architectural-modules/file"
- },
- {
- "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"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/notification/page.mdx",
- "pathname": "/architectural-modules/notification"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/notification/send-notification/page.mdx",
- "pathname": "/architectural-modules/notification/send-notification"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/notification/sendgrid/page.mdx",
- "pathname": "/architectural-modules/notification/sendgrid"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/page.mdx",
- "pathname": "/architectural-modules"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/how-to-use/page.mdx",
- "pathname": "/architectural-modules/workflow-engine/how-to-use"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/in-memory/page.mdx",
- "pathname": "/architectural-modules/workflow-engine/in-memory"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/page.mdx",
- "pathname": "/architectural-modules/workflow-engine"
- },
- {
- "filePath": "/www/apps/resources/app/architectural-modules/workflow-engine/redis/page.mdx",
- "pathname": "/architectural-modules/workflow-engine/redis"
- },
{
"filePath": "/www/apps/resources/app/commerce-modules/api-key/admin-widget-zones/page.mdx",
"pathname": "/commerce-modules/api-key/admin-widget-zones"
@@ -827,6 +735,98 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/how-to-tutorials/tutorials/product-reviews/page.mdx",
"pathname": "/how-to-tutorials/tutorials/product-reviews"
},
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/cache/create/page.mdx",
+ "pathname": "/infrastructure-modules/cache/create"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/cache/in-memory/page.mdx",
+ "pathname": "/infrastructure-modules/cache/in-memory"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/cache/page.mdx",
+ "pathname": "/infrastructure-modules/cache"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/cache/redis/page.mdx",
+ "pathname": "/infrastructure-modules/cache/redis"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/event/create/page.mdx",
+ "pathname": "/infrastructure-modules/event/create"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/event/local/page.mdx",
+ "pathname": "/infrastructure-modules/event/local"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/event/page.mdx",
+ "pathname": "/infrastructure-modules/event"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/event/redis/page.mdx",
+ "pathname": "/infrastructure-modules/event/redis"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/file/local/page.mdx",
+ "pathname": "/infrastructure-modules/file/local"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/file/page.mdx",
+ "pathname": "/infrastructure-modules/file"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/file/s3/page.mdx",
+ "pathname": "/infrastructure-modules/file/s3"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/locking/page.mdx",
+ "pathname": "/infrastructure-modules/locking"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/locking/postgres/page.mdx",
+ "pathname": "/infrastructure-modules/locking/postgres"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/locking/redis/page.mdx",
+ "pathname": "/infrastructure-modules/locking/redis"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/notification/local/page.mdx",
+ "pathname": "/infrastructure-modules/notification/local"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/notification/page.mdx",
+ "pathname": "/infrastructure-modules/notification"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/notification/send-notification/page.mdx",
+ "pathname": "/infrastructure-modules/notification/send-notification"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/notification/sendgrid/page.mdx",
+ "pathname": "/infrastructure-modules/notification/sendgrid"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/page.mdx",
+ "pathname": "/infrastructure-modules"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/workflow-engine/how-to-use/page.mdx",
+ "pathname": "/infrastructure-modules/workflow-engine/how-to-use"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/workflow-engine/in-memory/page.mdx",
+ "pathname": "/infrastructure-modules/workflow-engine/in-memory"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/workflow-engine/page.mdx",
+ "pathname": "/infrastructure-modules/workflow-engine"
+ },
+ {
+ "filePath": "/www/apps/resources/app/infrastructure-modules/workflow-engine/redis/page.mdx",
+ "pathname": "/infrastructure-modules/workflow-engine/redis"
+ },
{
"filePath": "/www/apps/resources/app/integrations/guides/algolia/page.mdx",
"pathname": "/integrations/guides/algolia"
diff --git a/www/apps/resources/generated/generated-how-to-tutorials-sidebar.mjs b/www/apps/resources/generated/generated-how-to-tutorials-sidebar.mjs
index 190b64eef6..e60c2fc4ea 100644
--- a/www/apps/resources/generated/generated-how-to-tutorials-sidebar.mjs
+++ b/www/apps/resources/generated/generated-how-to-tutorials-sidebar.mjs
@@ -59,7 +59,7 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"isPathHref": true,
"type": "ref",
"title": "Create Cache Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/cache/create",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/cache/create",
"children": []
},
{
@@ -67,7 +67,7 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"isPathHref": true,
"type": "ref",
"title": "Create Event Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/event/create",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/event/create",
"children": []
},
{
@@ -139,7 +139,7 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"isPathHref": true,
"type": "ref",
"title": "Send Notification",
- "path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/notification/send-notification",
"children": []
},
{
@@ -187,7 +187,7 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
"isPathHref": true,
"type": "ref",
"title": "Use Workflow Engine Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/workflow-engine/how-to-use",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/how-to-use",
"children": []
}
]
diff --git a/www/apps/resources/generated/generated-architectural-modules-sidebar.mjs b/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs
similarity index 86%
rename from www/apps/resources/generated/generated-architectural-modules-sidebar.mjs
rename to www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs
index 2a6eebb447..6247655a29 100644
--- a/www/apps/resources/generated/generated-architectural-modules-sidebar.mjs
+++ b/www/apps/resources/generated/generated-infrastructure-modules-sidebar.mjs
@@ -1,12 +1,12 @@
-const generatedgeneratedArchitecturalModulesSidebarSidebar = {
- "sidebar_id": "architectural-modules",
- "title": "Architectural Modules",
+const generatedgeneratedInfrastructureModulesSidebarSidebar = {
+ "sidebar_id": "infrastructure-modules",
+ "title": "Infrastructure Modules",
"items": [
{
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules",
+ "path": "/infrastructure-modules",
"title": "Overview",
"children": []
},
@@ -24,7 +24,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/cache",
+ "path": "/infrastructure-modules/cache",
"title": "Overview",
"children": []
},
@@ -38,7 +38,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/cache/in-memory",
+ "path": "/infrastructure-modules/cache/in-memory",
"title": "In-Memory",
"children": []
},
@@ -46,7 +46,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/cache/redis",
+ "path": "/infrastructure-modules/cache/redis",
"title": "Redis",
"children": []
}
@@ -62,7 +62,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/cache/create",
+ "path": "/infrastructure-modules/cache/create",
"title": "Create Cache Module",
"children": []
},
@@ -89,7 +89,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/event",
+ "path": "/infrastructure-modules/event",
"title": "Overview",
"children": []
},
@@ -103,7 +103,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/event/local",
+ "path": "/infrastructure-modules/event/local",
"title": "Local",
"children": []
},
@@ -111,7 +111,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/event/redis",
+ "path": "/infrastructure-modules/event/redis",
"title": "Redis",
"children": []
}
@@ -127,7 +127,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/event/create",
+ "path": "/infrastructure-modules/event/create",
"title": "Create Event Module",
"children": []
},
@@ -154,7 +154,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/file",
+ "path": "/infrastructure-modules/file",
"title": "Overview",
"children": []
},
@@ -168,7 +168,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/file/local",
+ "path": "/infrastructure-modules/file/local",
"title": "Local",
"children": []
},
@@ -176,7 +176,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/file/s3",
+ "path": "/infrastructure-modules/file/s3",
"title": "AWS S3 (and Compatible APIs)",
"children": []
}
@@ -219,7 +219,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/locking",
+ "path": "/infrastructure-modules/locking",
"title": "Overview",
"children": []
},
@@ -233,7 +233,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/locking/redis",
+ "path": "/infrastructure-modules/locking/redis",
"title": "Redis",
"children": []
},
@@ -241,7 +241,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/locking/postgres",
+ "path": "/infrastructure-modules/locking/postgres",
"title": "PostgreSQL",
"children": []
}
@@ -284,7 +284,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/notification",
+ "path": "/infrastructure-modules/notification",
"title": "Overview",
"children": []
},
@@ -298,7 +298,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/notification/local",
+ "path": "/infrastructure-modules/notification/local",
"title": "Local",
"children": []
},
@@ -306,7 +306,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/notification/sendgrid",
+ "path": "/infrastructure-modules/notification/sendgrid",
"title": "SendGrid",
"children": []
}
@@ -357,7 +357,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/notification/send-notification",
+ "path": "/infrastructure-modules/notification/send-notification",
"title": "Send Notification",
"children": []
},
@@ -384,7 +384,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/workflow-engine",
+ "path": "/infrastructure-modules/workflow-engine",
"title": "Overview",
"children": []
},
@@ -398,7 +398,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/workflow-engine/in-memory",
+ "path": "/infrastructure-modules/workflow-engine/in-memory",
"title": "In-Memory",
"children": []
},
@@ -406,7 +406,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/workflow-engine/redis",
+ "path": "/infrastructure-modules/workflow-engine/redis",
"title": "Redis",
"children": []
}
@@ -422,7 +422,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "link",
- "path": "/architectural-modules/workflow-engine/how-to-use",
+ "path": "/infrastructure-modules/workflow-engine/how-to-use",
"title": "Use Workflow Engine Module",
"children": []
}
@@ -433,4 +433,4 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
]
}
-export default generatedgeneratedArchitecturalModulesSidebarSidebar
\ No newline at end of file
+export default generatedgeneratedInfrastructureModulesSidebarSidebar
\ No newline at end of file
diff --git a/www/apps/resources/generated/generated-integrations-sidebar.mjs b/www/apps/resources/generated/generated-integrations-sidebar.mjs
index 8b51f446d9..45ac2841e4 100644
--- a/www/apps/resources/generated/generated-integrations-sidebar.mjs
+++ b/www/apps/resources/generated/generated-integrations-sidebar.mjs
@@ -83,7 +83,7 @@ const generatedgeneratedIntegrationsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "ref",
- "path": "/architectural-modules/file/s3",
+ "path": "/infrastructure-modules/file/s3",
"title": "AWS",
"children": []
}
@@ -142,7 +142,7 @@ const generatedgeneratedIntegrationsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "ref",
- "path": "/architectural-modules/notification/sendgrid",
+ "path": "/infrastructure-modules/notification/sendgrid",
"title": "SendGrid",
"children": []
}
diff --git a/www/apps/resources/next.config.mjs b/www/apps/resources/next.config.mjs
index af22a71cbc..72d2b9b6e5 100644
--- a/www/apps/resources/next.config.mjs
+++ b/www/apps/resources/next.config.mjs
@@ -168,6 +168,11 @@ const nextConfig = {
destination: "/medusa-cli/commands/start",
permanent: true,
},
+ {
+ source: "/infrastructure-modules/:path*",
+ destination: "/infrastructure-modules/:path*",
+ permanent: true,
+ },
]
},
outputFileTracingExcludes: {
diff --git a/www/apps/resources/references/cache/interfaces/cache.ICacheService/page.mdx b/www/apps/resources/references/cache/interfaces/cache.ICacheService/page.mdx
index 6f4eb40aa2..b657af37a2 100644
--- a/www/apps/resources/references/cache/interfaces/cache.ICacheService/page.mdx
+++ b/www/apps/resources/references/cache/interfaces/cache.ICacheService/page.mdx
@@ -35,7 +35,7 @@ const step1 = createStep(
)
```
-This will resolve the service of the configured Cache Module, which is the [In-Memory Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/in-memory) by default.
+This will resolve the service of the configured Cache Module, which is the [In-Memory Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/in-memory) by default.
You can then use the Cache Module's service's methods in the step. The rest of this guide details these methods.
diff --git a/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx b/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx
index cb4595e25a..cca62419db 100644
--- a/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx
+++ b/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.deleteFilesStep/page.mdx
@@ -13,7 +13,7 @@ import { TypeList, WorkflowDiagram } from "docs-ui"
This documentation provides a reference to the `deleteFilesStep`. It belongs to the `@medusajs/medusa/core-flows` package.
This step deletes one or more files using the installed
-[File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file). The files
+[File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file). The files
will be removed from the database and the storage.
## Example
diff --git a/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx b/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx
index 6f14354fd1..63b173bec0 100644
--- a/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx
+++ b/www/apps/resources/references/core_flows/File/Steps_File/functions/core_flows.File.Steps_File.uploadFilesStep/page.mdx
@@ -13,7 +13,7 @@ import { TypeList, WorkflowDiagram } from "docs-ui"
This documentation provides a reference to the `uploadFilesStep`. It belongs to the `@medusajs/medusa/core-flows` package.
This step uploads one or more files using the installed
-[File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file).
+[File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file).
## Example
diff --git a/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx b/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx
index 1e2d047f38..043b40a08e 100644
--- a/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx
+++ b/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.deleteFilesWorkflow/page.mdx
@@ -16,7 +16,7 @@ This documentation provides a reference to the `deleteFilesWorkflow`. It belongs
This workflow deletes one or more files. It's used by the
[Delete File Upload Admin API Route](https://docs.medusajs.com/api/admin#uploads\_deleteuploadsid).
-The [File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file) installed
+The [File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file) installed
in your application will be used to delete the file from storage.
You can use this workflow within your customizations or your own custom workflows, allowing you to
@@ -131,7 +131,7 @@ const myWorkflow = createWorkflow(
## Steps
-
+
## Input
diff --git a/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx b/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx
index 559f7a7d5e..98714b2cab 100644
--- a/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx
+++ b/www/apps/resources/references/core_flows/File/Workflows_File/functions/core_flows.File.Workflows_File.uploadFilesWorkflow/page.mdx
@@ -14,7 +14,7 @@ import { TypeList, WorkflowDiagram } from "docs-ui"
This documentation provides a reference to the `uploadFilesWorkflow`. It belongs to the `@medusajs/medusa/core-flows` package.
This workflow uploads one or more files using the installed
-[File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file). The workflow is used by the
+[File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file). The workflow is used by the
[Upload Files Admin API Route](https://docs.medusajs.com/api/admin#uploads\_postuploads).
You can use this workflow within your customizations or your own custom workflows, allowing you to
@@ -149,7 +149,7 @@ const myWorkflow = createWorkflow(
## Steps
-
+
## Input
diff --git a/www/apps/resources/references/core_flows/Notification/Steps_Notification/functions/core_flows.Notification.Steps_Notification.notifyOnFailureStep/page.mdx b/www/apps/resources/references/core_flows/Notification/Steps_Notification/functions/core_flows.Notification.Steps_Notification.notifyOnFailureStep/page.mdx
index c62136edc4..20f094d65b 100644
--- a/www/apps/resources/references/core_flows/Notification/Steps_Notification/functions/core_flows.Notification.Steps_Notification.notifyOnFailureStep/page.mdx
+++ b/www/apps/resources/references/core_flows/Notification/Steps_Notification/functions/core_flows.Notification.Steps_Notification.notifyOnFailureStep/page.mdx
@@ -31,4 +31,4 @@ const data = notifyOnFailureStep([{
## Input
-` \\| `null`","description":"The data to use in the notification template. This data may be used to personalize\nthe notification, such as the user's name or the order number.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"trigger_type","type":"`string` \\| `null`","description":"The type of trigger that caused the notification to be sent. For example, `order_created`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resource_id","type":"`string` \\| `null`","description":"The ID of the resource that triggered the notification. For example, the ID of the order\nthat triggered the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resource_type","type":"`string` \\| `null`","description":"The type of the resource that triggered the notification. For example, `order`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"receiver_id","type":"`string` \\| `null`","description":"The ID of the user receiving the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"original_notification_id","type":"`string` \\| `null`","description":"The ID of the original notification if it's being resent.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotency_key","type":"`string` \\| `null`","description":"A key to ensure that the notification is sent only once. If the notification\nis sent multiple times, the key ensures that the notification is sent only once.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" openedLevel={1} sectionTitle="notifyOnFailureStep"/>
+` \\| `null`","description":"The data to use in the notification template. This data may be used to personalize\nthe notification, such as the user's name or the order number.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"trigger_type","type":"`string` \\| `null`","description":"The type of trigger that caused the notification to be sent. For example, `order_created`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resource_id","type":"`string` \\| `null`","description":"The ID of the resource that triggered the notification. For example, the ID of the order\nthat triggered the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resource_type","type":"`string` \\| `null`","description":"The type of the resource that triggered the notification. For example, `order`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"receiver_id","type":"`string` \\| `null`","description":"The ID of the user receiving the notification.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"original_notification_id","type":"`string` \\| `null`","description":"The ID of the original notification if it's being resent.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotency_key","type":"`string` \\| `null`","description":"A key to ensure that the notification is sent only once. If the notification\nis sent multiple times, the key ensures that the notification is sent only once.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" openedLevel={1} sectionTitle="notifyOnFailureStep"/>
diff --git a/www/apps/resources/references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx b/www/apps/resources/references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx
index 53185aaf16..cb0b3df0a8 100644
--- a/www/apps/resources/references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx
+++ b/www/apps/resources/references/core_flows/Product/Steps_Product/functions/core_flows.Product.Steps_Product.generateProductCsvStep/page.mdx
@@ -14,7 +14,7 @@ import { TypeList, WorkflowDiagram } from "docs-ui"
This documentation provides a reference to the `generateProductCsvStep`. It belongs to the `@medusajs/medusa/core-flows` package.
This step generates a CSV file that exports products. The CSV
-file is created and stored using the registered [File Module Provider](https://docs.medusajs.com/resources/architectural-modules/file).
+file is created and stored using the registered [File Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/file).
## Example
@@ -34,4 +34,4 @@ const data = generateProductCsvStep(products)
## Output
-
+
diff --git a/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx b/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx
index 90d838fbc8..914c2aae97 100644
--- a/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx
+++ b/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.exportProductsWorkflow/page.mdx
@@ -254,7 +254,7 @@ const myWorkflow = createWorkflow(
## Steps
-
+
## Input
diff --git a/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx b/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx
index b7fab52a60..99f1d7ae64 100644
--- a/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx
+++ b/www/apps/resources/references/core_flows/Product/Workflows_Product/functions/core_flows.Product.Workflows_Product.importProductsWorkflow/page.mdx
@@ -23,7 +23,7 @@ This workflow starts a product import from a CSV file in the background. It's us
You can use this workflow within your custom workflows, allowing you to wrap custom logic around product import.
For example, you can import products from another system.
-The workflow only starts the import, but you'll have to confirm it using the [Workflow Engine](https://docs.medusajs.com/resources/architectural-modules/workflow-engine).
+The workflow only starts the import, but you'll have to confirm it using the [Workflow Engine](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine).
The below example shows how to confirm the import.
@@ -231,7 +231,7 @@ const myWorkflow = createWorkflow(
. You'll use this ID to confirm the import afterwards.
-You confirm the import using the [Workflow Engine](https://docs.medusajs.com/resources/architectural-modules/workflow-engine).
+You confirm the import using the [Workflow Engine](https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine).
For example, in an API route:
```ts workflow={false}
diff --git a/www/apps/resources/references/event/interfaces/event.IEventBusModuleService/page.mdx b/www/apps/resources/references/event/interfaces/event.IEventBusModuleService/page.mdx
index 5ef230ab0e..d547c3ce14 100644
--- a/www/apps/resources/references/event/interfaces/event.IEventBusModuleService/page.mdx
+++ b/www/apps/resources/references/event/interfaces/event.IEventBusModuleService/page.mdx
@@ -35,7 +35,7 @@ const step1 = createStep(
)
```
-This will resolve the service of the configured Event Module, which is the [Local Event Module](https://docs.medusajs.com/resources/architectural-modules/event/local) by default.
+This will resolve the service of the configured Event Module, which is the [Local Event Module](https://docs.medusajs.com/resources/infrastructure-modules/event/local) by default.
You can then use the Event Module's service's methods in the step. The rest of this guide details these methods.
diff --git a/www/apps/resources/references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx b/www/apps/resources/references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx
index faf8c633a3..55a917a1af 100644
--- a/www/apps/resources/references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx
+++ b/www/apps/resources/references/medusa_config/interfaces/medusa_config.ConfigModule/page.mdx
@@ -523,7 +523,7 @@ This property holds all custom modules installed in your Medusa application.
:::note
-Medusa's commerce modules are configured by default, so only
+Medusa's Commerce Modules are configured by default, so only
add them to this property if you're changing their configurations or adding providers to a module.
:::
diff --git a/www/apps/resources/references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx b/www/apps/resources/references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx
index 28f9681f0a..f38d1892c7 100644
--- a/www/apps/resources/references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx
+++ b/www/apps/resources/references/types/CommonTypes/interfaces/types.CommonTypes.ConfigModule/page.mdx
@@ -45,4 +45,4 @@ setting the environment variables depends on the hosting provider.
## Properties
-`","description":"This configuration specifies the supported authentication providers per actor type (such as `user`, `customer`, or any custom actors).\nFor example, you only want to allow SSO logins for `users`, while you want to allow email/password logins for `customers` to the storefront.\n\n`authMethodsPerActor` is a a map where the actor type (eg. 'user') is the key, and the value is an array of supported auth provider IDs.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"restrictedFields","type":"`object`","description":"Specifies the fields that can't be selected in the response unless specified in the allowed query config.\nThis is useful to restrict sensitive fields from being exposed in the API.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"databaseName","type":"`string`","description":"The name of the database to connect to. If the name is specified in `databaseUrl`, then you don't have to use this configuration.\n\nMake sure to create the PostgreSQL database before using it. You can check how to create a database in\n[PostgreSQL's documentation](https://www.postgresql.org/docs/current/sql-createdatabase.html).","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseUrl","type":"`string`","description":"The PostgreSQL connection URL of the database, which is of the following format:\n\n```bash\npostgres://[user][:password]@[host][:port]/[dbname]\n```\n\nWhere:\n\n- `[user]`: (required) your PostgreSQL username. If not specified, the system's username is used by default. The database user that you use must have create privileges. If you're using the `postgres` superuser, then it should have these privileges by default. Otherwise, make sure to grant your user create privileges. You can learn how to do that in [PostgreSQL's documentation](https://www.postgresql.org/docs/current/ddl-priv.html).\n- `[:password]`: an optional password for the user. When provided, make sure to put `:` before the password.\n- `[host]`: (required) your PostgreSQL host. When run locally, it should be `localhost`.\n- `[:port]`: an optional port that the PostgreSQL server is listening on. By default, it's `5432`. When provided, make sure to put `:` before the port.\n- `[dbname]`: (required) the name of the database.\n\nYou can learn more about the connection URL format in [PostgreSQL’s documentation](https://www.postgresql.org/docs/current/libpq-connect.html).","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseSchema","type":"`string`","description":"The database schema to connect to. This is not required to provide if you’re using the default schema, which is `public`.\n\n```js title=\"medusa-config.js\"\nmodule.exports = defineConfig({\n projectConfig: {\n databaseSchema: process.env.DATABASE_SCHEMA ||\n \"custom\",\n // ...\n },\n // ...\n})\n```","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseLogging","type":"`boolean`","description":"This configuration specifies whether database messages should be logged.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseDriverOptions","type":"`Record` & `object`","description":"This configuration is used to pass additional options to the database connection. You can pass any configuration. For example, pass the\n`ssl` property that enables support for TLS/SSL connections.\n\nThis is useful for production databases, which can be supported by setting the `rejectUnauthorized` attribute of `ssl` object to `false`.\nDuring development, it’s recommended not to pass this option.\n\n:::note\n\nMake sure to add to the end of the database URL `?ssl_mode=disable` as well when disabling `rejectUnauthorized`.\n\n:::","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"connection","type":"`object`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"redisUrl","type":"`string`","description":"This configuration specifies the connection URL to Redis to store the Medusa server's session.\n\n:::note\n\nYou must first have Redis installed. You can refer to [Redis's installation guide](https://redis.io/docs/getting-started/installation/).\n\n:::\n\nThe Redis connection URL has the following format:\n\n```bash\nredis[s]://[[username][:password]@][host][:port][/db-number]\n```\n\nFor a local Redis installation, the connection URL should be `redis://localhost:6379` unless you’ve made any changes to the Redis configuration during installation.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"redisPrefix","type":"`string`","description":"This configuration defines a prefix on all keys stored in Redis for the Medusa server's session. The default value is `sess:`.\n\nIf this configuration option is provided, it is prepended to `sess:`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"redisOptions","type":"`RedisOptions`","description":"This configuration defines options to pass ioredis for the Redis connection used to store the Medusa server's session. Refer to [ioredis’s RedisOptions documentation](https://redis.github.io/ioredis/index.html#RedisOptions)\nfor the list of available options.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"sessionOptions","type":"[SessionOptions](../../../interfaces/types.SessionOptions/page.mdx)","description":"This configuration defines additional options to pass to [express-session](https://www.npmjs.com/package/express-session), which is used to store the Medusa server's session.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"name","type":"`string`","description":"The name of the session ID cookie to set in the response (and read from in the request). The default value is `connect.sid`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#name) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resave","type":"`boolean`","description":"Whether the session should be saved back to the session store, even if the session was never modified during the request. The default value is `true`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#resave) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"rolling","type":"`boolean`","description":"Whether the session identifier cookie should be force-set on every response. The default value is `false`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#rolling) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"saveUninitialized","type":"`boolean`","description":"Whether a session that is \"uninitialized\" is forced to be saved to the store. The default value is `true`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#saveUninitialized) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"secret","type":"`string`","description":"The secret to sign the session ID cookie. By default, the value of `http.cookieSecret` is used.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#secret) for details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"ttl","type":"`number`","description":"Used when calculating the `Expires` `Set-Cookie` attribute of cookies. By default, its value is `10 * 60 * 60 * 1000`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#cookiemaxage) for details.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"workerMode","type":"`\"shared\"` \\| `\"worker\"` \\| `\"server\"`","description":"Configure the application's worker mode.\n\nWorkers are processes running separately from the main application. They're useful for executing long-running or resource-heavy tasks in the background, such as importing products.\n\nWith a worker, these tasks are offloaded to a separate process. So, they won't affect the performance of the main application.\n\n\n\nMedusa has three runtime modes:\n\n- Use `shared` to run the application in a single process.\n- Use `worker` to run the a worker process only.\n- Use `server` to run the application server only.\n\nIn production, it's recommended to deploy two instances:\n\n1. One having the `workerMode` configuration set to `server`.\n2. Another having the `workerMode` configuration set to `worker`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"admin","type":"[AdminOptions](../types.CommonTypes.AdminOptions/page.mdx)","description":"This property holds configurations for the Medusa Admin dashboard.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"path","type":"`/${string}`","description":"The path to the admin dashboard. The default value is `/app`.\n\nThe value cannot be one of the reserved paths:\n- `/admin`\n- `/store`\n- `/auth`\n- `/`","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"disable","type":"`boolean`","description":"Whether to disable the admin dashboard. If set to `true`, the admin dashboard is disabled,\nin both development and production environments. The default value is `false`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"backendUrl","type":"`string`","description":"The URL of your Medusa application. Defaults to the browser origin. This is useful to set when running the admin on a separate domain.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"storefrontUrl","type":"`string`","description":"The URL of your Medusa storefront application. This will help generate links from the admin\nto provide to customers to complete any processes","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"vite","type":"(`config`: `InlineConfig`) => `InlineConfig`","description":"Configure the Vite configuration for the admin dashboard. This function receives the default Vite configuration\nand returns the modified configuration. The default value is `undefined`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"featureFlags","type":"`Record`","description":"Some features in the Medusa application are guarded by a feature flag. This ensures constant shipping of new features while maintaining the engine’s stability.\n\nYou can enable a feature in your application by enabling its feature flag. Feature flags are enabled through either environment\nvariables or through this configuration property exported in `medusa-config.js`.\n\nThe `featureFlags`'s value is an object. Its properties are the names of the feature flags, and their value is a boolean indicating whether the feature flag is enabled.\n\nYou can find available feature flags and their key name [here](https://github.com/medusajs/medusa/tree/develop/packages/medusa/src/loaders/feature-flags).","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"modules","type":"`Record`","description":"This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's commerce modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\nThe keys of the `modules` configuration object refer to the module's registration name. Its value can be one of the following:\n\n1. A boolean value indicating whether the module type is enabled. This is only supported for Medusa's commerce and architectural modules;\n2. Or an object having the following properties:\n 1. `resolve`: a string indicating the path to the module relative to `src`, or the module's NPM package name. For example, `./modules/my-module`.\n 2. `options`: (optional) an object indicating the options to pass to the module.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="ConfigModule"/>
+`","description":"This configuration specifies the supported authentication providers per actor type (such as `user`, `customer`, or any custom actors).\nFor example, you only want to allow SSO logins for `users`, while you want to allow email/password logins for `customers` to the storefront.\n\n`authMethodsPerActor` is a a map where the actor type (eg. 'user') is the key, and the value is an array of supported auth provider IDs.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"restrictedFields","type":"`object`","description":"Specifies the fields that can't be selected in the response unless specified in the allowed query config.\nThis is useful to restrict sensitive fields from being exposed in the API.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"databaseName","type":"`string`","description":"The name of the database to connect to. If the name is specified in `databaseUrl`, then you don't have to use this configuration.\n\nMake sure to create the PostgreSQL database before using it. You can check how to create a database in\n[PostgreSQL's documentation](https://www.postgresql.org/docs/current/sql-createdatabase.html).","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseUrl","type":"`string`","description":"The PostgreSQL connection URL of the database, which is of the following format:\n\n```bash\npostgres://[user][:password]@[host][:port]/[dbname]\n```\n\nWhere:\n\n- `[user]`: (required) your PostgreSQL username. If not specified, the system's username is used by default. The database user that you use must have create privileges. If you're using the `postgres` superuser, then it should have these privileges by default. Otherwise, make sure to grant your user create privileges. You can learn how to do that in [PostgreSQL's documentation](https://www.postgresql.org/docs/current/ddl-priv.html).\n- `[:password]`: an optional password for the user. When provided, make sure to put `:` before the password.\n- `[host]`: (required) your PostgreSQL host. When run locally, it should be `localhost`.\n- `[:port]`: an optional port that the PostgreSQL server is listening on. By default, it's `5432`. When provided, make sure to put `:` before the port.\n- `[dbname]`: (required) the name of the database.\n\nYou can learn more about the connection URL format in [PostgreSQL’s documentation](https://www.postgresql.org/docs/current/libpq-connect.html).","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseSchema","type":"`string`","description":"The database schema to connect to. This is not required to provide if you’re using the default schema, which is `public`.\n\n```js title=\"medusa-config.js\"\nmodule.exports = defineConfig({\n projectConfig: {\n databaseSchema: process.env.DATABASE_SCHEMA ||\n \"custom\",\n // ...\n },\n // ...\n})\n```","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseLogging","type":"`boolean`","description":"This configuration specifies whether database messages should be logged.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"databaseDriverOptions","type":"`Record` & `object`","description":"This configuration is used to pass additional options to the database connection. You can pass any configuration. For example, pass the\n`ssl` property that enables support for TLS/SSL connections.\n\nThis is useful for production databases, which can be supported by setting the `rejectUnauthorized` attribute of `ssl` object to `false`.\nDuring development, it’s recommended not to pass this option.\n\n:::note\n\nMake sure to add to the end of the database URL `?ssl_mode=disable` as well when disabling `rejectUnauthorized`.\n\n:::","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"connection","type":"`object`","description":"","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"redisUrl","type":"`string`","description":"This configuration specifies the connection URL to Redis to store the Medusa server's session.\n\n:::note\n\nYou must first have Redis installed. You can refer to [Redis's installation guide](https://redis.io/docs/getting-started/installation/).\n\n:::\n\nThe Redis connection URL has the following format:\n\n```bash\nredis[s]://[[username][:password]@][host][:port][/db-number]\n```\n\nFor a local Redis installation, the connection URL should be `redis://localhost:6379` unless you’ve made any changes to the Redis configuration during installation.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"redisPrefix","type":"`string`","description":"This configuration defines a prefix on all keys stored in Redis for the Medusa server's session. The default value is `sess:`.\n\nIf this configuration option is provided, it is prepended to `sess:`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"redisOptions","type":"`RedisOptions`","description":"This configuration defines options to pass ioredis for the Redis connection used to store the Medusa server's session. Refer to [ioredis’s RedisOptions documentation](https://redis.github.io/ioredis/index.html#RedisOptions)\nfor the list of available options.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"sessionOptions","type":"[SessionOptions](../../../interfaces/types.SessionOptions/page.mdx)","description":"This configuration defines additional options to pass to [express-session](https://www.npmjs.com/package/express-session), which is used to store the Medusa server's session.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"name","type":"`string`","description":"The name of the session ID cookie to set in the response (and read from in the request). The default value is `connect.sid`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#name) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"resave","type":"`boolean`","description":"Whether the session should be saved back to the session store, even if the session was never modified during the request. The default value is `true`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#resave) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"rolling","type":"`boolean`","description":"Whether the session identifier cookie should be force-set on every response. The default value is `false`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#rolling) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"saveUninitialized","type":"`boolean`","description":"Whether a session that is \"uninitialized\" is forced to be saved to the store. The default value is `true`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#saveUninitialized) for more details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"secret","type":"`string`","description":"The secret to sign the session ID cookie. By default, the value of `http.cookieSecret` is used.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#secret) for details.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"ttl","type":"`number`","description":"Used when calculating the `Expires` `Set-Cookie` attribute of cookies. By default, its value is `10 * 60 * 60 * 1000`.\nRefer to [express-session’s documentation](https://www.npmjs.com/package/express-session#cookiemaxage) for details.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"workerMode","type":"`\"shared\"` \\| `\"worker\"` \\| `\"server\"`","description":"Configure the application's worker mode.\n\nWorkers are processes running separately from the main application. They're useful for executing long-running or resource-heavy tasks in the background, such as importing products.\n\nWith a worker, these tasks are offloaded to a separate process. So, they won't affect the performance of the main application.\n\n\n\nMedusa has three runtime modes:\n\n- Use `shared` to run the application in a single process.\n- Use `worker` to run the a worker process only.\n- Use `server` to run the application server only.\n\nIn production, it's recommended to deploy two instances:\n\n1. One having the `workerMode` configuration set to `server`.\n2. Another having the `workerMode` configuration set to `worker`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"admin","type":"[AdminOptions](../types.CommonTypes.AdminOptions/page.mdx)","description":"This property holds configurations for the Medusa Admin dashboard.","optional":false,"defaultValue":"","expandable":false,"children":[{"name":"path","type":"`/${string}`","description":"The path to the admin dashboard. The default value is `/app`.\n\nThe value cannot be one of the reserved paths:\n- `/admin`\n- `/store`\n- `/auth`\n- `/`","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"disable","type":"`boolean`","description":"Whether to disable the admin dashboard. If set to `true`, the admin dashboard is disabled,\nin both development and production environments. The default value is `false`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"backendUrl","type":"`string`","description":"The URL of your Medusa application. Defaults to the browser origin. This is useful to set when running the admin on a separate domain.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"storefrontUrl","type":"`string`","description":"The URL of your Medusa storefront application. This will help generate links from the admin\nto provide to customers to complete any processes","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"vite","type":"(`config`: `InlineConfig`) => `InlineConfig`","description":"Configure the Vite configuration for the admin dashboard. This function receives the default Vite configuration\nand returns the modified configuration. The default value is `undefined`.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"featureFlags","type":"`Record`","description":"Some features in the Medusa application are guarded by a feature flag. This ensures constant shipping of new features while maintaining the engine’s stability.\n\nYou can enable a feature in your application by enabling its feature flag. Feature flags are enabled through either environment\nvariables or through this configuration property exported in `medusa-config.js`.\n\nThe `featureFlags`'s value is an object. Its properties are the names of the feature flags, and their value is a boolean indicating whether the feature flag is enabled.\n\nYou can find available feature flags and their key name [here](https://github.com/medusajs/medusa/tree/develop/packages/medusa/src/loaders/feature-flags).","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"modules","type":"`Record`","description":"This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's Commerce Modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\nThe keys of the `modules` configuration object refer to the module's registration name. Its value can be one of the following:\n\n1. A boolean value indicating whether the module type is enabled. This is only supported for Medusa's Commerce and Infrastructure modules;\n2. Or an object having the following properties:\n 1. `resolve`: a string indicating the path to the module relative to `src`, or the module's NPM package name. For example, `./modules/my-module`.\n 2. `options`: (optional) an object indicating the options to pass to the module.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="ConfigModule"/>
diff --git a/www/apps/resources/sidebar.mjs b/www/apps/resources/sidebar.mjs
index ef9a2c3276..08181ee4b5 100644
--- a/www/apps/resources/sidebar.mjs
+++ b/www/apps/resources/sidebar.mjs
@@ -1,4 +1,4 @@
-import { architecturalModulesSidebar } from "./sidebars/architectural-modules.mjs"
+import { infrastructureModulesSidebar } from "./sidebars/infrastructure-modules.mjs"
import { integrationsSidebar } from "./sidebars/integrations.mjs"
import { recipesSidebar } from "./sidebars/recipes.mjs"
import { referencesSidebar } from "./sidebars/references.mjs"
@@ -46,9 +46,9 @@ export const sidebar = [
items: commerceModulesSidebar,
},
{
- sidebar_id: "architectural-modules",
- title: "Architectural Modules",
- items: architecturalModulesSidebar,
+ sidebar_id: "infrastructure-modules",
+ title: "Infrastructure Modules",
+ items: infrastructureModulesSidebar,
},
{
sidebar_id: "troubleshooting",
diff --git a/www/apps/resources/sidebars/architectural-modules.mjs b/www/apps/resources/sidebars/infrastructure-modules.mjs
similarity index 79%
rename from www/apps/resources/sidebars/architectural-modules.mjs
rename to www/apps/resources/sidebars/infrastructure-modules.mjs
index 445dcde3e8..727c5cb8b8 100644
--- a/www/apps/resources/sidebars/architectural-modules.mjs
+++ b/www/apps/resources/sidebars/infrastructure-modules.mjs
@@ -1,8 +1,8 @@
/** @type {import('types').Sidebar.SidebarItem[]} */
-export const architecturalModulesSidebar = [
+export const infrastructureModulesSidebar = [
{
type: "link",
- path: "/architectural-modules",
+ path: "/infrastructure-modules",
title: "Overview",
},
{
@@ -15,7 +15,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/cache",
+ path: "/infrastructure-modules/cache",
title: "Overview",
},
{
@@ -24,12 +24,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/cache/in-memory",
+ path: "/infrastructure-modules/cache/in-memory",
title: "In-Memory",
},
{
type: "link",
- path: "/architectural-modules/cache/redis",
+ path: "/infrastructure-modules/cache/redis",
title: "Redis",
},
],
@@ -40,7 +40,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/cache/create",
+ path: "/infrastructure-modules/cache/create",
title: "Create Cache Module",
},
{
@@ -59,7 +59,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/event",
+ path: "/infrastructure-modules/event",
title: "Overview",
},
{
@@ -68,12 +68,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/event/local",
+ path: "/infrastructure-modules/event/local",
title: "Local",
},
{
type: "link",
- path: "/architectural-modules/event/redis",
+ path: "/infrastructure-modules/event/redis",
title: "Redis",
},
],
@@ -84,7 +84,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/event/create",
+ path: "/infrastructure-modules/event/create",
title: "Create Event Module",
},
{
@@ -103,7 +103,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/file",
+ path: "/infrastructure-modules/file",
title: "Overview",
},
{
@@ -112,12 +112,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/file/local",
+ path: "/infrastructure-modules/file/local",
title: "Local",
},
{
type: "link",
- path: "/architectural-modules/file/s3",
+ path: "/infrastructure-modules/file/s3",
title: "AWS S3 (and Compatible APIs)",
},
],
@@ -147,7 +147,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/locking",
+ path: "/infrastructure-modules/locking",
title: "Overview",
},
{
@@ -156,12 +156,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/locking/redis",
+ path: "/infrastructure-modules/locking/redis",
title: "Redis",
},
{
type: "link",
- path: "/architectural-modules/locking/postgres",
+ path: "/infrastructure-modules/locking/postgres",
title: "PostgreSQL",
},
],
@@ -191,7 +191,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/notification",
+ path: "/infrastructure-modules/notification",
title: "Overview",
},
{
@@ -200,12 +200,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/notification/local",
+ path: "/infrastructure-modules/notification/local",
title: "Local",
},
{
type: "link",
- path: "/architectural-modules/notification/sendgrid",
+ path: "/infrastructure-modules/notification/sendgrid",
title: "SendGrid",
},
],
@@ -229,7 +229,7 @@ export const architecturalModulesSidebar = [
},
{
type: "link",
- path: "/architectural-modules/notification/send-notification",
+ path: "/infrastructure-modules/notification/send-notification",
title: "Send Notification",
},
{
@@ -248,7 +248,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/workflow-engine",
+ path: "/infrastructure-modules/workflow-engine",
title: "Overview",
},
{
@@ -257,12 +257,12 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/workflow-engine/in-memory",
+ path: "/infrastructure-modules/workflow-engine/in-memory",
title: "In-Memory",
},
{
type: "link",
- path: "/architectural-modules/workflow-engine/redis",
+ path: "/infrastructure-modules/workflow-engine/redis",
title: "Redis",
},
],
@@ -273,7 +273,7 @@ export const architecturalModulesSidebar = [
children: [
{
type: "link",
- path: "/architectural-modules/workflow-engine/how-to-use",
+ path: "/infrastructure-modules/workflow-engine/how-to-use",
title: "Use Workflow Engine Module",
},
],
diff --git a/www/apps/resources/sidebars/integrations.mjs b/www/apps/resources/sidebars/integrations.mjs
index 45e4bf333a..a58142fa64 100644
--- a/www/apps/resources/sidebars/integrations.mjs
+++ b/www/apps/resources/sidebars/integrations.mjs
@@ -56,7 +56,7 @@ export const integrationsSidebar = [
children: [
{
type: "ref",
- path: "/architectural-modules/file/s3",
+ path: "/infrastructure-modules/file/s3",
title: "AWS",
},
],
@@ -97,7 +97,7 @@ export const integrationsSidebar = [
},
{
type: "ref",
- path: "/architectural-modules/notification/sendgrid",
+ path: "/infrastructure-modules/notification/sendgrid",
title: "SendGrid",
},
],
diff --git a/www/apps/resources/utils/get-sidebar-for-path.ts b/www/apps/resources/utils/get-sidebar-for-path.ts
index dd5f8299c6..7af5235d05 100644
--- a/www/apps/resources/utils/get-sidebar-for-path.ts
+++ b/www/apps/resources/utils/get-sidebar-for-path.ts
@@ -63,12 +63,12 @@ const sidebarMappings: {
{
module: async () =>
import(
- "@/generated/generated-architectural-modules-sidebar.mjs"
+ "@/generated/generated-infrastructure-modules-sidebar.mjs"
) as Promise<{
default: Sidebar.Sidebar
}>,
paths: [
- "/architectural-modules",
+ "/infrastructure-modules",
"/references/file-provider-module",
"/references/locking",
"/references/notification-provider-module",
diff --git a/www/apps/user-guide/app/reset-password/page.mdx b/www/apps/user-guide/app/reset-password/page.mdx
index f7c21ceb28..76c06dc57e 100644
--- a/www/apps/user-guide/app/reset-password/page.mdx
+++ b/www/apps/user-guide/app/reset-password/page.mdx
@@ -29,7 +29,7 @@ You will receive an email with instructions to reset your password. The email wi
-If you don't receive an email, check with your technical team to ensure they have installed a [Notification Module Provider](!resources!/architectural-modules/notification) and configured the [password reset notification](!resources!/commerce-modules/auth/reset-password).
+If you don't receive an email, check with your technical team to ensure they have installed a [Notification Module Provider](!resources!/infrastructure-modules/notification) and configured the [password reset notification](!resources!/commerce-modules/auth/reset-password).
diff --git a/www/apps/user-guide/generated/edit-dates.mjs b/www/apps/user-guide/generated/edit-dates.mjs
index 9098cd2ce7..65fd9bdf04 100644
--- a/www/apps/user-guide/generated/edit-dates.mjs
+++ b/www/apps/user-guide/generated/edit-dates.mjs
@@ -55,5 +55,5 @@ export const generatedEditDates = {
"app/settings/developer/publishable-api-keys/page.mdx": "2025-02-25T16:15:29.847Z",
"app/settings/developer/secret-api-keys/page.mdx": "2025-02-25T16:17:40.621Z",
"app/settings/developer/workflows/page.mdx": "2025-02-25T15:52:48.349Z",
- "app/reset-password/page.mdx": "2025-02-26T11:13:45.457Z"
+ "app/reset-password/page.mdx": "2025-04-17T08:29:28.890Z"
}
\ No newline at end of file
diff --git a/www/packages/docs-ui/src/constants.tsx b/www/packages/docs-ui/src/constants.tsx
index 170c92a669..3b5a58f267 100644
--- a/www/packages/docs-ui/src/constants.tsx
+++ b/www/packages/docs-ui/src/constants.tsx
@@ -171,38 +171,38 @@ export const navDropdownItems: NavigationItem[] = [
},
{
type: "sub-menu",
- title: "Architectural Modules",
- link: "/resources/architectural-modules",
+ title: "Infrastructure Modules",
+ link: "/resources/infrastructure-modules",
items: [
{
type: "link",
title: "Cache",
- link: "/resources/architectural-modules/cache",
+ link: "/resources/infrastructure-modules/cache",
},
{
type: "link",
title: "Event",
- link: "/resources/architectural-modules/event",
+ link: "/resources/infrastructure-modules/event",
},
{
type: "link",
title: "File",
- link: "/resources/architectural-modules/file",
+ link: "/resources/infrastructure-modules/file",
},
{
type: "link",
title: "Locking",
- link: "/resources/architectural-modules/locking",
+ link: "/resources/infrastructure-modules/locking",
},
{
type: "link",
title: "Notification",
- link: "/resources/architectural-modules/notification",
+ link: "/resources/infrastructure-modules/notification",
},
{
type: "link",
title: "Workflow Engine",
- link: "/resources/architectural-modules/workflow-engine",
+ link: "/resources/infrastructure-modules/workflow-engine",
},
],
},
diff --git a/www/packages/tags/src/tags/auth.ts b/www/packages/tags/src/tags/auth.ts
index 6c0d50be3c..bf18ea8f2f 100644
--- a/www/packages/tags/src/tags/auth.ts
+++ b/www/packages/tags/src/tags/auth.ts
@@ -1,12 +1,12 @@
export const auth = [
- {
- "title": "Reset Password",
- "path": "https://docs.medusajs.com/user-guide/reset-password"
- },
{
"title": "Create Actor Type",
"path": "https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type"
},
+ {
+ "title": "Reset Password",
+ "path": "https://docs.medusajs.com/user-guide/reset-password"
+ },
{
"title": "Log-out Customer in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/customers/log-out"
diff --git a/www/packages/tags/src/tags/cache.ts b/www/packages/tags/src/tags/cache.ts
index 96586af408..84de971706 100644
--- a/www/packages/tags/src/tags/cache.ts
+++ b/www/packages/tags/src/tags/cache.ts
@@ -1,7 +1,7 @@
export const cache = [
{
"title": "Create Cache Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/cache/create"
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/cache/create"
},
{
"title": "Use Cache Module",
diff --git a/www/packages/tags/src/tags/event.ts b/www/packages/tags/src/tags/event.ts
index 38465bae25..56d3810630 100644
--- a/www/packages/tags/src/tags/event.ts
+++ b/www/packages/tags/src/tags/event.ts
@@ -1,7 +1,7 @@
export const event = [
{
"title": "Create Event Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/event/create"
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/event/create"
},
{
"title": "Use Event Module",
diff --git a/www/packages/tags/src/tags/how-to.ts b/www/packages/tags/src/tags/how-to.ts
index b442d997e1..1163bc582d 100644
--- a/www/packages/tags/src/tags/how-to.ts
+++ b/www/packages/tags/src/tags/how-to.ts
@@ -1,20 +1,4 @@
export const howTo = [
- {
- "title": "Create Cache Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/cache/create"
- },
- {
- "title": "Create Event Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/event/create"
- },
- {
- "title": "Send Notification",
- "path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification"
- },
- {
- "title": "Use Workflow Engine Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/workflow-engine/how-to-use"
- },
{
"title": "Create Actor Type",
"path": "https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type"
@@ -31,6 +15,22 @@ export const howTo = [
"title": "Get Variant Price with Taxes",
"path": "https://docs.medusajs.com/resources/commerce-modules/product/guides/price-with-taxes"
},
+ {
+ "title": "Create Cache Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/cache/create"
+ },
+ {
+ "title": "Create Event Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/event/create"
+ },
+ {
+ "title": "Send Notification",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/notification/send-notification"
+ },
+ {
+ "title": "Use Workflow Engine Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/how-to-use"
+ },
{
"title": "Create Auth Provider",
"path": "https://docs.medusajs.com/resources/references/auth/provider"
diff --git a/www/packages/tags/src/tags/notification.ts b/www/packages/tags/src/tags/notification.ts
index 0630cafdbe..e9342371be 100644
--- a/www/packages/tags/src/tags/notification.ts
+++ b/www/packages/tags/src/tags/notification.ts
@@ -1,8 +1,4 @@
export const notification = [
- {
- "title": "Send Notification",
- "path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification"
- },
{
"title": "Handle Password Reset Event",
"path": "https://docs.medusajs.com/resources/commerce-modules/auth/reset-password"
@@ -11,6 +7,10 @@ export const notification = [
"title": "Abandoned Cart Notification",
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/abandoned-cart"
},
+ {
+ "title": "Send Notification",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/notification/send-notification"
+ },
{
"title": "notifyOnFailureStep",
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/steps/notifyOnFailureStep"
diff --git a/www/packages/tags/src/tags/server.ts b/www/packages/tags/src/tags/server.ts
index 1fce49a554..497158ae6b 100644
--- a/www/packages/tags/src/tags/server.ts
+++ b/www/packages/tags/src/tags/server.ts
@@ -1,20 +1,4 @@
export const server = [
- {
- "title": "Create Cache Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/cache/create"
- },
- {
- "title": "Create Event Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/event/create"
- },
- {
- "title": "Send Notification",
- "path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification"
- },
- {
- "title": "Use Workflow Engine Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/workflow-engine/how-to-use"
- },
{
"title": "Create Actor Type",
"path": "https://docs.medusajs.com/resources/commerce-modules/auth/create-actor-type"
@@ -67,6 +51,22 @@ export const server = [
"title": "Product Reviews",
"path": "https://docs.medusajs.com/resources/how-to-tutorials/tutorials/product-reviews"
},
+ {
+ "title": "Create Cache Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/cache/create"
+ },
+ {
+ "title": "Create Event Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/event/create"
+ },
+ {
+ "title": "Send Notification",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/notification/send-notification"
+ },
+ {
+ "title": "Use Workflow Engine Module",
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/how-to-use"
+ },
{
"title": "Build Wishlist Plugin",
"path": "https://docs.medusajs.com/resources/plugins/guides/wishlist"
diff --git a/www/packages/tags/src/tags/workflow-engine.ts b/www/packages/tags/src/tags/workflow-engine.ts
index 17ae08d183..271f18b886 100644
--- a/www/packages/tags/src/tags/workflow-engine.ts
+++ b/www/packages/tags/src/tags/workflow-engine.ts
@@ -1,6 +1,6 @@
export const workflowEngine = [
{
"title": "Use Workflow Engine Module",
- "path": "https://docs.medusajs.com/resources/architectural-modules/workflow-engine/how-to-use"
+ "path": "https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine/how-to-use"
}
]
\ No newline at end of file
diff --git a/www/utils/generated/oas-output/base/admin.oas.base.yaml b/www/utils/generated/oas-output/base/admin.oas.base.yaml
index 0a74ee4e12..77b8c285fb 100644
--- a/www/utils/generated/oas-output/base/admin.oas.base.yaml
+++ b/www/utils/generated/oas-output/base/admin.oas.base.yaml
@@ -555,7 +555,7 @@ tags:
You can upload public files, such as product images, or private files, such as CSV files used to import products.
externalDocs:
description: Check out available file module providers.
- url: https://docs.medusajs.com/resources/architectural-modules/file
+ url: https://docs.medusajs.com/resources/infrastructure-modules/file
- name: Users
description: >
A user is an admin user that can authenticate and perform functionalities
@@ -580,7 +580,7 @@ tags:
Depending on the workflow engine you use, executions may only be retained for a short while, or only until the Medusa application is restarted.
externalDocs:
description: Check out available Workflow Engine Modules
- url: https://docs.medusajs.com/resources/architectural-modules/workflow-engine
+ url: https://docs.medusajs.com/resources/infrastructure-modules/workflow-engine
servers:
- url: http://localhost:9000
- url: https://api.medusajs.com
diff --git a/www/utils/generated/oas-output/operations/admin/post_auth_[actor_type]_[auth_provider]_reset-password.ts b/www/utils/generated/oas-output/operations/admin/post_auth_[actor_type]_[auth_provider]_reset-password.ts
index 12d1e43377..70e25c1a4d 100644
--- a/www/utils/generated/oas-output/operations/admin/post_auth_[actor_type]_[auth_provider]_reset-password.ts
+++ b/www/utils/generated/oas-output/operations/admin/post_auth_[actor_type]_[auth_provider]_reset-password.ts
@@ -7,7 +7,7 @@
* Generate a reset password token for an admin user. This API route doesn't reset the admin's password or send them the reset instructions in a notification.
*
*
- * Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the user a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification), and it should have the URL to reset the password in the Medusa Admin dashboard, such as `http://localhost:9000/app/reset-password?token=123`.
+ * Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the user a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification), and it should have the URL to reset the password in the Medusa Admin dashboard, such as `http://localhost:9000/app/reset-password?token=123`.
*
*
* Use the generated token to update the user's password using the [Reset Password API route](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providerupdate).
diff --git a/www/utils/generated/oas-output/operations/store/post_auth_[actor_type]_[auth_provider]_reset-password.ts b/www/utils/generated/oas-output/operations/store/post_auth_[actor_type]_[auth_provider]_reset-password.ts
index a89e49bcc8..1a6a8545a2 100644
--- a/www/utils/generated/oas-output/operations/store/post_auth_[actor_type]_[auth_provider]_reset-password.ts
+++ b/www/utils/generated/oas-output/operations/store/post_auth_[actor_type]_[auth_provider]_reset-password.ts
@@ -7,7 +7,7 @@
* Generate a reset password token for a customer. This API route doesn't reset the customer password or send them the reset instructions in a notification.
*
*
- * Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the customer a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/architectural-modules/notification), and it should have a URL that accepts a `token` query parameter, allowing the customer to reset their password from the storefront.
+ * Instead, This API route emits the `auth.password_reset` event, passing it the token as a payload. You can listen to that event in a subscriber as explained in [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password), then send the customer a notification. The notification is sent using a [Notification Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/notification), and it should have a URL that accepts a `token` query parameter, allowing the customer to reset their password from the storefront.
*
*
* Use the generated token to update the customer's password using the [Reset Password API route](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerupdate).
diff --git a/www/utils/generated/typedoc-json-output/0-types.json b/www/utils/generated/typedoc-json-output/0-types.json
index ee4aef50ad..cf826fd780 100644
--- a/www/utils/generated/typedoc-json-output/0-types.json
+++ b/www/utils/generated/typedoc-json-output/0-types.json
@@ -15198,7 +15198,7 @@
"summary": [
{
"kind": "text",
- "text": "This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's commerce modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\nThe keys of the "
+ "text": "This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's Commerce Modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\nThe keys of the "
},
{
"kind": "code",
diff --git a/www/utils/generated/typedoc-json-output/medusa-config.json b/www/utils/generated/typedoc-json-output/medusa-config.json
index 61df14f1c0..353a9aee0a 100644
--- a/www/utils/generated/typedoc-json-output/medusa-config.json
+++ b/www/utils/generated/typedoc-json-output/medusa-config.json
@@ -2750,7 +2750,7 @@
"summary": [
{
"kind": "text",
- "text": "This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's commerce modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\n"
+ "text": "This property holds all custom modules installed in your Medusa application.\n\n:::note\n\nMedusa's Commerce Modules are configured by default, so only\nadd them to this property if you're changing their configurations or adding providers to a module.\n\n:::\n\n"
},
{
"kind": "code",
diff --git a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/cache.ts b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/cache.ts
index fba14d8678..6c445f6ab3 100644
--- a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/cache.ts
+++ b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/cache.ts
@@ -36,7 +36,7 @@ const step1 = createStep(
)
\`\`\`
-This will resolve the service of the configured Cache Module, which is the [In-Memory Cache Module](https://docs.medusajs.com/resources/architectural-modules/cache/in-memory) by default.
+This will resolve the service of the configured Cache Module, which is the [In-Memory Cache Module](https://docs.medusajs.com/resources/infrastructure-modules/cache/in-memory) by default.
You can then use the Cache Module's service's methods in the step. The rest of this guide details these methods.
diff --git a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/event.ts b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/event.ts
index 4b0c09e5fb..31c1938dc7 100644
--- a/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/event.ts
+++ b/www/utils/packages/typedoc-generate-references/src/constants/merger-custom-options/event.ts
@@ -36,7 +36,7 @@ const step1 = createStep(
)
\`\`\`
-This will resolve the service of the configured Event Module, which is the [Local Event Module](https://docs.medusajs.com/resources/architectural-modules/event/local) by default.
+This will resolve the service of the configured Event Module, which is the [Local Event Module](https://docs.medusajs.com/resources/infrastructure-modules/event/local) by default.
You can then use the Event Module's service's methods in the step. The rest of this guide details these methods.
diff --git a/www/vale/styles/docs/ModuleNames.yml b/www/vale/styles/docs/ModuleNames.yml
index 7f61a19850..1f0efb1254 100644
--- a/www/vale/styles/docs/ModuleNames.yml
+++ b/www/vale/styles/docs/ModuleNames.yml
@@ -7,7 +7,6 @@ tokens:
- the [A-Z]\w+ module
exceptions:
- the custom module
- - the commerce module
- the transpiled module
- the main module
- the same module
diff --git a/www/vale/styles/docs/Tooling.yml b/www/vale/styles/docs/Tooling.yml
index 1a120cc26c..b9255a889a 100644
--- a/www/vale/styles/docs/Tooling.yml
+++ b/www/vale/styles/docs/Tooling.yml
@@ -10,6 +10,13 @@ swap:
"Data Modeling Language": "Data Model Language"
'(?:[\w\d'']+) framework': $1 Framework
"Medusa's Framework": "Medusa Framework"
+ "Architecural Module[s]?": "Infrastructure Module"
+ "Architectural module[s]?": "Infrastructure module"
+ "architectural module[s]?": "infrastructure module"
+ "Infrastructure module[s]?": "Infrastructure Module"
+ "infrastructure module[s]?": "Infrastructure Module"
+ "Commerce module[s]?": "Commerce Module"
+ "commerce module[s]?": "Commerce Module"
exceptions:
- 'storefront framework'
- 'Storefront Framework'