diff --git a/www/apps/api-reference/markdown/admin.mdx b/www/apps/api-reference/markdown/admin.mdx index 2dae3f5b067ad..aa2f2deb8c191 100644 --- a/www/apps/api-reference/markdown/admin.mdx +++ b/www/apps/api-reference/markdown/admin.mdx @@ -661,20 +661,35 @@ curl -g "http://localhost:9000/admin/products?created_at[$lt]=2023-02-17T07:22:3 ### Array +Array filters can be passed either as: -Each array value must be passed as a separate query parameter in the form -`[]=`. You can also specify the index of each +- `[]=,`, separating the values by a comma. +- `[]=&[]=`, passing each value as a separate query parameter. You can also specify the index of each parameter in the brackets `[0]=`. -```bash title="Array filter" + + + +```bash +curl -g "http://localhost:9000/admin/products?sales_channel_id[]=sc_01GPGVB42PZ7N3YQEP2WDM7PC7,sc_234PGVB42PZ7N3YQEP2WDM7PC7" \ +-H 'Authorization: Bearer {jwt_token}' +``` + + + + +```bash curl -g "http://localhost:9000/admin/products?sales_channel_id[]=sc_01GPGVB42PZ7N3YQEP2WDM7PC7&sales_channel_id[]=sc_234PGVB42PZ7N3YQEP2WDM7PC7" \ -H 'Authorization: Bearer {jwt_token}' ``` + + + Note that the `-g` parameter passed to `curl` disables errors being thrown for using the brackets. Read more [here](https://curl.se/docs/manpage.html#-g). diff --git a/www/apps/api-reference/markdown/store.mdx b/www/apps/api-reference/markdown/store.mdx index bfbfb45de16ed..2b2174e99d29d 100644 --- a/www/apps/api-reference/markdown/store.mdx +++ b/www/apps/api-reference/markdown/store.mdx @@ -639,19 +639,33 @@ curl -g "http://localhost:9000/store/products?created_at[$lt]=2023-02-17T07:22:3 ### Array +Array filters can be passed either as: -Each array value must be passed as a separate query parameter in the form -`[]=`. You can also specify the index of each +- `[]=,`, separating the values by a comma. +- `[]=&[]=`, passing each value as a separate query parameter. You can also specify the index of each parameter in the brackets `[0]=`. -```bash title="Array filter" + + + +```bash +curl -g "http://localhost:9000/store/products?sales_channel_id[]=sc_01GPGVB42PZ7N3YQEP2WDM7PC7,sc_234PGVB42PZ7N3YQEP2WDM7PC7" +``` + + + + +```bash curl -g "http://localhost:9000/store/products?sales_channel_id[]=sc_01GPGVB42PZ7N3YQEP2WDM7PC7&sales_channel_id[]=sc_234PGVB42PZ7N3YQEP2WDM7PC7" ``` + + + Note that the `-g` parameter passed to `curl` disables errors being thrown for using the brackets. Read more [here](https://curl.se/docs/manpage.html#-g). diff --git a/www/apps/book/public/llms-full.txt b/www/apps/book/public/llms-full.txt index 3545069f0e319..5a5843defd6b1 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](undefined/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](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 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. @@ -42,10 +42,10 @@ By following this documentation, you'll be able to create custom commerce experi This documentation is split into the following sections: 1. The main documentation, which is the one you're currently reading. It's recommended to follow the chapters in this documentation to understand the core concepts of Medusa and how to use them. -2. The [Development Resources documentation](undefined/resources) provides guides and resources useful during your development, such as tools, API references, recipes, step-by-step guides and examples, and more. -3. The [Store](undefined/api/store) and [Admin](undefined/api/admin) API references provide a reference to the Medusa application's endpoints and instructions related to authentication, parameter types in requests, and more. +2. The [Development Resources documentation](https://docs.medusajs.com/resources/index.html.md) provides guides and resources useful during your development, such as tools, API references, recipes, step-by-step guides and examples, and more. +3. The [Store](https://docs.medusajs.com/api/store) and [Admin](https://docs.medusajs.com/api/admin) API references provide a reference to the Medusa application's endpoints and instructions related to authentication, parameter types in requests, and more. -To get started, check out the [Installation chapter](undefined/learn/installation). +To get started, check out the [Installation chapter](https://docs.medusajs.com/learn/installation/index.html.md). ### Using with LLM Editors @@ -58,7 +58,7 @@ This documentation provides an [llms-full.txt](https://docs.medusajs.com/llms-fu - Need Help? Refer to our [GitHub repository](https://github.com/medusajs/medusa) for [issues](https://github.com/medusajs/medusa/issues) and [discussions](https://github.com/medusajs/medusa/discussions). - [Join the community on Discord](https://discord.gg/medusajs). - Have questions or need more support? Contact our [sales team](https://medusajs.com/contact/). -- Facing issues in your development? Refer to our [troubleshooting guides](undefined/resources/troubleshooting). +- Facing issues in your development? Refer to our [troubleshooting guides](https://docs.medusajs.com/resources/troubleshooting/index.html.md). # Build Medusa Application @@ -69,7 +69,7 @@ Next chapters explain how to deploy the Medusa application. ## build Command -The Medusa CLI tool has a [build](undefined/resources/medusa-cli/commands/build) command which creates a standalone build of the Medusa application that: +The Medusa CLI tool has a [build](https://docs.medusajs.com/resources/medusa-cli/commands/build/index.html.md) command which creates a standalone build of the Medusa application that: - Doesn't rely on the source TypeScript files. - Can be copied to a production server reliably. @@ -112,7 +112,7 @@ cd .medusa/server && npm install cp ../../.env .env.production ``` -When `NODE_ENV=production`, the Medusa application loads the environment variables from `.env.production`. Learn more about environment variables in [this guide](undefined/learn/fundamentals/environment-variables). +When `NODE_ENV=production`, the Medusa application loads the environment variables from `.env.production`. Learn more about environment variables in [this guide](https://docs.medusajs.com/learn/fundamentals/environment-variables/index.html.md). - Set `NODE_ENV` to `production` in the system environment variable, then start the Medusa application from `.medusa/server`: @@ -127,17 +127,7 @@ npm run start The next chapter covers how you generally deploy the production build. -You can also refer to the [deployment how-to guides](undefined/resources/deployment) for platform-specific how-to guides. - - -# Debugging and Testing - -In the next chapters, you’ll learn about the tools Medusa provides for testing and debugging your Medusa application. - -By the end of this chapter, you’ll learn: - -- How to use Medusa's `@medusajs/test-utils` test to write integration tests. -- How to use Medusa’s `Logger` utility to log messages. +You can also refer to the [deployment how-to guides](https://docs.medusajs.com/resources/deployment/index.html.md) for platform-specific how-to guides. # Medusa Deployment Overview @@ -182,7 +172,7 @@ With Medusa Cloud, you maintain full customization control as you deploy your ow [Sign up and learn more about Medusa Cloud](https://medusajs.com/contact) -To self-host Medusa, the [next chapter](undefined/learn/deployment/general) explains the general steps to deploy your Medusa application. Refer to [this reference](undefined/resources/deployment) to find how-to deployment guides for general and specific hosting providers. +To self-host Medusa, the [next chapter](https://docs.medusajs.com/learn/deployment/general/index.html.md) explains the general steps to deploy your Medusa application. Refer to [this reference](https://docs.medusajs.com/resources/deployment/index.html.md) to find how-to deployment guides for general and specific hosting providers. *** @@ -194,7 +184,17 @@ If you’re using the Next.js Starter storefront, you may deploy the storefront Per Vercel’s [license and plans](https://vercel.com/pricing), their free plan can only be used for personal, non-commercial projects. So, you can deploy the storefront on the free plan for development purposes, but for commercial projects, you must update your Vercel plan. -Refer to [this reference](undefined/resources/deployment) to find how-to deployment guides for specific hosting providers. +Refer to [this reference](https://docs.medusajs.com/resources/deployment/index.html.md) to find how-to deployment guides for specific hosting providers. + + +# Debugging and Testing + +In the next chapters, you’ll learn about the tools Medusa provides for testing and debugging your Medusa application. + +By the end of this chapter, you’ll learn: + +- How to use Medusa's `@medusajs/test-utils` test to write integration tests. +- How to use Medusa’s `Logger` utility to log messages. # Install Medusa @@ -203,7 +203,7 @@ In this chapter, you'll learn how to install and run a Medusa application. ## Create Medusa Application -A Medusa application is made up of a Node.js server and an admin dashboard. You can optionally install a separate [Next.js storefront](undefined/learn/storefront-development/nextjs-starter) either while installing the Medusa application or at a later point. +A Medusa application is made up of a Node.js server and an admin dashboard. You can optionally install a separate [Next.js storefront](https://docs.medusajs.com/learn/storefront-development/nextjs-starter/index.html.md) either while installing the Medusa application or at a later point. ### Prerequisites @@ -237,9 +237,9 @@ You can stop the servers for the Medusa application and Next.js storefront by ex If you ran into an error during your installation, refer to the following troubleshooting guides for help: -1. [create-medusa-app troubleshooting guides](undefined/resources/troubleshooting/create-medusa-app-errors). -2. [CORS errors](undefined/resources/troubleshooting/cors-errors). -3. [All troubleshooting guides](undefined/resources/troubleshooting). +1. [create-medusa-app troubleshooting guides](https://docs.medusajs.com/resources/troubleshooting/create-medusa-app-errors/index.html.md). +2. [CORS errors](https://docs.medusajs.com/resources/troubleshooting/cors-errors/index.html.md). +3. [All troubleshooting guides](https://docs.medusajs.com/resources/troubleshooting/index.html.md). If you can't find your error reported anywhere, please open a [GitHub issue](https://github.com/medusajs/medusa/issues/new/choose). @@ -255,7 +255,7 @@ npm run dev This runs your Medusa application at `http://localhost:9000`, and the Medusa Admin dashboard `http://localhost:9000/app`. -For details on starting and configuring the Next.js storefront, refer to [this documentation](undefined/learn/storefront-development/nextjs-starter). +For details on starting and configuring the Next.js storefront, refer to [this documentation](https://docs.medusajs.com/learn/storefront-development/nextjs-starter/index.html.md). The application will restart if you make any changes to code under the `src` directory, except for admin customizations which are hot reloaded, providing you with a seamless developer experience without having to refresh your browser to see the changes. @@ -287,18 +287,18 @@ Your Medusa application's project will have the following files and directories: This directory is the central place for your custom development. It includes the following sub-directories: -- `admin`: Holds your admin dashboard's custom [widgets](undefined/learn/fundamentals/admin/widgets) and [UI routes](undefined/learn/fundamentals/admin/ui-routes). -- `api`: Holds your custom [API routes](undefined/learn/fundamentals/api-routes) that are added as endpoints in your Medusa application. -- `jobs`: Holds your [scheduled jobs](undefined/learn/fundamentals/scheduled-jobs) that run at a specified interval during your Medusa application's runtime. -- `links`: Holds your [module links](undefined/learn/fundamentals/module-links) that build associations between data models of different modules. -- `modules`: Holds your custom [modules](undefined/learn/fundamentals/modules) that implement custom business logic. -- `scripts`: Holds your custom [scripts](undefined/learn/fundamentals/custom-cli-scripts) to be executed using Medusa's CLI tool. -- `subscribers`: Holds your [event listeners](undefined/learn/fundamentals/events-and-subscribers) that are executed asynchronously whenever an event is emitted. -- `workflows`: Holds your custom [flows](undefined/learn/fundamentals/workflows) that can be executed from anywhere in your application. +- `admin`: Holds your admin dashboard's custom [widgets](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md) and [UI routes](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). +- `api`: Holds your custom [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) that are added as endpoints in your Medusa application. +- `jobs`: Holds your [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) that run at a specified interval during your Medusa application's runtime. +- `links`: Holds your [module links](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) that build associations between data models of different modules. +- `modules`: Holds your custom [modules](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md) that implement custom business logic. +- `scripts`: Holds your custom [scripts](https://docs.medusajs.com/learn/fundamentals/custom-cli-scripts/index.html.md) to be executed using Medusa's CLI tool. +- `subscribers`: Holds your [event listeners](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) that are executed asynchronously whenever an event is emitted. +- `workflows`: Holds your custom [flows](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) that can be executed from anywhere in your application. ### medusa-config.ts -This file holds your [Medusa configurations](undefined/resources/references/medusa-config), such as your PostgreSQL database configurations. +This file holds your [Medusa configurations](https://docs.medusajs.com/resources/references/medusa-config/index.html.md), such as your PostgreSQL database configurations. *** @@ -306,13 +306,13 @@ This file holds your [Medusa configurations](undefined/resources/references/medu By default, your Medusa application is equipped with the basic configuration to start your development. -If you run into issues with configurations, such as CORS configurations, or need to make changes to the default configuration, refer to [this guide on all available configurations](undefined/resources/references/medusa-config). +If you run into issues with configurations, such as CORS configurations, or need to make changes to the default configuration, refer to [this guide on all available configurations](https://docs.medusajs.com/resources/references/medusa-config/index.html.md). *** ## Update Medusa Application -Refer to [this documentation](undefined/learn/update) to learn how to update your Medusa project. +Refer to [this documentation](https://docs.medusajs.com/learn/update/index.html.md) to learn how to update your Medusa project. *** @@ -325,7 +325,7 @@ In the next chapters, you'll learn about the architecture of your Medusa applica The Development Resources documentation provides guides and references that are useful for your development. This documentation included links to parts of the Development Resources documentation where necessary. -Check out the Development Resources documentation [here](undefined/resources). +Check out the Development Resources documentation [here](https://docs.medusajs.com/resources/index.html.md). # Storefront Development @@ -334,8 +334,8 @@ The Medusa application is made up of a Node.js server and an admin dashboard. St 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](undefined/resources/nextjs-starter) -- [Build Custom Storefront](undefined/resources/storefront-development) +- [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) *** @@ -347,7 +347,7 @@ A publishable API key sets the scope of your request to one or more sales channe 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](undefined/resources/storefront-development/publishable-api-keys). +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 @@ -360,7 +360,7 @@ Medusa's current version is v{config.version.number}. {releaseNoteText} When Medusa puts out a new release, all packages are updated to the same version. This ensures that all packages are compatible with each other, and makes it easier for you to switch between versions. -This doesn't apply to the design-system packages, including `@medusajs/ui`, `@medusajs/ui-presets`, and `@medusajs/ui-icons`. These packages are versioned independently. However, you don't need to install and manage them separately in your Medusa application, as they are included in the `@medusajs/admin-sdk`. If you're using them in a standalone project, such as a storefront or custom admin dashboard, refer to [this section in the Medusa UI documentation](undefined/ui/installation/standalone-project#updating-ui-packages) for update instructions. +This doesn't apply to the design-system packages, including `@medusajs/ui`, `@medusajs/ui-presets`, and `@medusajs/ui-icons`. These packages are versioned independently. However, you don't need to install and manage them separately in your Medusa application, as they are included in the `@medusajs/admin-sdk`. If you're using them in a standalone project, such as a storefront or custom admin dashboard, refer to [this section in the Medusa UI documentation](https://docs.medusajs.com/ui/installation/standalone-project#updating-ui-packages/index.html.md) for update instructions. Medusa updates the version number `major.minor.patch` according to the following rules: @@ -378,7 +378,7 @@ To check the currently installed version of Medusa in your project, run the foll npx medusa -v ``` -This will show you the installed version of Medusa and the [Medusa CLI tool](undefined/resources/medusa-cli), which should be the same. +This will show you the installed version of Medusa and the [Medusa CLI tool](https://docs.medusajs.com/resources/medusa-cli/index.html.md), which should be the same. *** @@ -500,14 +500,39 @@ import { BrandModuleService } from "@/modules/brand/service" ``` +# 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. + +By following these guides, you'll add brands to the Medusa application that you can associate with products. + +To build a custom feature in Medusa, you need three main tools: + +- [Module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md): a package with commerce logic for a single domain. It defines new tables to add to the database, and a class of methods to manage these tables. +- [Workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md): a tool to perform an operation comprising multiple steps with built-in rollback and retry mechanisms. +- [API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md): a REST endpoint that exposes commerce features to clients, such as the admin dashboard or a storefront. The API route executes a workflow that implements the commerce feature using modules. + +![Diagram showcasing the flow of a custom developed feature](https://res.cloudinary.com/dza7lstvk/image/upload/v1725867628/Medusa%20Book/custom-development_nofvp6.jpg) + +*** + +## Next Chapters: Brand Module Example + +The next chapters will guide you to: + +1. Build a Brand Module that creates a `Brand` data model and provides data-management features. +2. Add a workflow to create a brand. +3. Expose an API route that allows admin users to create a brand using the workflow. + + # Customize Medusa Admin Dashboard -In the previous chapters, you've customized your Medusa application to [add brands](undefined/learn/customization/custom-features/module), [expose an API route to create brands](undefined/learn/customization/custom-features/api-route), and [linked brands to products](undefined/learn/customization/extend-features/define-link). +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). After customizing and extending your application with new features, you may need to provide an interface for admin users to utilize these features. The Medusa Admin dashboard is extendable, allowing you to: -- Insert components, called [widgets](undefined/learn/fundamentals/admin/widgets), on existing pages. -- Add new pages, called [UI Routes](undefined/learn/fundamentals/admin/ui-routes). +- Insert components, called [widgets](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md), on existing pages. +- Add new pages, called [UI Routes](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). From these customizations, you can send requests to custom API routes, allowing admin users to manage custom resources on the dashboard @@ -521,29 +546,27 @@ In the next chapters, you'll continue with the brands example to: - Add a new page in the dashboard that shows all brands in the store. -# 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. +# Extend Core Commerce Features -By following these guides, you'll add brands to the Medusa application that you can associate with products. +In the upcoming chapters, you'll learn about the concepts and tools to extend Medusa's core commerce features. -To build a custom feature in Medusa, you need three main tools: +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. -- [Module](undefined/learn/fundamentals/modules): a package with commerce logic for a single domain. It defines new tables to add to the database, and a class of methods to manage these tables. -- [Workflow](undefined/learn/fundamentals/workflows): a tool to perform an operation comprising multiple steps with built-in rollback and retry mechanisms. -- [API route](undefined/learn/fundamentals/api-routes): a REST endpoint that exposes commerce features to clients, such as the admin dashboard or a storefront. The API route executes a workflow that implements the commerce feature using modules. +Medusa's framework and orchestration tools mitigate these issues while supporting all your customization needs: -![Diagram showcasing the flow of a custom developed feature](https://res.cloudinary.com/dza7lstvk/image/upload/v1725867628/Medusa%20Book/custom-development_nofvp6.jpg) +- [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: Brand Module Example +## Next Chapters: Link Brands to Products Example -The next chapters will guide you to: +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: -1. Build a Brand Module that creates a `Brand` data model and provides data-management features. -2. Add a workflow to create a brand. -3. Expose an API route that allows admin users to create a brand using the workflow. +- 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 @@ -554,12 +577,12 @@ The next chapters will cover each of these concepts in depth, with the different ## Helpful Resources Guides -The [Development Resources](undefined/resources) documentation provides more helpful guides and references for your development journey. Some of these guides and references include: +The [Development Resources](https://docs.medusajs.com/resources/index.html.md) documentation provides more helpful guides and references for your development journey. Some of these guides and references include: -3. [Commerce Modules](undefined/resources/commerce-modules): Browse the list of commerce modules in Medusa and their references to learn how to use them. -4. [Service Factory Reference](undefined/resources/service-factory-reference): Learn about the methods generated by `MedusaService` with examples. -5. [Workflows Reference](undefined/resources/medusa-workflows-reference): Browse the list of core workflows and their hooks that are useful for your customizations. -6. [Admin Injection Zones](undefined/resources/admin-widget-injection-zones): Browse the injection zones in the Medusa Admin to learn where you can inject widgets. +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. *** @@ -567,53 +590,310 @@ The [Development Resources](undefined/resources) documentation provides more hel In the Development Resources documentation, you'll also find step-by-step guides for different use cases, such as building a marketplace, digital products, and more. -Refer to the [Recipes](undefined/resources/recipes) documentation to learn more. +Refer to the [Recipes](https://docs.medusajs.com/resources/recipes/index.html.md) documentation to learn more. -# Extend Core Commerce Features +# General Medusa Application Deployment Guide -In the upcoming chapters, you'll learn about the concepts and tools to extend Medusa's core commerce features. +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. -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. +Find how-to guides for specific platforms in [this documentation](https://docs.medusajs.com/resources/deployment/index.html.md). -Medusa's framework and orchestration tools mitigate these issues while supporting all your customization needs: +Want Medusa to manage and maintain your infrastructure? [Sign up and learn more about Medusa Cloud](https://medusajs.com/contact) + +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. -- [Module Links](undefined/learn/fundamentals/module-links): Link data models of different modules without building direct dependencies, ensuring that the Medusa application integrates your modules without side effects. -- [Workflow Hooks](undefined/learn/fundamentals/workflows/workflow-hooks): 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](undefined/learn/fundamentals/api-routes/additional-data): 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. +### Prerequisites + +- [Medusa application’s codebase hosted on GitHub repository.](https://docs.medusajs.com/learn/index.html.md) + +## Hosting Provider Requirements + +When you deploy your Medusa application, make sure your chosen hosting provider supports deploying the following resources: + +1. PostgreSQL database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the PostgreSQL database. +2. Redis database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the Redis database. +3. Medusa application in server and worker mode. This means your hosting provider should support deploying two applications or instances from the same codebase. +4. For optimal experience, the hosting provider and plan must offer at least 2GB of RAM. *** -## Next Chapters: Link Brands to Products Example +## 1. Configure Medusa Application -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](undefined/learn/customization/custom-features) to: +### Worker Mode -- Link brands from the custom [Brand Module](undefined/learn/customization/custom-features/module) to products from Medusa's [Product Module](undefined/resources/commerce-modules/product). -- 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. +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. -# Integrate Third-Party Systems +Learn more about the `workerMode` configuration in [this document](https://docs.medusajs.com/resources/references/medusa-config#workermode/index.html.md). -Commerce applications often connect to third-party systems that provide additional or specialized features. For example, you may integrate a Content-Management System (CMS) for rich content features, a payment provider to process credit-card payments, and a notification service to send emails. +So, add the following configuration in `medusa-config.ts`: -Medusa's framework facilitates integrating these systems and orchestrating operations across them, saving you the effort of managing them yourself. You won't find those capabilities in other commerce platforms that in these scenarios become a bottleneck to building customizations and iterating quickly. +```ts title="medusa-config.ts" +module.exports = defineConfig({ + projectConfig: { + // ... + workerMode: process.env.MEDUSA_WORKER_MODE as "shared" | "worker" | "server", + }, +}) +``` -In Medusa, you integrate a third-party system by: +Later, you’ll set different values of the `MEDUSA_WORKER_MODE` environment variable for each Medusa application deployment or instance. -1. Creating a module whose service provides the methods to connect to and perform operations in the third-party system. -2. Building workflows that complete tasks spanning across systems. You use the module that integrates a third-party system in the workflow's steps. -3. Executing the workflows you built in an [API route](undefined/learn/fundamentals/api-routes), at a scheduled time, or when an event is emitted. +### Configure Medusa Admin + +You need to disable the Medusa Admin in the worker Medusa application, while keeping it enabled in the server Medusa application. So, 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. + +### 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/resources/references/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, + }, +}) +``` *** -## Next Chapters: Sync Brands Example +## 2. Add predeploy Script -In the previous chapters, you've [added brands](undefined/learn/customization/custom-features/module) to your Medusa application. In the next chapters, you will: +Before you start the Medusa application in production, you should always run migrations and sync links. -1. Integrate a dummy third-party CMS in the Brand Module. -2. Sync brands to the CMS when a brand is created. -3. Sync brands from the CMS at a daily schedule. +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. + +### 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 run the `start` command from the `.medusa/server` directory. + +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 predeploy && npm run start +``` + +### Set Backend URL in Admin 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`. + +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 run the `start` command from the `.medusa/server` directory. + +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 predeploy && 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. # Re-Use Customizations with Plugins @@ -628,7 +908,7 @@ To reuse your customizations across multiple Medusa applications, such as implem Medusa provides the tooling to create a plugin package, test it in a local Medusa application, and publish it to NPM. -To learn more about plugins and how to create them, refer to [this chapter](undefined/learn/fundamentals/plugins). +To learn more about plugins and how to create them, refer to [this chapter](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). # Configure Instrumentation @@ -741,6 +1021,29 @@ Trace span names start with the following keywords based on what it's reporting: - `pg.query:` when reporting database queries and operations. +# Integrate Third-Party Systems + +Commerce applications often connect to third-party systems that provide additional or specialized features. For example, you may integrate a Content-Management System (CMS) for rich content features, a payment provider to process credit-card payments, and a notification service to send emails. + +Medusa's framework facilitates integrating these systems and orchestrating operations across them, saving you the effort of managing them yourself. You won't find those capabilities in other commerce platforms that in these scenarios become a bottleneck to building customizations and iterating quickly. + +In Medusa, you integrate a third-party system by: + +1. Creating a module whose service provides the methods to connect to and perform operations in the third-party system. +2. Building workflows that complete tasks spanning across systems. You use the module that integrates a third-party system in the workflow's steps. +3. Executing the workflows you built in an [API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), at a scheduled time, or when an event is emitted. + +*** + +## Next Chapters: Sync Brands Example + +In the previous chapters, you've [added brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) to your Medusa application. In the next chapters, you will: + +1. Integrate a dummy third-party CMS in the Brand Module. +2. Sync brands to the CMS when a brand is created. +3. Sync brands from the CMS at a daily schedule. + + # Logging In this chapter, you’ll learn how to use Medusa’s logging utility. @@ -977,332 +1280,157 @@ Medusa's Testing Framework works for integration tests only. You can write unit 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](undefined/resources/deployment). - -Want Medusa to manage and maintain your infrastructure? [Sign up and learn more about Medusa Cloud](https://medusajs.com/contact) - -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. +# Medusa's Architecture -With Medusa Cloud, you maintain full customization control as you deploy your own modules and customizations directly from GitHub: +In this chapter, you'll learn about the architectural layers in Medusa. -- Push to deploy. -- Multiple testing environments. -- Preview environments for new PRs. -- Test on production-like data. +## HTTP, Workflow, and Module Layers -### Prerequisites +Medusa is a headless commerce platform. So, storefronts, admin dashboards, and other clients consume Medusa's functionalities through its API routes. -- [Medusa application’s codebase hosted on GitHub repository.](undefined/learn) +In a common Medusa application, requests go through four layers in the stack. In order of entry, those are: -## Hosting Provider Requirements +1. API Routes (HTTP): Our API Routes are the typical entry point. +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. -When you deploy your Medusa application, make sure your chosen hosting provider supports deploying the following resources: +These layers of stack can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). -1. PostgreSQL database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the PostgreSQL database. -2. Redis database: If your hosting provider doesn't support database hosting, you must find another hosting provider for the Redis database. -3. Medusa application in server and worker mode. This means your hosting provider should support deploying two applications or instances from the same codebase. -4. For optimal experience, the hosting provider and plan must offer at least 2GB of RAM. +![Diagram illustrating the HTTP layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175296/Medusa%20Book/http-layer_sroafr.jpg) *** -## 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 the `workerMode` configuration in [this document](undefined/resources/references/medusa-config#workermode). - -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. +## Database Layer -### Configure Medusa Admin +The Medusa application injects into each module a connection to the configured PostgreSQL database. Modules use that connection to read and write data to the database. -You need to disable the Medusa Admin in the worker Medusa application, while keeping it enabled in the server Medusa application. So, add the following configuration in `medusa-config.ts`: +Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). -```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. +![Diagram illustrating the database layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175379/Medusa%20Book/db-layer_pi7tix.jpg) -### Configure Redis URL +*** -The `redisUrl` configuration specifies the connection URL to Redis to store the Medusa server's session. +## Service Integrations -Learn more in the [Medusa Configuration documentation](undefined/resources/references/medusa-config#redisurl). +Third-party services are integrated through commerce and architectural modules. You also create custom third-party integrations through a custom module. -So, add the following configuration in `medusa-config.ts` : +Modules can be implemented within [plugins](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). -```ts title="medusa-config.ts" -module.exports = defineConfig({ - projectConfig: { - // ... - redisUrl: process.env.REDIS_URL, - }, -}) -``` +### 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 integrate Stripe through a payment module provider. -## 2. Add predeploy Script +![Diagram illustrating the commerce modules integration to third-party services](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175357/Medusa%20Book/service-commerce_qcbdsl.jpg) -Before you start the Medusa application in production, you should always run migrations and sync links. +### Architectural Modules -So, add the following script in `package.json`: +[Architectural modules](https://docs.medusajs.com/resources/architectural-modules/index.html.md) integrate third-party services and systems for architectural features. For example, you integrate Redis as a pub/sub service to send events, or SendGrid to send notifications. -```json -"scripts": { - // ... - "predeploy": "medusa db:migrate" -}, -``` +![Diagram illustrating the architectural modules integration to third-party services and systems](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175342/Medusa%20Book/service-arch_ozvryw.jpg) *** -## 3. Install Production Modules and Providers +## Full Diagram of Medusa's Architecture -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. +The following diagram illustrates Medusa's architecture over the three layers. -It’s highly recommended to instead use modules and providers suitable for production, including: +![Full diagram illustrating Medusa's architecture](https://res.cloudinary.com/dza7lstvk/image/upload/v1727174897/Medusa%20Book/architectural-diagram-full.jpg) -- [Redis Cache Module](undefined/resources/architectural-modules/cache/redis) -- [Redis Event Bus Module](undefined/resources/architectural-modules/event/redis) -- [Workflow Engine Redis Module](undefined/resources/architectural-modules/workflow-engine/redis) -- [S3 File Module Provider](undefined/resources/architectural-modules/file/s3) (or other file module providers production-ready). -- [SendGrid Notification Module Provider](undefined/resources/architectural-modules/notification/sendgrid) (or other notification module providers production-ready). -Then, add these modules in `medusa-config.ts`: +# Admin Development -```ts title="medusa-config.ts" -import { Modules } from "@medusajs/framework/utils" +In the next chapters, you'll learn more about possible admin customizations. -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, - }, - }, - }, - ], -}) -``` +You can customize the admin dashboard by: -Check out the [Integrations](undefined/resources/integrations) and [Architectural Modules](undefined/resources/architectural-modules) documentation for other modules and providers to use. +- Adding new sections to existing pages using Widgets. +- Adding new pages using UI Routes. *** -## 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. +## Medusa UI Package -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/). +Medusa provides a Medusa UI package to facilitate your admin development through ready-made components and ensure a consistent design between your customizations and the dashboard’s design. -After hosting both databases, keep their connection URLs for the next steps. +Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/index.html.md) to learn how to install it and use its components. *** -## 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 -``` +## Admin Components List -Where: +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. -- 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. +# Custom CLI Scripts -### Set Start Command +In this chapter, you'll learn how to create and execute custom scripts from Medusa's CLI tool. -The Medusa application's production build, which is created using the `build` command, outputs the Medusa application to `.medusa/server`. +## What is a Custom CLI Script? -So, you must run the `start` command from the `.medusa/server` directory. +A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI. -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 predeploy && npm run start -``` +## How to Create a Custom CLI Script? -### Set Backend URL in Admin Configuration +To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function. -After you’ve obtained the Medusa application’s URL, add the following configuration to `medusa-config.ts`: +For example, create the file `src/scripts/my-script.ts` with the following content: -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - admin: { - // ... - backendUrl: process.env.MEDUSA_BACKEND_URL, - }, -}) -``` +```ts title="src/scripts/my-script.ts" +import { + ExecArgs, + IProductModuleService, +} from "@medusajs/framework/types" +import { Modules } from "@medusajs/framework/utils" -Then, push the changes to the GitHub repository or deployed application. +export default async function myScript({ container }: ExecArgs) { + const productModuleService: IProductModuleService = container.resolve( + Modules.PRODUCT + ) -In your hosting provider, add or modify the following environment variables for the Medusa application in server mode: + const [, count] = await productModuleService + .listAndCountProducts() -```bash -ADMIN_CORS= # MEDUSA APPLICATION URL -AUTH_CORS= # ADD MEDUSA APPLICATION URL -MEDUSA_BACKEND_URL= # URL TO DEPLOYED MEDUSA APPLICATION + console.log(`You have ${count} product(s)`) +} ``` -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`. - -Remember to separate URLs in `AUTH_CORS` by commas. +The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application. *** -## 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 +## How to Run Custom CLI Script? -When setting the environment variables of the Medusa application, set the following variables: +To run the custom CLI script, run the Medusa CLI's `exec` command: ```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 run the `start` command from the `.medusa/server` directory. - -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 predeploy && npm run start +npx medusa exec ./src/scripts/my-script.ts ``` *** -## 7. Test Deployed Application +## Custom CLI Script Arguments -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. +Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property. -The Medusa Admin is also available at `/app`. +For example: -*** +```ts +import { ExecArgs } from "@medusajs/framework/types" -## Create Admin User +export default async function myScript({ args }: ExecArgs) { + console.log(`The arguments you passed: ${args}`) +} +``` -If your hosting provider supports running commands in your Medusa application's directory, run the following command to create an admin user: +Then, pass the arguments in the `exec` command after the file path: ```bash -npx medusa user -e admin-medusa@test.com -p supersecret +npx medusa exec ./src/scripts/my-script.ts arg1 arg2 ``` -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. - -You can customize the admin dashboard by: - -- Adding new sections to existing pages using Widgets. -- Adding new pages using UI Routes. - -*** - -## Medusa UI Package - -Medusa provides a Medusa UI package to facilitate your admin development through ready-made components and ensure a consistent design between your customizations and the dashboard’s design. - -Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/index.html.md) to learn how to install it and use its components. - -*** - -## Admin Components List - -To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](undefined/resources/admin-components) to find common components. - # API Routes @@ -1363,76 +1491,6 @@ curl http://localhost:9000/hello-world 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. - -## What is a Custom CLI Script? - -A custom CLI script is a function to execute through Medusa's CLI tool. This is useful when creating custom Medusa tooling to run through the CLI. - -*** - -## How to Create a Custom CLI Script? - -To create a custom CLI script, create a TypeScript or JavaScript file under the `src/scripts` directory. The file must default export a function. - -For example, create the file `src/scripts/my-script.ts` with the following content: - -```ts title="src/scripts/my-script.ts" -import { - ExecArgs, - IProductModuleService, -} from "@medusajs/framework/types" -import { Modules } from "@medusajs/framework/utils" - -export default async function myScript({ container }: ExecArgs) { - const productModuleService: IProductModuleService = container.resolve( - Modules.PRODUCT - ) - - const [, count] = await productModuleService - .listAndCountProducts() - - console.log(`You have ${count} product(s)`) -} -``` - -The function receives as a parameter an object having a `container` property, which is an instance of the Medusa Container. Use it to resolve resources in your Medusa application. - -*** - -## How to Run Custom CLI Script? - -To run the custom CLI script, run the Medusa CLI's `exec` command: - -```bash -npx medusa exec ./src/scripts/my-script.ts -``` - -*** - -## Custom CLI Script Arguments - -Your script can accept arguments from the command line. Arguments are passed to the function's object parameter in the `args` property. - -For example: - -```ts -import { ExecArgs } from "@medusajs/framework/types" - -export default async function myScript({ args }: ExecArgs) { - console.log(`The arguments you passed: ${args}`) -} -``` - -Then, pass the arguments in the `exec` command after the file path: - -```bash -npx medusa exec ./src/scripts/my-script.ts arg1 arg2 -``` - - # Environment Variables In this chapter, you'll learn how environment variables are loaded in Medusa. @@ -1476,20 +1534,7 @@ To ensure that the correct `.env` file is loaded as shown in the table above, on Since the Medusa Admin is built on top of [Vite](https://vite.dev/), you prefix the environment variables you want to use in a widget or UI route with `VITE_`. Then, you can access or use them with the `import.meta.env` object. -Learn more in [this documentation](undefined/learn/fundamentals/admin/environment-variables). - - -# Data Models Advanced Guides - -Data models are created and managed in a module. To learn how to create a data model in a custom module, refer to the [Modules chapter](undefined/learn/fundamentals/modules). - -In the next chapters, you'll learn about defining data models in more details. You'll learn about: - -- The different property types available. -- How to set a property as a primary key. -- How to create and manage relationships. -- How to configure properties, such as making them nullable or searchable. -- How to manually write migrations. +Learn more in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). # Events and Subscribers @@ -1508,11 +1553,11 @@ You listen to an event in a subscriber, which is an asynchronous function that's Subscribers are useful to perform actions that aren't integral to the original flow. For example, you can handle the `order.placed` event in a subscriber that sends a confirmation email to the customer. The subscriber has no impact on the original order-placement flow, as it's executed outside of it. -If the action you're performing is integral to the main flow of the core commerce feature, use [workflow hooks](undefined/learn/fundamentals/workflows/workflow-hooks) instead. +If the action you're performing is integral to the main flow of the core commerce feature, use [workflow hooks](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md) instead. ### List of Emitted Events -Find a list of all emitted events in [this reference](undefined/resources/events-reference). +Find a list of all emitted events in [this reference](https://docs.medusajs.com/resources/events-reference/index.html.md). *** @@ -1557,9 +1602,9 @@ This subscriber file exports: The subscriber function receives an object as a parameter that has the following properties: - `event`: An object with the event's details. The `data` property contains the data payload of the event emitted, which is the order's ID in this case. -- `container`: The [Medusa container](undefined/learn/fundamentals/medusa-container) that you can use to resolve registered resources. +- `container`: The [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) that you can use to resolve registered resources. -In the subscriber function, you use the container to resolve the Logger utility and log a message in the console. Also, assuming you have a [workflow](undefined/learn/fundamentals/workflows) that sends an order confirmation email, you execute it in the subscriber. +In the subscriber function, you use the container to resolve the Logger utility and log a message in the console. Also, assuming you have a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md) that sends an order confirmation email, you execute it in the subscriber. *** @@ -1571,7 +1616,7 @@ To test the subscriber, start the Medusa application: npm run dev ``` -Then, try placing an order either using Medusa's API routes or the [Next.js Storefront](undefined/learn/storefront-development/nextjs-starter). You'll see the following message in the terminal: +Then, try placing an order either using Medusa's API routes or the [Next.js Storefront](https://docs.medusajs.com/learn/storefront-development/nextjs-starter/index.html.md). You'll see the following message in the terminal: ```bash info: Processing order.placed which has 1 subscribers @@ -1588,10 +1633,10 @@ The subscription and emitting of events is handled by an Event Module, an archit Medusa provides two Event Modules out of the box: -- [Local Event Module](undefined/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](undefined/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](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. -Medusa's [architecture](undefined/learn/introduction/architecture) 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](undefined/resources/architectural-modules/event/create). +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). # Medusa Container @@ -1606,7 +1651,7 @@ In other platforms, if you have a resource A (for example, a class) that depends Medusa simplifies this process by giving you access to the container, with the tools or resources already registered, at all times in your customizations. When you reach a point in your code where you need a tool, you resolve it from the container and use it. -For example, consider you're creating an API route that retrieves products based on filters using [Query](undefined/learn/fundamentals/module-links/query), a tool that fetches data across the application. In the API route's function, you can resolve Query from the container passed to the API route and use it: +For example, consider you're creating an API route that retrieves products based on filters using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md), a tool that fetches data across the application. In the API route's function, you can resolve Query from the container passed to the API route and use it: ```ts highlights={highlights} import { MedusaRequest, MedusaResponse } from "@medusajs/framework" @@ -1634,13 +1679,13 @@ export async function GET( The API route accepts as a first parameter a request object that has a `scope` property, which is the Medusa container. It has a `resolve` method that resolves a resource from the container by the key it's registered with. -You can learn more about how Query works in [this chapter](undefined/learn/fundamentals/module-links/query). +You can learn more about how Query works in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). *** ## List of Resources Registered in the Medusa Container -Find a full list of the registered resources and their registration key in [this reference](undefined/resources/medusa-container-resources) +Find a full list of the registered resources and their registration key in [this reference](https://docs.medusajs.com/resources/medusa-container-resources/index.html.md) *** @@ -1650,7 +1695,7 @@ This section gives quick examples of how to resolve resources from the Medusa co ### Subscriber -A [subscriber](undefined/learn/fundamentals/events-and-subscribers) function, which is executed when an event is emitted, accepts as a parameter an object with a `container` property, whose value is the Medusa container. Use its `resolve` method to resolve a resource by its registration key: +A [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md) function, which is executed when an event is emitted, accepts as a parameter an object with a `container` property, whose value is the Medusa container. Use its `resolve` method to resolve a resource by its registration key: ```ts highlights={subscriberHighlights} import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" @@ -1680,7 +1725,7 @@ export const config: SubscriberConfig = { ### Scheduled Job -A [scheduled job](undefined/learn/fundamentals/scheduled-jobs) function, which is executed at a specified interval, accepts the Medusa container as a parameter. Use its `resolve` method to resolve a resource by its registration key: +A [scheduled job](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md) function, which is executed at a specified interval, accepts the Medusa container as a parameter. Use its `resolve` method to resolve a resource by its registration key: ```ts highlights={scheduledJobHighlights} import { MedusaContainer } from "@medusajs/framework/types" @@ -1713,7 +1758,7 @@ export const config = { ### Workflow Step -A [step in a workflow](undefined/learn/fundamentals/workflows), which is a special function where you build durable execution logic across multiple modules, accepts in its second parameter a `container` property, whose value is the Medusa container. Use its `resolve` method to resolve a resource by its registration key: +A [step in a workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), which is a special function where you build durable execution logic across multiple modules, accepts in its second parameter a `container` property, whose value is the Medusa container. Use its `resolve` method to resolve a resource by its registration key: ```ts highlights={workflowStepsHighlight} import { @@ -1739,9 +1784,66 @@ const step1 = createStep("step-1", async (_, { container }) => { ### Module Services and Loaders -A [module](undefined/learn/fundamentals/modules), which is a package of functionalities for a single feature or domain, has its own container, so it can't resolve resources from the Medusa container. +A [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), which is a package of functionalities for a single feature or domain, has its own container, so it can't resolve resources from the Medusa container. + +Learn more about the module's container in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md). + + +# Data Models Advanced Guides + +Data models are created and managed in a module. To learn how to create a data model in a custom module, refer to the [Modules chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). + +In the next chapters, you'll learn about defining data models in more details. You'll learn about: + +- The different property types available. +- How to set a property as a primary key. +- How to create and manage relationships. +- How to configure properties, such as making them nullable or searchable. +- How to manually write migrations. + + +# 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. + +![Diagram showcasing a wishlist plugin installed in a Medusa application](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540762/Medusa%20Book/plugin-diagram_oepiis.jpg) + +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. + +*** + +## Plugin Guides and Resources -Learn more about the module's container in [this chapter](undefined/learn/fundamentals/modules/container). +For more resources and guides related to plugins, refer to the [Resources documentation](https://docs.medusajs.com/resources/plugins/index.html.md). # Module Link @@ -1874,7 +1976,7 @@ In this chapter, you’ll learn about modules and how to create them. ## What is a Module? -A module is a reusable package of functionalities related to a single domain or integration. Medusa comes with multiple pre-built modules for core commerce needs, such as the [Cart Module](undefined/resources/commerce-modules/cart) that holds the data models and business logic for cart operations. +A module is a reusable package of functionalities related to a single domain or integration. Medusa comes with multiple pre-built modules for core commerce needs, such as the [Cart Module](https://docs.medusajs.com/resources/commerce-modules/cart/index.html.md) that holds the data models and business logic for cart operations. When building a commerce application, you often need to introduce custom behavior specific to your products, tech stack, or your general ways of working. In other commerce platforms, introducing custom business logic and data models requires setting up separate applications to manage these customizations. @@ -1884,13 +1986,13 @@ As you learn more about Medusa, you will see that modules are central to customi - You want to build a custom feature related to a single domain or integrate a third-party service. -- You want to create a reusable package of customizations that include not only modules, but also API routes, workflows, and other customizations. Instead, use a [plugin](undefined/learn/fundamentals/plugins). +- You want to create a reusable package of customizations that include not only modules, but also API routes, workflows, and other customizations. Instead, use a [plugin](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). *** ## How to Create a Module? -In a module, you define data models that represent new tables in the database, and you manage these models in a class called a service. Then, the Medusa application registers the module's service in the [Medusa container](undefined/learn/fundamentals/medusa-container) so that you can build commerce flows and features around the functionalities provided by the module. +In a module, you define data models that represent new tables in the database, and you manage these models in a class called a service. Then, the Medusa application registers the module's service in the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) so that you can build commerce flows and features around the functionalities provided by the module. In this section, you'll build a Blog Module that has a `Post` data model and a service to manage that data model, you'll expose an API endpoint to create a blog post. @@ -1921,13 +2023,13 @@ You define the data model using the `define` method of the DML. It accepts two p 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. -Learn about other property types in [this chapter](undefined/learn/fundamentals/data-models/property-types). +Learn about other property types in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/property-types/index.html.md). The code snippet above defines a `Post` data model with `id` and `title` properties. ### 2. Create Service -You perform database operations on your data models in a service, which is a class exported by the module and acts like an interface to its functionalities. Medusa registers the service in its [container](undefined/learn/fundamentals/medusa-container), allowing you to resolve and use it when building custom commerce flows. +You perform database operations on your data models in a service, which is a class exported by the module and acts like an interface to its functionalities. Medusa registers the service in its [container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md), allowing you to resolve and use it when building custom commerce flows. You define a service in a `service.ts` or `service.js` file at the root of your module's directory. So, to create the Blog Module's service, create the file `src/modules/blog/service.ts` with the following content: @@ -1951,7 +2053,7 @@ The `MedusaService` function accepts an object of data models to generate method For example, the `BlogModuleService` now has a `createPosts` method to create post records, and a `retrievePost` method to retrieve a post record. The suffix of each method (except for `retrieve`) is the pluralized name of the data model. -Find all methods generated by the `MedusaService` in [this reference](undefined/resources/service-factory-reference) +Find all methods generated by the `MedusaService` in [this reference](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) If a module doesn't have data models, such as when it's integrating a third-party service, it doesn't need to extend `MedusaService`. @@ -1983,7 +2085,7 @@ You export `BLOG_MODULE` to reference the module's name more reliably when resol ### 4. Add Module to Medusa's Configurations -If you're creating the module in a plugin, this step isn't required as the module is registered when the plugin is registered. Learn more about plugins in [this documentation](undefined/learn/fundamentals/plugins). +If you're creating the module in a plugin, this step isn't required as the module is registered when the plugin is registered. Learn more about plugins in [this documentation](https://docs.medusajs.com/learn/fundamentals/plugins/index.html.md). Once you finish building the module, add it to Medusa's configurations to start using it. Medusa will then register the module's main service in the Medusa container, allowing you to resolve and use it in other customizations. @@ -2014,7 +2116,7 @@ You don't have to write migrations yourself. Medusa's CLI tool has a command tha 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](undefined/resources/medusa-cli/commands/plugin#plugindbgenerate) instead. +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 @@ -2058,9 +2160,9 @@ This creates the `post` table in the database. Since the module's main service is registered in the Medusa container, you can resolve it in other customizations to use its methods. -To test out the Blog Module, you'll add the functionality to create a post in a [workflow](undefined/learn/fundamentals/workflows), which is a special function that performs a task in a series of steps with rollback logic. Then, you'll expose an [API route](undefined/learn/fundamentals/api-routes) that creates a blog post by executing the workflow. +To test out the Blog Module, you'll add the functionality to create a post in a [workflow](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md), which is a special function that performs a task in a series of steps with rollback logic. Then, you'll expose an [API route](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md) that creates a blog post by executing the workflow. -By building a commerce feature in a workflow, you can execute it in other customizations while ensuring data consistency across systems. If an error occurs during execution, every step has its own rollback logic to undo its actions. Workflows have other special features which you can learn about in [this chapter](undefined/learn/fundamentals/workflows). +By building a commerce feature in a workflow, you can execute it in other customizations while ensuring data consistency across systems. If an error occurs during execution, every step has its own rollback logic to undo its actions. Workflows have other special features which you can learn about in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). To create the workflow, create the file `src/workflows/create-post.ts` with the following content: @@ -2165,51 +2267,7 @@ This will create a post and return it in the response: } ``` -You can also execute the workflow from a [subscriber](undefined/learn/fundamentals/events-and-subscribers) when an event occurs, or from a [scheduled job](undefined/learn/fundamentals/scheduled-jobs) 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](undefined/learn/fundamentals/modules), [API Routes](undefined/learn/fundamentals/api-routes), [Workflows](undefined/learn/fundamentals/workflows), [Workflow Hooks](undefined/learn/fundamentals/workflows/workflow-hooks), [Links](undefined/learn/fundamentals/module-links), [Subscribers](undefined/learn/fundamentals/events-and-subscribers), [Scheduled Jobs](undefined/learn/fundamentals/scheduled-jobs), and [Admin Extensions](undefined/learn/fundamentals/admin). - -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. - -![Diagram showcasing a wishlist plugin installed in a Medusa application](https://res.cloudinary.com/dza7lstvk/image/upload/v1737540762/Medusa%20Book/plugin-diagram_oepiis.jpg) - -Learn how to create a wishlist plugin in [this guide](undefined/resources/plugins/guides/wishlist). - -*** - -## Plugin vs Module - -A [module](undefined/learn/fundamentals/modules) 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](undefined/learn/fundamentals/modules). 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. - -*** - -## Plugin Guides and Resources - -For more resources and guides related to plugins, refer to the [Resources documentation](undefined/resources/plugins). +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. # Scheduled Jobs @@ -2225,8 +2283,8 @@ In other commerce platforms, this feature isn't natively supported. Instead, you 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](undefined/learn/fundamentals/modules/loaders) instead. -- You want to execute the action if an event occurs. Use [subscribers](undefined/learn/fundamentals/events-and-subscribers) instead. +- 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. *** @@ -2253,7 +2311,7 @@ export const config = { } ``` -You export an asynchronous function that receives the [Medusa container](undefined/learn/fundamentals/medusa-container) as a parameter. In the function, you resolve the [Logger utility](undefined/learn/debugging-and-testing/logging) from the Medusa container and log a message. +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: @@ -2282,9 +2340,9 @@ info: Greeting! 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](undefined/learn/fundamentals/modules), you use [workflows](undefined/learn/fundamentals/workflows). 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. +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](undefined/learn/fundamentals/workflows), 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: +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" @@ -2306,6 +2364,280 @@ In the scheduled job function, you execute the `syncProductToErpWorkflow` by inv The next time you start the Medusa application, it will run this job every day at midnight. +# Next.js Starter Storefront + +The Medusa application is made up of a Node.js server and an admin dashboard. The storefront is installed 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. + +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. + +In this chapter, you’ll learn how to install the Next.js Starter storefront separately from the Medusa application. You can also install it while installing the Medusa application as explained in [the installation chapter](https://docs.medusajs.com/learn/installation/index.html.md). + +## Install Next.js Starter + +### Prerequisites + +- [Node.js v20+](https://nodejs.org/en/download) +- [Git CLI tool](https://git-scm.com/downloads) + +If you already have a Medusa application installed with at least one region, you can install the Next.js Starter storefront with the following steps: + +1. Clone the [Next.js Starter](https://github.com/medusajs/nextjs-starter-medusa): + +```bash +git clone https://github.com/medusajs/nextjs-starter-medusa my-medusa-storefront +``` + +2. Change to the `my-medusa-storefront` directory, install the dependencies, and rename the template environment variable file: + +```bash npm2yarn +cd my-medusa-storefront +npm install +mv .env.template .env.local +``` + +3. Set the Medusa application's publishable API key in the `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY` environment variable. You can retrieve the publishable API key in on the Medusa Admin dashboard by going to Settings -> Publishable API Keys + +```bash +NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_123... +``` + +4. While the Medusa application is running, start the Next.js Starter storefront: + +```bash npm2yarn +npm run dev +``` + +Your Next.js Starter storefront is now running at `http://localhost:8000`. + +*** + +## Customize Storefront + +To customize the storefront, refer to the following directories: + +- `src/app`: The storefront’s pages. +- `src/modules`: The storefront’s components. +- `src/styles`: The storefront’s styles. + +You can learn more about development with Next.js through [their documentation](https://nextjs.org/docs/getting-started). + +*** + +## Configurations and Integrations + +The Next.js Starter is compatible with some Medusa integrations out-of-the-box, such as the Stripe provider module. You can also change some of its configurations if necessary. + +Refer to the [Next.js Starter reference](https://docs.medusajs.com/resources/nextjs-starter/index.html.md) for more details. + + +# 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. + +An API Route is an endpoint that acts as an entry point for other clients to interact with your Medusa customizations, such as the admin dashboard, storefronts, or third-party systems. + +The Medusa core application provides a set of [admin](https://docs.medusajs.com/api/admin) and [store](https://docs.medusajs.com/api/store) API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities. + +### Prerequisites + +- [createBrandWorkflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) + +## 1. Create the API Route + +You create an API route in a `route.{ts,js}` file under a sub-directory of the `src/api` directory. The file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…). + +Learn more about API routes [in this guide](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md). + +The route's path is the path of `route.{ts,js}` relative to `src/api`. So, to create the API route at `/admin/brands`, create the file `src/api/admin/brands/route.ts` with the following content: + +![Directory structure of the Medusa application after adding the route](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869882/Medusa%20Book/brand-route-dir-overview-2_hjqlnf.jpg) + +```ts title="src/api/admin/brands/route.ts" +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { + createBrandWorkflow, +} from "../../../workflows/create-brand" + +type PostAdminCreateBrandType = { + name: string +} + +export const POST = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const { result } = await createBrandWorkflow(req.scope) + .run({ + input: req.validatedBody, + }) + + res.json({ brand: result }) +} +``` + +You export a route handler function with its name (`POST`) being the HTTP method of the API route you're exposing. + +The function receives two parameters: a `MedusaRequest` object to access request details, and `MedusaResponse` object to return or manipulate the response. The `MedusaRequest` object's `scope` property is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) that holds framework tools and custom and core modules' services. + +`MedusaRequest` accepts the request body's type as a type argument. + +In the API route's handler, you execute the `createBrandWorkflow` by invoking it and passing the Medusa container `req.scope` as a parameter, then invoking its `run` method. You pass the workflow's input in the `input` property of the `run` method's parameter. You pass the request body's parameters using the `validatedBody` property of `MedusaRequest`. + +You return a JSON response with the created brand using the `res.json` method. + +*** + +## 2. Create Validation Schema + +The API route you created accepts the brand's name in the request body. So, you'll create a schema used to validate incoming request body parameters. + +Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters. + +Learn more about API route validation in [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/validation/index.html.md). + +You create a validation schema in a TypeScript or JavaScript file under a sub-directory of the `src/api` directory. So, create the file `src/api/admin/brands/validators.ts` with the following content: + +![Directory structure of Medusa application after adding validators file](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869806/Medusa%20Book/brand-route-dir-overview-1_yfyjss.jpg) + +```ts title="src/api/admin/brands/validators.ts" +import { z } from "zod" + +export const PostAdminCreateBrand = z.object({ + name: z.string(), +}) +``` + +You export a validation schema that expects in the request body an object having a `name` property whose value is a string. + +You can then replace `PostAdminCreateBrandType` in `src/api/admin/brands/route.ts` with the following: + +```ts title="src/api/admin/brands/route.ts" +// ... +import { z } from "zod" +import { PostAdminCreateBrand } from "./validators" + +type PostAdminCreateBrandType = z.infer + +// ... +``` + +*** + +## 3. Add Validation Middleware + +A middleware is a function executed before the route handler when a request is sent to an API Route. It's useful to guard API routes, parse custom request body types, and apply validation on an API route. + +Learn more about middlewares in [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/middlewares/index.html.md). + +Medusa provides a `validateAndTransformBody` middleware that accepts a Zod validation schema and returns a response error if a request is sent with body parameters that don't satisfy the validation schema. + +Middlewares are defined in the special file `src/api/middlewares.ts`. So, to add the validation middleware on the API route you created in the previous step, create the file `src/api/middlewares.ts` with the following content: + +![Directory structure of the Medusa application after adding the middleware](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869977/Medusa%20Book/brand-route-dir-overview-3_kcx511.jpg) + +```ts title="src/api/middlewares.ts" +import { + defineMiddlewares, + validateAndTransformBody, +} from "@medusajs/framework/http" +import { PostAdminCreateBrand } from "./admin/brands/validators" + +export default defineMiddlewares({ + routes: [ + { + matcher: "/admin/brands", + method: "POST", + middlewares: [ + validateAndTransformBody(PostAdminCreateBrand), + ], + }, + ], +}) +``` + +You define the middlewares using the `defineMiddlewares` function and export its returned value. The function accepts an object having a `routes` property, which is an array of middleware objects. + +In the middleware object, you define three properties: + +- `matcher`: a string or regular expression indicating the API route path to apply the middleware on. You pass the create brand's route `/admin/brand`. +- `method`: The HTTP method to restrict the middleware to, which is `POST`. +- `middlewares`: An array of middlewares to apply on the route. You pass the `validateAndTransformBody` middleware, passing it the Zod schema you created earlier. + +The Medusa application will now validate the body parameters of `POST` requests sent to `/admin/brands` to ensure they match the Zod validation schema. If not, an error is returned in the response specifying the issues to fix in the request body. + +*** + +## Test API Route + +To test out the API route, start the Medusa application with the following command: + +```bash npm2yarn +npm run dev +``` + +Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. + +So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: + +```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 with your admin user's credentials. + +Don't have an admin user? Refer to [this guide](https://docs.medusajs.com/learn/installation#create-medusa-admin-user/index.html.md). + +Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: + +```bash +curl -X POST 'http://localhost:9000/admin/brands' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer {token}' \ +--data '{ + "name": "Acme" +}' +``` + +This returns the created brand in the response: + +```json title="Example Response" +{ + "brand": { + "id": "01J7AX9ES4X113HKY6C681KDZJ", + "name": "Acme", + "created_at": "2024-09-09T08:09:34.244Z", + "updated_at": "2024-09-09T08:09:34.244Z" + } +} +``` + +*** + +## Summary + +By following the previous example chapters, you implemented a custom feature that allows admin users to create a brand. You did that by: + +1. Creating a module that defines and manages a `brand` table in the database. +2. Creating a workflow that uses the module's service to create a brand record, and implements the compensation logic to delete that brand in case an error occurs. +3. Creating an API route that allows admin users to create a brand. + +*** + +## Next Steps: Associate Brand with Product + +Now that you have brands in your Medusa application, you want to associate a brand with a product, which is defined in the [Product Module](https://docs.medusajs.com/resources/commerce-modules/product/index.html.md). + +In the next chapters, you'll learn how to build associations between data models defined in different modules. + + # Workflows In this chapter, you’ll learn about workflows and how to define and execute them. @@ -2324,7 +2656,7 @@ However, unlike regular functions, workflows: - Support defining roll-back logic for each step, so that you can handle errors gracefully and your data remain consistent across systems. - Perform long actions asynchronously, giving you control over when a step starts and finishes. -You implement all custom flows within workflows, then execute them from [API routes](undefined/learn/fundamentals/api-routes), [subscribers](undefined/learn/fundamentals/events-and-subscribers), and [scheduled jobs](undefined/learn/fundamentals/scheduled-jobs). +You implement all custom flows within workflows, then execute them from [API routes](https://docs.medusajs.com/learn/fundamentals/api-routes/index.html.md), [subscribers](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md), and [scheduled jobs](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md). *** @@ -2411,7 +2743,7 @@ You can execute a workflow from different customizations: - Execute in a subscriber to use the workflow's functionalities when a commerce operation is performed. - Execute in a scheduled job to run the workflow's functionalities automatically at a specified repeated interval. -To execute the workflow, invoke it passing the [Medusa container](undefined/learn/fundamentals/medusa-container) as a parameter. Then, use its `run` method: +To execute the workflow, invoke it passing the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) as a parameter. Then, use its `run` method: ### API Route @@ -2516,7 +2848,7 @@ You’ll receive the following response: ## Access Medusa Container in Workflow Steps -A step receives an object as a second parameter with configurations and context-related properties. One of these properties is the `container` property, which is the [Medusa container](undefined/learn/fundamentals/medusa-container) to allow you to resolve framework and commerce tools in your application. +A step receives an object as a second parameter with configurations and context-related properties. One of these properties is the `container` property, which is the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) to allow you to resolve framework and commerce tools in your application. For example, consider you want to implement a workflow that returns the total products in your application. Create the file `src/workflows/product-count.ts` with the following content: @@ -2557,356 +2889,234 @@ In `getProductCountStep`, you use the `container` to resolve the Product Module' You can now execute this workflow in a custom API route, scheduled job, or subscriber to get the total count of products. -Find a full list of the registered resources in the Medusa container and their registration key in [this reference](undefined/resources/medusa-container-resources). You can use these resources in your custom workflows. +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. -# Medusa's Architecture +# Guide: Implement Brand Module -In this chapter, you'll learn about the architectural layers in Medusa. +In this chapter, you'll build a Brand Module that adds a `brand` table to the database and provides data-management features for it. -## HTTP, Workflow, and Module Layers +A module is a reusable package of functionalities related to a single domain or integration. Medusa comes with multiple pre-built modules for core commerce needs, such as the [Cart Module](https://docs.medusajs.com/resources/commerce-modules/cart/index.html.md) that holds the data models and business logic for cart operations. -Medusa is a headless commerce platform. So, storefronts, admin dashboards, and other clients consume Medusa's functionalities through its API routes. +In a module, you create data models and business logic to manage them. In the next chapters, you'll see how you use the module to build commerce features. -In a common Medusa application, requests go through four layers in the stack. In order of entry, those are: +Learn more about modules in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). -1. API Routes (HTTP): Our API Routes are the typical entry point. -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. +## 1. Create Module Directory -These layers of stack can be implemented within [plugins](undefined/learn/fundamentals/plugins). +Modules are created in a sub-directory of `src/modules`. So, start by creating the directory `src/modules/brand` that will hold the Brand Module's files. -![Diagram illustrating the HTTP layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175296/Medusa%20Book/http-layer_sroafr.jpg) +![Directory structure in Medusa project after adding the brand directory](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868844/Medusa%20Book/brand-dir-overview-1_hxwvgx.jpg) *** -## Database Layer +## 2. Create Data Model -The Medusa application injects into each module a connection to the configured PostgreSQL database. Modules use that connection to read and write data to the database. +A data model represents a table in the database. You create data models using Medusa's Data Model Language (DML). It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations. -Modules can be implemented within [plugins](undefined/learn/fundamentals/plugins). +Learn more about data models in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules#1-create-data-model/index.html.md). -![Diagram illustrating the database layer](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175379/Medusa%20Book/db-layer_pi7tix.jpg) +You create a data model in a TypeScript or JavaScript file under the `models` directory of a module. So, to create a data model that represents a new `brand` table in the database, create the file `src/modules/brand/models/brand.ts` with the following content: -*** +![Directory structure in module after adding the brand data model](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868920/Medusa%20Book/brand-dir-overview-2_lexhdl.jpg) -## Service Integrations +```ts title="src/modules/brand/models/brand.ts" +import { model } from "@medusajs/framework/utils" -Third-party services are integrated through commerce and architectural modules. You also create custom third-party integrations through a custom module. +export const Brand = model.define("brand", { + id: model.id().primaryKey(), + name: model.text(), +}) +``` -Modules can be implemented within [plugins](undefined/learn/fundamentals/plugins). +You create a `Brand` data model which has an `id` primary key property, and a `name` text property. -### Commerce Modules +You define the data model using the `define` method of the DML. It accepts two parameters: -[Commerce modules](undefined/resources/commerce-modules) integrate third-party services relevant for commerce or user-facing features. For example, you integrate Stripe through a payment module provider. +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. -![Diagram illustrating the commerce modules integration to third-party services](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175357/Medusa%20Book/service-commerce_qcbdsl.jpg) +Learn about other property types in [this chapter](https://docs.medusajs.com/learn/fundamentals/data-models/property-types/index.html.md). -### Architectural Modules +*** -[Architectural modules](undefined/resources/architectural-modules) integrate third-party services and systems for architectural features. For example, you integrate Redis as a pub/sub service to send events, or SendGrid to send notifications. +## 3. Create Module Service -![Diagram illustrating the architectural modules integration to third-party services and systems](https://res.cloudinary.com/dza7lstvk/image/upload/v1727175342/Medusa%20Book/service-arch_ozvryw.jpg) +You perform database operations on your data models in a service, which is a class exported by the module and acts like an interface to its functionalities. -*** +In this step, you'll create the Brand Module's service that provides methods to manage the `Brand` data model. In the next chapters, you'll use this service when exposing custom features that involve managing brands. -## Full Diagram of Medusa's Architecture +Learn more about services in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules#2-create-service/index.html.md). -The following diagram illustrates Medusa's architecture over the three layers. +You define a service in a `service.ts` or `service.js` file at the root of your module's directory. So, create the file `src/modules/brand/service.ts` with the following content: -![Full diagram illustrating Medusa's architecture](https://res.cloudinary.com/dza7lstvk/image/upload/v1727174897/Medusa%20Book/architectural-diagram-full.jpg) +![Directory structure in module after adding the service](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868984/Medusa%20Book/brand-dir-overview-3_jo7baj.jpg) +```ts title="src/modules/brand/service.ts" highlights={serviceHighlights} +import { MedusaService } from "@medusajs/framework/utils" +import { Brand } from "./models/brand" -# Next.js Starter Storefront +class BrandModuleService extends MedusaService({ + Brand, +}) { -The Medusa application is made up of a Node.js server and an admin dashboard. The storefront is installed 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. +} -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. +export default BrandModuleService +``` -In this chapter, you’ll learn how to install the Next.js Starter storefront separately from the Medusa application. You can also install it while installing the Medusa application as explained in [the installation chapter](undefined/learn/installation). +The `BrandModuleService` extends a class returned by `MedusaService` from the Modules SDK. This function generates a class with data-management methods for your module's data models. -## Install Next.js Starter +The `MedusaService` function receives an object of the module's data models as a parameter, and generates methods to manage those data models. So, the `BrandModuleService` now has methods like `createBrands` and `retrieveBrand` to manage the `Brand` data model. -### Prerequisites +You'll use these methods in the [next chapter](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md). -- [Node.js v20+](https://nodejs.org/en/download) -- [Git CLI tool](https://git-scm.com/downloads) +Find a reference of all generated methods in [this guide](https://docs.medusajs.com/resources/service-factory-reference/index.html.md). -If you already have a Medusa application installed with at least one region, you can install the Next.js Starter storefront with the following steps: +*** -1. Clone the [Next.js Starter](https://github.com/medusajs/nextjs-starter-medusa): +## 4. Export Module Definition -```bash -git clone https://github.com/medusajs/nextjs-starter-medusa my-medusa-storefront -``` +A module must export a definition that tells Medusa the name of the module and its main service. This definition is exported in an `index.ts` file at the module's root directory. -2. Change to the `my-medusa-storefront` directory, install the dependencies, and rename the template environment variable file: +So, to export the Brand Module's definition, create the file `src/modules/brand/index.ts` with the following content: -```bash npm2yarn -cd my-medusa-storefront -npm install -mv .env.template .env.local -``` +![Directory structure in module after adding the definition file](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869045/Medusa%20Book/brand-dir-overview-4_nf8ymw.jpg) -3. Set the Medusa application's publishable API key in the `NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY` environment variable. You can retrieve the publishable API key in on the Medusa Admin dashboard by going to Settings -> Publishable API Keys +```ts title="src/modules/brand/index.ts" +import { Module } from "@medusajs/framework/utils" +import BrandModuleService from "./service" -```bash -NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY=pk_123... +export const BRAND_MODULE = "brand" + +export default Module(BRAND_MODULE, { + service: BrandModuleService, +}) ``` -4. While the Medusa application is running, start the Next.js Starter storefront: +You use `Module` from the Modules SDK to create the module's definition. It accepts two parameters: -```bash npm2yarn -npm run dev +1. The module's name (`brand`). You'll use this name when you use this module in other customizations. +2. An object with a required property `service` indicating the module's main service. + +You export `BRAND_MODULE` to reference the module's name more reliably in other customizations. + +*** + +## 5. Add Module to Medusa's Configurations + +To start using your module, you must add it to Medusa's configurations in `medusa-config.ts`. + +The object passed to `defineConfig` in `medusa-config.ts` accepts a `modules` property, whose value is an array of modules to add to the application. So, add the following in `medusa-config.ts`: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/brand", + }, + ], +}) ``` -Your Next.js Starter storefront is now running at `http://localhost:8000`. +The Brand Module is now added to your Medusa application. You'll start using it in the [next chapter](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md). *** -## Customize Storefront +## 6. Generate and Run Migrations -To customize the storefront, refer to the following directories: +A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations ensure that your module is re-usable and removes friction when working in a team, making it easy to reflect changes across team members' databases. -- `src/app`: The storefront’s pages. -- `src/modules`: The storefront’s components. -- `src/styles`: The storefront’s styles. +Learn more about migrations in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules#5-generate-migrations/index.html.md). -You can learn more about development with Next.js through [their documentation](https://nextjs.org/docs/getting-started). +[Medusa's CLI tool](https://docs.medusajs.com/resources/medusa-cli/index.html.md) allows you to generate migration files for your module, then run those migrations to reflect the changes in the database. So, run the following commands in your Medusa application's directory: + +```bash +npx medusa db:generate brand +npx medusa db:migrate +``` + +The `db:generate` command accepts as an argument the name of the module to generate the migrations for, and the `db:migrate` command runs all migrations that haven't been run yet in the Medusa application. *** -## Configurations and Integrations +## Next Step: Create Brand Workflow -The Next.js Starter is compatible with some Medusa integrations out-of-the-box, such as the Stripe provider module. You can also change some of its configurations if necessary. +The Brand Module now creates a `brand` table in the database and provides a class to manage its records. -Refer to the [Next.js Starter reference](undefined/resources/nextjs-starter) for more details. +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: Add Product's Brand Widget in Admin +# Create Brands UI Route in Admin -In this chapter, you'll customize the product details page of the Medusa Admin dashboard to show the product's [brand](undefined/learn/customization/custom-features/module). 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. +In this chapter, you'll add a UI route to the admin dashboard that shows all [brands](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) in a new page. You'll retrieve the brands from the server and display them in a table with pagination. ### Prerequisites -- [Brands linked to products](undefined/learn/customization/extend-features/define-link) +- [Brands Module](https://docs.medusajs.com/learn/customization/custom-features/modules/index.html.md) -## 1. Initialize JS SDK +## 1. Get Brands API Route -In your custom widget, you'll retrieve the product's brand by sending a request to the Medusa server. Medusa has a [JS SDK](undefined/resources/js-sdk) that simplifies sending requests to the server's API routes. +In a [previous chapter](https://docs.medusajs.com/learn/customization/extend-features/query-linked-records/index.html.md), you learned how to add an API route that retrieves brands and their products using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll expand that API route to support pagination, so that on the admin dashboard you can show the brands in a paginated table. -So, you'll start by configuring the JS SDK. Create the file `src/admin/lib/sdk.ts` with the following content: +Replace or create the `GET` API route at `src/api/admin/brands/route.ts` with the following: -![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) +```ts title="src/api/admin/brands/route.ts" highlights={apiRouteHighlights} +// other imports... +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" -```ts title="src/admin/lib/sdk.ts" -import Medusa from "@medusajs/js-sdk" +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { + const query = req.scope.resolve("query") + + const { + data: brands, + metadata: { count, take, skip } = {}, + } = await query.graph({ + entity: "brand", + ...req.queryConfig, + }) -export const sdk = new Medusa({ - baseUrl: import.meta.env.VITE_BACKEND_URL || "/", - debug: import.meta.env.DEV, - auth: { - type: "session", - }, -}) + res.json({ + brands, + count, + limit: take, + offset: skip, + }) +} ``` -You initialize the SDK passing it the following options: +In the API route, you use Query's `graph` method to retrieve the brands. In the method's object parameter, you spread the `queryConfig` property of the request object. This property holds configurations for pagination and retrieved fields. -- `baseUrl`: The URL to the Medusa server. -- `debug`: Whether to enable logging debug messages. This should only be enabled in development. -- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. +The query configurations are combined from default configurations, which you'll add next, and the request's query parameters: -Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](undefined/learn/fundamentals/admin/environment-variables). +- `fields`: The fields to retrieve in the brands. +- `limit`: The maximum number of items to retrieve. +- `offset`: The number of items to skip before retrieving the returned items. -You can now use the SDK to send requests to the Medusa server. +When you pass pagination configurations to the `graph` method, the returned object has the pagination's details in a `metadata` property, whose value is an object having the following properties: -Learn more about the JS SDK and its options in [this reference](undefined/resources/js-sdk). +- `count`: The total count of items. +- `take`: The maximum number of items returned in the `data` array. +- `skip`: The number of items skipped before retrieving the returned items. -*** +You return in the response the retrieved brands and the pagination configurations. -## 2. Add Widget to Product Details Page +Learn more about pagination with Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#apply-pagination/index.html.md). -You'll now add a widget to the product-details page. A widget is a React component that's injected into pre-defined zones in the Medusa Admin dashboard. It's created in a `.tsx` file under the `src/admin/widgets` directory. +*** -Learn more about widgets in [this documentation](undefined/learn/fundamentals/admin/widgets). +## 2. Add Default Query Configurations -To create a widget that shows a product's brand in its details page, create the file `src/admin/widgets/product-brand.tsx` with the following content: +Next, you'll set the default query configurations of the above API route and allow passing query parameters to change the configurations. -![Directory structure of the Medusa application after adding the widget](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414684/Medusa%20Book/brands-admin-dir-overview-2_eq5xhi.jpg) - -```tsx title="src/admin/widgets/product-brand.tsx" highlights={highlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { DetailWidgetProps, AdminProduct } from "@medusajs/framework/types" -import { clx, Container, Heading, Text } from "@medusajs/ui" -import { useQuery } from "@tanstack/react-query" -import { sdk } from "../lib/sdk" - -type AdminProductBrand = AdminProduct & { - brand?: { - id: string - name: string - } -} - -const ProductBrandWidget = ({ - data: product, -}: DetailWidgetProps) => { - const { data: queryResult } = useQuery({ - queryFn: () => sdk.admin.product.retrieve(product.id, { - fields: "+brand.*", - }), - queryKey: [["product", product.id]], - }) - const brandName = (queryResult?.product as AdminProductBrand)?.brand?.name - - return ( - -
-
- Brand -
-
-
- - Name - - - - {brandName || "-"} - -
-
- ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductBrandWidget -``` - -A widget's file must export: - -- A React component to be rendered in the specified injection zone. The component must be the file's default export. -- A configuration object created with `defineWidgetConfig` from the Admin Extension SDK. The function receives an object as a parameter that has a `zone` property, whose value is the zone to inject the widget to. - -Since the widget is injected at the top of the product details page, the widget receives the product's details as a parameter. - -In the widget, you use [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. In the `queryFn` function that executes the query, you use the JS SDK to send a request to the [Get Product API Route](undefined/api/admin#products_getproductsid), passing `+brand.*` in the `fields` query parameter to retrieve the product's brand. - -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. - -You then render a section that shows the brand's name. In admin customizations, use components from the [Medusa UI package](undefined/ui) to maintain a consistent user interface and design in the dashboard. - -*** - -## Test it Out - -To test out your widget, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, open the page of a product that has a brand. You'll see a new section at the top showing the brand's name. - -![The widget is added as the first section of the product details page.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414415/Medusa%20Book/Screenshot_2024-12-05_at_5.59.25_PM_y85m14.png) - -*** - -## Admin Components Guides - -When building your widget, you may need more complicated components. For example, you may add a form to the above widget to set the product's brand. - -The [Admin Components guides](undefined/resources/admin-components) show you how to build and use common components in the Medusa Admin, such as forms, tables, JSON data viewer, and more. The components in the guides also follow the Medusa Admin's design convention. - -*** - -## Next Chapter: Add UI Route for Brands - -In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users. - - -# Create Brands UI Route in Admin - -In this chapter, you'll add a UI route to the admin dashboard that shows all [brands](undefined/learn/customization/custom-features/module) in a new page. You'll retrieve the brands from the server and display them in a table with pagination. - -### Prerequisites - -- [Brands Module](undefined/learn/customization/custom-features/modules) - -## 1. Get Brands API Route - -In a [previous chapter](undefined/learn/customization/extend-features/query-linked-records), you learned how to add an API route that retrieves brands and their products using [Query](undefined/learn/fundamentals/module-links/query). You'll expand that API route to support pagination, so that on the admin dashboard you can show the brands in a paginated table. - -Replace or create the `GET` API route at `src/api/admin/brands/route.ts` with the following: - -```ts title="src/api/admin/brands/route.ts" highlights={apiRouteHighlights} -// 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, - metadata: { count, take, skip } = {}, - } = await query.graph({ - entity: "brand", - ...req.queryConfig, - }) - - res.json({ - brands, - count, - limit: take, - offset: skip, - }) -} -``` - -In the API route, you use Query's `graph` method to retrieve the brands. In the method's object parameter, you spread the `queryConfig` property of the request object. This property holds configurations for pagination and retrieved fields. - -The query configurations are combined from default configurations, which you'll add next, and the request's query parameters: - -- `fields`: The fields to retrieve in the brands. -- `limit`: The maximum number of items to retrieve. -- `offset`: The number of items to skip before retrieving the returned items. - -When you pass pagination configurations to the `graph` method, the returned object has the pagination's details in a `metadata` property, whose value is an object having the following properties: - -- `count`: The total count of items. -- `take`: The maximum number of items returned in the `data` array. -- `skip`: The number of items skipped before retrieving the returned items. - -You return in the response the retrieved brands and the pagination configurations. - -Learn more about pagination with Query in [this chapter](undefined/learn/fundamentals/module-links/query#apply-pagination). - -*** - -## 2. Add Default Query Configurations - -Next, you'll set the default query configurations of the above API route and allow passing query parameters to change the configurations. - -Medusa provides a `validateAndTransformQuery` middleware that validates the accepted query parameters for a request and sets the default Query configuration. So, in `src/api/middlewares.ts`, add a new middleware configuration object: +Medusa provides a `validateAndTransformQuery` middleware that validates the accepted query parameters for a request and sets the default Query configuration. So, in `src/api/middlewares.ts`, add a new middleware configuration object: ```ts title="src/api/middlewares.ts" import { @@ -2949,22 +3159,22 @@ You apply the `validateAndTransformQuery` middleware on the `GET /admin/brands` - `fields`: A comma-separated string indicating the fields to retrieve. - `limit`: The maximum number of items to retrieve. - `offset`: The number of items to skip before retrieving the returned items. - - `order`: The name of the field to sort the items by. Learn more about sorting in [the API reference](undefined/api/admin#sort-order) + - `order`: The name of the field to sort the items by. Learn more about sorting in [the API reference](https://docs.medusajs.com/api/admin#sort-order) - An object of Query configurations having the following properties: - `defaults`: An array of default fields and relations to retrieve. - `isList`: Whether the API route returns a list of items. By applying the above middleware, you can pass pagination configurations to `GET /admin/brands`, which will return a paginated list of brands. You'll see how it works when you create the UI route. -Learn more about using the `validateAndTransformQuery` middleware to configure Query in [this chapter](undefined/learn/fundamentals/module-links/query#request-query-configurations). +Learn more about using the `validateAndTransformQuery` middleware to configure Query in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query#request-query-configurations/index.html.md). *** ## 3. Initialize JS SDK -In your custom UI route, you'll retrieve the brands by sending a request to the Medusa server. Medusa has a [JS SDK](undefined/resources/js-sdk) that simplifies sending requests to the core API route. +In your custom UI route, you'll retrieve the brands by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the core API route. -If you didn't follow the [previous chapter](undefined/learn/customization/customize-admin/widget), create the file `src/admin/lib/sdk.ts` with the following content: +If you didn't follow the [previous chapter](https://docs.medusajs.com/learn/customization/customize-admin/widget/index.html.md), create the file `src/admin/lib/sdk.ts` with the following content: ![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) @@ -2986,11 +3196,11 @@ You initialize the SDK passing it the following options: - `debug`: Whether to enable logging debug messages. This should only be enabled in development. - `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. -Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](undefined/learn/fundamentals/admin/environment-variables). +Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). You can now use the SDK to send requests to the Medusa server. -Learn more about the JS SDK and its options in [this reference](undefined/resources/js-sdk). +Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). *** @@ -2998,7 +3208,7 @@ Learn more about the JS SDK and its options in [this reference](undefined/resour You'll now add the UI route that shows the paginated list of brands. A UI route is a React component created in a `page.tsx` file under a sub-directory of `src/admin/routes`. The file's path relative to src/admin/routes determines its path in the dashboard. -Learn more about UI routes in [this chapter](undefined/learn/fundamentals/admin/ui-routes). +Learn more about UI routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/ui-routes/index.html.md). So, to add the UI route at the `localhost:9000/app/brands` path, create the file `src/admin/routes/brands/page.tsx` with the following content: @@ -3034,7 +3244,7 @@ export default BrandsPage A route's file must export the React component that will be rendered in the new page. It must be the default export of the file. You can also export configurations that add a link in the sidebar for the UI route. You create these configurations using `defineRouteConfig` from the Admin Extension SDK. -So far, you only show a container. In admin customizations, use components from the [Medusa UI package](undefined/ui) to maintain a consistent user interface and design in the dashboard. +So far, you only show a container. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. ### Retrieve Brands From API Route @@ -3057,7 +3267,7 @@ type BrandsResponse = { You define the type for a brand, and the type of expected response from the `GET /admin/brands` API route. -To display the brands, you'll use Medusa UI's [DataTable](undefined/ui/components/data-table) component. So, add the following imports in `src/admin/routes/brands/page.tsx`: +To display the brands, you'll use Medusa UI's [DataTable](https://docs.medusajs.com/ui/components/data-table/index.html.md) component. So, add the following imports in `src/admin/routes/brands/page.tsx`: ```tsx title="src/admin/routes/brands/page.tsx" import { @@ -3210,525 +3420,793 @@ 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: 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. - -A module is a reusable package of functionalities related to a single domain or integration. Medusa comes with multiple pre-built modules for core commerce needs, such as the [Cart Module](undefined/resources/commerce-modules/cart) that holds the data models and business logic for cart operations. - -In a module, you create data models and business logic to manage them. In the next chapters, you'll see how you use the module to build commerce features. - -Learn more about modules in [this chapter](undefined/learn/fundamentals/modules). - -## 1. Create Module Directory - -Modules are created in a sub-directory of `src/modules`. So, start by creating the directory `src/modules/brand` that will hold the Brand Module's files. +# Guide: Add Product's Brand Widget in Admin -![Directory structure in Medusa project after adding the brand directory](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868844/Medusa%20Book/brand-dir-overview-1_hxwvgx.jpg) +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. -*** +### Prerequisites -## 2. Create Data Model +- [Brands linked to products](https://docs.medusajs.com/learn/customization/extend-features/define-link/index.html.md) -A data model represents a table in the database. You create data models using Medusa's Data Model Language (DML). It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations. +## 1. Initialize JS SDK -Learn more about data models in [this chapter](undefined/learn/fundamentals/modules#1-create-data-model). +In your custom widget, you'll retrieve the product's brand by sending a request to the Medusa server. Medusa has a [JS SDK](https://docs.medusajs.com/resources/js-sdk/index.html.md) that simplifies sending requests to the server's API routes. -You create a data model in a TypeScript or JavaScript file under the `models` directory of a module. So, to create a data model that represents a new `brand` table in the database, create the file `src/modules/brand/models/brand.ts` with the following content: +So, you'll start by configuring the JS SDK. Create the file `src/admin/lib/sdk.ts` with the following content: -![Directory structure in module after adding the brand data model](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868920/Medusa%20Book/brand-dir-overview-2_lexhdl.jpg) +![The directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414606/Medusa%20Book/brands-admin-dir-overview-1_jleg0t.jpg) -```ts title="src/modules/brand/models/brand.ts" -import { model } from "@medusajs/framework/utils" +```ts title="src/admin/lib/sdk.ts" +import Medusa from "@medusajs/js-sdk" -export const Brand = model.define("brand", { - id: model.id().primaryKey(), - name: model.text(), +export const sdk = new Medusa({ + baseUrl: import.meta.env.VITE_BACKEND_URL || "/", + debug: import.meta.env.DEV, + auth: { + type: "session", + }, }) ``` -You create a `Brand` data model which has an `id` primary key property, and a `name` text property. +You initialize the SDK passing it the following options: -You define the data model using the `define` method of the DML. It accepts two parameters: +- `baseUrl`: The URL to the Medusa server. +- `debug`: Whether to enable logging debug messages. This should only be enabled in development. +- `auth.type`: The authentication method used in the client application, which is `session` in the Medusa Admin dashboard. -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. +Notice that you use `import.meta.env` to access environment variables in your customizations because the Medusa Admin is built on top of Vite. Learn more in [this chapter](https://docs.medusajs.com/learn/fundamentals/admin/environment-variables/index.html.md). + +You can now use the SDK to send requests to the Medusa server. -Learn about other property types in [this chapter](undefined/learn/fundamentals/data-models/property-types). +Learn more about the JS SDK and its options in [this reference](https://docs.medusajs.com/resources/js-sdk/index.html.md). *** -## 3. Create Module Service +## 2. Add Widget to Product Details Page -You perform database operations on your data models in a service, which is a class exported by the module and acts like an interface to its functionalities. +You'll now add a widget to the product-details page. A widget is a React component that's injected into pre-defined zones in the Medusa Admin dashboard. It's created in a `.tsx` file under the `src/admin/widgets` directory. -In this step, you'll create the Brand Module's service that provides methods to manage the `Brand` data model. In the next chapters, you'll use this service when exposing custom features that involve managing brands. +Learn more about widgets in [this documentation](https://docs.medusajs.com/learn/fundamentals/admin/widgets/index.html.md). -Learn more about services in [this chapter](undefined/learn/fundamentals/modules#2-create-service). +To create a widget that shows a product's brand in its details page, create the file `src/admin/widgets/product-brand.tsx` with the following content: -You define a service in a `service.ts` or `service.js` file at the root of your module's directory. So, create the file `src/modules/brand/service.ts` with the following content: +![Directory structure of the Medusa application after adding the widget](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414684/Medusa%20Book/brands-admin-dir-overview-2_eq5xhi.jpg) -![Directory structure in module after adding the service](https://res.cloudinary.com/dza7lstvk/image/upload/v1732868984/Medusa%20Book/brand-dir-overview-3_jo7baj.jpg) +```tsx title="src/admin/widgets/product-brand.tsx" highlights={highlights} +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { DetailWidgetProps, AdminProduct } from "@medusajs/framework/types" +import { clx, Container, Heading, Text } from "@medusajs/ui" +import { useQuery } from "@tanstack/react-query" +import { sdk } from "../lib/sdk" -```ts title="src/modules/brand/service.ts" highlights={serviceHighlights} -import { MedusaService } from "@medusajs/framework/utils" -import { Brand } from "./models/brand" +type AdminProductBrand = AdminProduct & { + brand?: { + id: string + name: string + } +} -class BrandModuleService extends MedusaService({ - Brand, -}) { +const ProductBrandWidget = ({ + data: product, +}: DetailWidgetProps) => { + const { data: queryResult } = useQuery({ + queryFn: () => sdk.admin.product.retrieve(product.id, { + fields: "+brand.*", + }), + queryKey: [["product", product.id]], + }) + const brandName = (queryResult?.product as AdminProductBrand)?.brand?.name + + return ( + +
+
+ Brand +
+
+
+ + Name + + + {brandName || "-"} + +
+
+ ) } -export default BrandModuleService +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductBrandWidget ``` -The `BrandModuleService` extends a class returned by `MedusaService` from the Modules SDK. This function generates a class with data-management methods for your module's data models. +A widget's file must export: -The `MedusaService` function receives an object of the module's data models as a parameter, and generates methods to manage those data models. So, the `BrandModuleService` now has methods like `createBrands` and `retrieveBrand` to manage the `Brand` data model. +- A React component to be rendered in the specified injection zone. The component must be the file's default export. +- A configuration object created with `defineWidgetConfig` from the Admin Extension SDK. The function receives an object as a parameter that has a `zone` property, whose value is the zone to inject the widget to. -You'll use these methods in the [next chapter](undefined/learn/customization/custom-features/workflow). +Since the widget is injected at the top of the product details page, the widget receives the product's details as a parameter. -Find a reference of all generated methods in [this guide](undefined/resources/service-factory-reference). +In the widget, you use [Tanstack (React) Query](https://tanstack.com/query/latest) to query the Medusa server. Tanstack Query provides features like asynchronous state management and optimized caching. In the `queryFn` function that executes the query, you use the JS SDK to send a request to the [Get Product API Route](https://docs.medusajs.com/api/admin#products_getproductsid), passing `+brand.*` in the `fields` query parameter to retrieve the product's brand. -*** +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. -## 4. Export Module Definition +You then render a section that shows the brand's name. In admin customizations, use components from the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md) to maintain a consistent user interface and design in the dashboard. -A module must export a definition that tells Medusa the name of the module and its main service. This definition is exported in an `index.ts` file at the module's root directory. +*** -So, to export the Brand Module's definition, create the file `src/modules/brand/index.ts` with the following content: +## Test it Out -![Directory structure in module after adding the definition file](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869045/Medusa%20Book/brand-dir-overview-4_nf8ymw.jpg) +To test out your widget, start the Medusa application: -```ts title="src/modules/brand/index.ts" -import { Module } from "@medusajs/framework/utils" -import BrandModuleService from "./service" +```bash npm2yarn +npm run dev +``` -export const BRAND_MODULE = "brand" +Then, open the admin dashboard at `http://localhost:9000/app`. After you log in, open the page of a product that has a brand. You'll see a new section at the top showing the brand's name. -export default Module(BRAND_MODULE, { - service: BrandModuleService, -}) -``` +![The widget is added as the first section of the product details page.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733414415/Medusa%20Book/Screenshot_2024-12-05_at_5.59.25_PM_y85m14.png) -You use `Module` from the Modules SDK to create the module's definition. It accepts two parameters: +*** -1. The module's name (`brand`). You'll use this name when you use this module in other customizations. -2. An object with a required property `service` indicating the module's main service. +## Admin Components Guides -You export `BRAND_MODULE` to reference the module's name more reliably in other customizations. +When building your widget, you may need more complicated components. For example, you may add a form to the above widget to set the product's brand. + +The [Admin Components guides](https://docs.medusajs.com/resources/admin-components/index.html.md) show you how to build and use common components in the Medusa Admin, such as forms, tables, JSON data viewer, and more. The components in the guides also follow the Medusa Admin's design convention. *** -## 5. Add Module to Medusa's Configurations +## Next Chapter: Add UI Route for Brands -To start using your module, you must add it to Medusa's configurations in `medusa-config.ts`. +In the next chapter, you'll add a UI route that displays the list of brands in your application and allows admin users. -The object passed to `defineConfig` in `medusa-config.ts` accepts a `modules` property, whose value is an array of modules to add to the application. So, add the following in `medusa-config.ts`: -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "./src/modules/brand", - }, - ], -}) -``` +# Guide: Create Brand Workflow -The Brand Module is now added to your Medusa application. You'll start using it in the [next chapter](undefined/learn/customization/custom-features/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. -## 6. Generate and Run Migrations +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. -A migration is a TypeScript or JavaScript file that defines database changes made by a module. Migrations ensure that your module is re-usable and removes friction when working in a team, making it easy to reflect changes across team members' databases. +Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). -Learn more about migrations in [this chapter](undefined/learn/fundamentals/modules#5-generate-migrations). +### Prerequisites -[Medusa's CLI tool](undefined/resources/medusa-cli) allows you to generate migration files for your module, then run those migrations to reflect the changes in the database. So, run the following commands in your Medusa application's directory: +- [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) -```bash -npx medusa db:generate brand -npx medusa db:migrate -``` +*** -The `db:generate` command accepts as an argument the name of the module to generate the migrations for, and the `db:migrate` command runs all migrations that haven't been run yet in the Medusa application. +## 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 -## Next Step: Create Brand Workflow +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 Brand Module now creates a `brand` table in the database and provides a class to manage its records. +![Directory structure in the Medusa project after adding the file for createBrandStep](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869184/Medusa%20Book/brand-workflow-dir-overview-1_fjvf5j.jpg) -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. +```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 +} -# Guide: Create Brand API Route +export const createBrandStep = createStep( + "create-brand-step", + async (input: CreateBrandStepInput, { container }) => { + const brandModuleService: BrandModuleService = container.resolve( + BRAND_MODULE + ) -In the previous two chapters, you created a [Brand Module](undefined/learn/customization/custom-features/module) that added the concepts of brands to your application, then created a [workflow to create a brand](undefined/learn/customization/custom-features/workflow). In this chapter, you'll expose an API route that allows admin users to create a brand using the workflow from the previous chapter. + const brand = await brandModuleService.createBrands(input) -An API Route is an endpoint that acts as an entry point for other clients to interact with your Medusa customizations, such as the admin dashboard, storefronts, or third-party systems. + return new StepResponse(brand, brand.id) + } +) +``` -The Medusa core application provides a set of [admin](undefined/api/admin) and [store](undefined/api/store) API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities. +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. -### Prerequisites +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. -- [createBrandWorkflow](undefined/learn/customization/custom-features/workflow) +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. -## 1. Create the API Route +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. -You create an API route in a `route.{ts,js}` file under a sub-directory of the `src/api` directory. The file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…). +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 API routes [in this guide](undefined/learn/fundamentals/api-routes). +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. -The route's path is the path of `route.{ts,js}` relative to `src/api`. So, to create the API route at `/admin/brands`, create the file `src/api/admin/brands/route.ts` with the following content: +### Add Compensation Function to Step -![Directory structure of the Medusa application after adding the route](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869882/Medusa%20Book/brand-route-dir-overview-2_hjqlnf.jpg) +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. -```ts title="src/api/admin/brands/route.ts" -import { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { - createBrandWorkflow, -} from "../../../workflows/create-brand" +Learn more about the compensation function in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md). -type PostAdminCreateBrandType = { - name: string -} +To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`: -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - const { result } = await createBrandWorkflow(req.scope) - .run({ - input: req.validatedBody, - }) +```ts title="src/workflows/create-brand.ts" +export const createBrandStep = createStep( + // ... + async (id: string, { container }) => { + const brandModuleService: BrandModuleService = container.resolve( + BRAND_MODULE + ) - res.json({ brand: result }) -} + await brandModuleService.deleteBrands(id) + } +) ``` -You export a route handler function with its name (`POST`) being the HTTP method of the API route you're exposing. - -The function receives two parameters: a `MedusaRequest` object to access request details, and `MedusaResponse` object to return or manipulate the response. The `MedusaRequest` object's `scope` property is the [Medusa container](undefined/learn/fundamentals/medusa-container) that holds framework tools and custom and core modules' services. +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. -`MedusaRequest` accepts the request body's type as a type argument. +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. -In the API route's handler, you execute the `createBrandWorkflow` by invoking it and passing the Medusa container `req.scope` as a parameter, then invoking its `run` method. You pass the workflow's input in the `input` property of the `run` method's parameter. You pass the request body's parameters using the `validatedBody` property of `MedusaRequest`. +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). -You return a JSON response with the created brand using the `res.json` method. +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 Validation Schema +## 2. Create createBrandWorkflow -The API route you created accepts the brand's name in the request body. So, you'll create a schema used to validate incoming request body parameters. +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. -Medusa uses [Zod](https://zod.dev/) to create validation schemas. These schemas are then used to validate incoming request bodies or query parameters. +Add the following content in the same `src/workflows/create-brand.ts` file: -Learn more about API route validation in [this chapter](undefined/learn/fundamentals/api-routes/validation). +```ts title="src/workflows/create-brand.ts" +// other imports... +import { + // ... + createWorkflow, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" -You create a validation schema in a TypeScript or JavaScript file under a sub-directory of the `src/api` directory. So, create the file `src/api/admin/brands/validators.ts` with the following content: +// ... -![Directory structure of Medusa application after adding validators file](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869806/Medusa%20Book/brand-route-dir-overview-1_yfyjss.jpg) +type CreateBrandWorkflowInput = { + name: string +} -```ts title="src/api/admin/brands/validators.ts" -import { z } from "zod" +export const createBrandWorkflow = createWorkflow( + "create-brand", + (input: CreateBrandWorkflowInput) => { + const brand = createBrandStep(input) -export const PostAdminCreateBrand = z.object({ - name: z.string(), -}) + return new WorkflowResponse(brand) + } +) ``` -You export a validation schema that expects in the request body an object having a `name` property whose value is a string. - -You can then replace `PostAdminCreateBrandType` in `src/api/admin/brands/route.ts` with the following: - -```ts title="src/api/admin/brands/route.ts" -// ... -import { z } from "zod" -import { PostAdminCreateBrand } from "./validators" +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. -type PostAdminCreateBrandType = z.infer +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. *** -## 3. Add Validation Middleware +## Next Steps: Expose Create Brand API Route -A middleware is a function executed before the route handler when a request is sent to an API Route. It's useful to guard API routes, parse custom request body types, and apply validation on an API route. +You now have a `createBrandWorkflow` that you can execute to create a brand. -Learn more about middlewares in [this chapter](undefined/learn/fundamentals/api-routes/middlewares). +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. -Medusa provides a `validateAndTransformBody` middleware that accepts a Zod validation schema and returns a response error if a request is sent with body parameters that don't satisfy the validation schema. -Middlewares are defined in the special file `src/api/middlewares.ts`. So, to add the validation middleware on the API route you created in the previous step, create the file `src/api/middlewares.ts` with the following content: +# Guide: Define Module Link Between Brand and Product -![Directory structure of the Medusa application after adding the middleware](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869977/Medusa%20Book/brand-route-dir-overview-3_kcx511.jpg) +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. -```ts title="src/api/middlewares.ts" -import { - defineMiddlewares, - validateAndTransformBody, -} from "@medusajs/framework/http" -import { PostAdminCreateBrand } from "./admin/brands/validators" +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. -export default defineMiddlewares({ - routes: [ - { - matcher: "/admin/brands", - method: "POST", - middlewares: [ - validateAndTransformBody(PostAdminCreateBrand), - ], - }, - ], -}) -``` +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. -You define the middlewares using the `defineMiddlewares` function and export its returned value. The function accepts an object having a `routes` property, which is an array of middleware objects. +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. -In the middleware object, you define three properties: +Learn more about module links in [this chapters](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md). -- `matcher`: a string or regular expression indicating the API route path to apply the middleware on. You pass the create brand's route `/admin/brand`. -- `method`: The HTTP method to restrict the middleware to, which is `POST`. -- `middlewares`: An array of middlewares to apply on the route. You pass the `validateAndTransformBody` middleware, passing it the Zod schema you created earlier. +### Prerequisites -The Medusa application will now validate the body parameters of `POST` requests sent to `/admin/brands` to ensure they match the Zod validation schema. If not, an error is returned in the response specifying the issues to fix in the request body. +- [Brand Module having a Brand data model](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) -*** +## 1. Define Link -## Test API Route +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. -To test out the API route, start the Medusa application with the following command: +So, to define a link between the `Product` and `Brand` models, create the file `src/links/product-brand.ts` with the following content: -```bash npm2yarn -npm run dev +![The directory structure of the Medusa application after adding the link.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733329897/Medusa%20Book/brands-link-dir-overview_t1rhlp.jpg) + +```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 +) ``` -Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. +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. -So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: +The `defineLink` function accepts two parameters of the same type, which is either: -```bash -curl -X POST 'http://localhost:9000/auth/user/emailpass' \ --H 'Content-Type: application/json' \ ---data-raw '{ - "email": "admin@medusa-test.com", - "password": "supersecret" -}' -``` +- 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. -Make sure to replace the email and password with your admin user's credentials. +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. -Don't have an admin user? Refer to [this guide](undefined/learn/installation#create-medusa-admin-user). +*** -Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: +## 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 -curl -X POST 'http://localhost:9000/admin/brands' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "name": "Acme" -}' +npx medusa db:migrate ``` -This returns the created brand in the response: +This command reflects migrations on the database and syncs module links, which creates a table for the `product-brand` link. -```json title="Example Response" -{ - "brand": { - "id": "01J7AX9ES4X113HKY6C681KDZJ", - "name": "Acme", - "created_at": "2024-09-09T08:09:34.244Z", - "updated_at": "2024-09-09T08:09:34.244Z" +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: Sync Brands from Medusa to CMS + +In the [previous chapter](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md), you created a CMS Module that integrates a dummy third-party system. You can now perform actions using that module within your custom flows. + +In another previous chapter, you [added a workflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) that creates a brand. After integrating the CMS, you want to sync that brand to the third-party system as well. + +Medusa has an event system that emits events when an operation is performed. It allows you to listen to those events and perform an asynchronous action in a function called a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). This is useful to perform actions that aren't integral to the original flow, such as syncing data to a third-party system. + +Learn more about Medusa's event system and subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). + +In this chapter, you'll modify the `createBrandWorkflow` you created before to emit a custom event that indicates a brand was created. Then, you'll listen to that event in a subscriber to sync the brand to the third-party CMS. You'll implement the sync logic within a workflow that you execute in the subscriber. + +### Prerequisites + +- [createBrandWorkflow](https://docs.medusajs.com/learn/customization/custom-features/workflow/index.html.md) +- [CMS Module](https://docs.medusajs.com/learn/customization/integrate-systems/service/index.html.md) + +## 1. Emit Event in createBrandWorkflow + +Since syncing the brand to the third-party system isn't integral to creating a brand, you'll emit a custom event indicating that a brand was created. + +Medusa provides an `emitEventStep` that allows you to emit an event in your workflows. So, in the `createBrandWorkflow` defined in `src/workflows/create-brand.ts`, use the `emitEventStep` helper step after the `createBrandStep`: + +```ts title="src/workflows/create-brand.ts" highlights={eventHighlights} +// other imports... +import { + emitEventStep, +} from "@medusajs/medusa/core-flows" + +// ... + +export const createBrandWorkflow = createWorkflow( + "create-brand", + (input: CreateBrandInput) => { + // ... + + emitEventStep({ + eventName: "brand.created", + data: { + id: brand.id, + }, + }) + + return new WorkflowResponse(brand) } -} +) ``` +The `emitEventStep` accepts an object parameter having two properties: + +- `eventName`: The name of the event to emit. You'll use this name later to listen to the event in a subscriber. +- `data`: The data payload to emit with the event. This data is passed to subscribers that listen to the event. You add the brand's ID to the data payload, informing the subscribers which brand was created. + +You'll learn how to handle this event in a later step. + *** -## Summary +## 2. Create Sync to Third-Party System Workflow -By following the previous example chapters, you implemented a custom feature that allows admin users to create a brand. You did that by: +The subscriber that will listen to the `brand.created` event will sync the created brand to the third-party CMS. So, you'll implement the syncing logic in a workflow, then execute the workflow in the subscriber. -1. Creating a module that defines and manages a `brand` table in the database. -2. Creating a workflow that uses the module's service to create a brand record, and implements the compensation logic to delete that brand in case an error occurs. -3. Creating an API route that allows admin users to create a brand. +Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. -*** +Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). -## Next Steps: Associate Brand with Product +You'll create a `syncBrandToSystemWorkflow` that has two steps: -Now that you have brands in your Medusa application, you want to associate a brand with a product, which is defined in the [Product Module](undefined/resources/commerce-modules/product). +- `useQueryGraphStep`: a step that Medusa provides to retrieve data using [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). You'll use this to retrieve the brand's details using its ID. +- `syncBrandToCmsStep`: a step that you'll create to sync the brand to the CMS. -In the next chapters, you'll learn how to build associations between data models defined in different modules. +### syncBrandToCmsStep + +To implement the step that syncs the brand to the CMS, create the file `src/workflows/sync-brands-to-cms.ts` with the following content: +![Directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493547/Medusa%20Book/cms-dir-overview-4_u5t0ug.jpg) -# Guide: Create Brand Workflow +```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" +import { InferTypeOf } from "@medusajs/framework/types" +import { Brand } from "../modules/brand/models/brand" +import { CMS_MODULE } from "../modules/cms" +import CmsModuleService from "../modules/cms/service" -This chapter builds on the work from the [previous chapter](undefined/learn/customization/custom-features/module) where you created a Brand Module. +type SyncBrandToCmsStepInput = { + brand: InferTypeOf +} -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. +const syncBrandToCmsStep = createStep( + "sync-brand-to-cms", + async ({ brand }: SyncBrandToCmsStepInput, { container }) => { + const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) -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](undefined/learn/customization/custom-features/api-route), 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. + await cmsModuleService.createBrand(brand) -Learn more about workflows in [this chapter](undefined/learn/fundamentals/workflows). + return new StepResponse(null, brand.id) + }, + async (id, { container }) => { + if (!id) { + return + } -### Prerequisites + const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) -- [Brand Module](undefined/learn/customization/custom-features/module) + await cmsModuleService.deleteBrand(id) + } +) +``` -*** +You create the `syncBrandToCmsStep` that accepts a brand as an input. In the step, you resolve the CMS Module's service from the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) and use its `createBrand` method. This method will create the brand in the third-party CMS. -## 1. Create createBrandStep +You also pass the brand's ID to the step's compensation function. In this function, you delete the brand in the third-party CMS if an error occurs during the workflow's execution. -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 compensation functions in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/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: +### Create Workflow -![Directory structure in the Medusa project after adding the file for createBrandStep](https://res.cloudinary.com/dza7lstvk/image/upload/v1732869184/Medusa%20Book/brand-workflow-dir-overview-1_fjvf5j.jpg) +You can now create the workflow that uses the above step. Add the workflow to the same `src/workflows/sync-brands-to-cms.ts` file: -```ts title="src/workflows/create-brand.ts" -import { - createStep, - StepResponse, +```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncWorkflowHighlights} +// other imports... +import { + // ... + createWorkflow, + WorkflowResponse, } from "@medusajs/framework/workflows-sdk" -import { BRAND_MODULE } from "../modules/brand" -import BrandModuleService from "../modules/brand/service" +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" -export type CreateBrandStepInput = { - name: string +// ... + +type SyncBrandToCmsWorkflowInput = { + id: string } -export const createBrandStep = createStep( - "create-brand-step", - async (input: CreateBrandStepInput, { container }) => { - const brandModuleService: BrandModuleService = container.resolve( - BRAND_MODULE - ) +export const syncBrandToCmsWorkflow = createWorkflow( + "sync-brand-to-cms", + (input: SyncBrandToCmsWorkflowInput) => { + // @ts-ignore + const { data: brands } = useQueryGraphStep({ + entity: "brand", + fields: ["*"], + filters: { + id: input.id, + }, + options: { + throwIfKeyNotFound: true, + }, + }) - const brand = await brandModuleService.createBrands(input) + syncBrandToCmsStep({ + brand: brands[0], + } as SyncBrandToCmsStepInput) - return new StepResponse(brand, brand.id) + return new WorkflowResponse({}) } ) ``` -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. +You create a `syncBrandToCmsWorkflow` that accepts the brand's ID as input. The workflow has the following steps: -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. +- `useQueryGraphStep`: Retrieve the brand's details using Query. You pass the brand's ID as a filter, and set the `throwIfKeyNotFound` option to true so that the step throws an error if a brand with the specified ID doesn't exist. +- `syncBrandToCmsStep`: Create the brand in the third-party CMS. -The [Medusa container](undefined/learn/fundamentals/medusa-container) 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. +You'll execute this workflow in the subscriber next. -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 `useQueryGraphStep` in [this reference](https://docs.medusajs.com/resources/references/helper-steps/useQueryGraphStep/index.html.md). -Learn more about the generated `create` method's usage in [this reference](undefined/resources/service-factory-reference/methods/create). +*** -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. +## 3. Handle brand.created Event -### Add Compensation Function to Step +You now have a workflow with the logic to sync a brand to the CMS. You need to execute this workflow whenever the `brand.created` event is emitted. So, you'll create a subscriber that listens to and handle the event. -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. +Subscribers are created in a TypeScript or JavaScript file under the `src/subscribers` directory. So, create the file `src/subscribers/brand-created.ts` with the following content: -Learn more about the compensation function in [this chapter](undefined/learn/fundamentals/workflows/compensation-function). +![Directory structure of the Medusa application after adding the subscriber](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493774/Medusa%20Book/cms-dir-overview-5_iqqwvg.jpg) -To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`: +```ts title="src/subscribers/brand-created.ts" highlights={subscriberHighlights} +import type { + SubscriberConfig, + SubscriberArgs, +} from "@medusajs/framework" +import { syncBrandToCmsWorkflow } from "../workflows/sync-brands-to-cms" -```ts title="src/workflows/create-brand.ts" -export const createBrandStep = createStep( - // ... - async (id: string, { container }) => { - const brandModuleService: BrandModuleService = container.resolve( - BRAND_MODULE - ) +export default async function brandCreatedHandler({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + await syncBrandToCmsWorkflow(container).run({ + input: data, + }) +} - await brandModuleService.deleteBrands(id) - } -) +export const config: SubscriberConfig = { + event: "brand.created", +} ``` -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. +A subscriber file must export: -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. +- The asynchronous function that's executed when the event is emitted. This must be the file's default export. +- An object that holds the subscriber's configurations. It has an `event` property that indicates the name of the event that the subscriber is listening to. + +The subscriber function accepts an object parameter that has two properties: -Learn more about the generated `delete` method's usage in [this reference](undefined/resources/service-factory-reference/methods/delete). +- `event`: An object of event details. Its `data` property holds the event's data payload, which is the brand's ID. +- `container`: The Medusa container used to resolve framework and commerce tools. -So, if an error occurs during the workflow's execution, the brand that was created by the step is deleted to maintain data consistency. +In the function, you execute the `syncBrandToCmsWorkflow`, passing it the data payload as an input. So, everytime a brand is created, Medusa will execute this function, which in turn executes the workflow to sync the brand to the CMS. + +Learn more about subscribers in [this chapter](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers/index.html.md). *** -## 2. Create createBrandWorkflow +## Test it Out -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. +To test the subscriber and workflow out, you'll use the [Create Brand API route](https://docs.medusajs.com/learn/customization/custom-features/api-route/index.html.md) you created in a previous chapter. -Add the following content in the same `src/workflows/create-brand.ts` file: +First, start the Medusa application: -```ts title="src/workflows/create-brand.ts" +```bash npm2yarn +npm run dev +``` + +Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: + +```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 with your admin user's credentials. + +Don't have an admin user? Refer to [this guide](https://docs.medusajs.com/learn/installation#create-medusa-admin-user/index.html.md). + +Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: + +```bash +curl -X POST 'http://localhost:9000/admin/brands' \ +-H 'Content-Type: application/json' \ +-H 'Authorization: Bearer {token}' \ +--data '{ + "name": "Acme" +}' +``` + +This request returns the created brand. If you check the logs, you'll find the `brand.created` event was emitted, and that the request to the third-party system was simulated: + +```plain +info: Processing brand.created which has 1 subscribers +http: POST /admin/brands ← - (200) - 16.418 ms +info: Sending a POST request to /brands. +info: Request Data: { + "id": "01JEDWENYD361P664WRQPMC3J8", + "name": "Acme", + "created_at": "2024-12-06T11:42:32.909Z", + "updated_at": "2024-12-06T11:42:32.909Z", + "deleted_at": null +} +info: API Key: "123" +``` + +*** + +## Next Chapter: Sync Brand from Third-Party CMS to Medusa + +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. + + +# 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. + +*** + +## 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 { - // ... - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" + 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.*"], + }) -type CreateBrandWorkflowInput = { - name: string + res.json({ brands }) } +``` -export const createBrandWorkflow = createWorkflow( - "create-brand", - (input: CreateBrandWorkflowInput) => { - const brand = createBrandStep(input) +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: - return new WorkflowResponse(brand) - } -) +- `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}' ``` -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. +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). -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. +This returns the brands in your store with their linked products. For example: -A workflow must return an instance of `WorkflowResponse`. It accepts as a parameter the data to return to the workflow's executor. +```json title="Example Response" +{ + "brands": [ + { + "id": "123", + // ... + "products": [ + { + "id": "prod_123", + // ... + } + ] + } + ] +} +``` *** -## Next Steps: Expose Create Brand API Route +## Summary -You now have a `createBrandWorkflow` that you can execute to create a brand. +By following the examples of the previous chapters, you: -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. +- 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: Extend Create Product Flow -After linking the [custom Brand data model](undefined/learn/customization/custom-features/module) and Medusa's [Product Module](undefined/resources/commerce-modules/product) in the [previous chapter](undefined/learn/customization/extend-features/define-link), you'll extend the create product workflow and API route to allow associating a brand with a product. +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](undefined/api/admin#products_postproducts), accept an `additional_data` request body parameter. This parameter can hold custom data that's passed to the [hooks](undefined/learn/fundamentals/workflows/workflow-hooks) of the workflow executed in the API route, allowing you to consume those hooks and perform actions with the custom data. +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](undefined/resources/references/medusa-workflows/createProductsWorkflow#productsCreated) hook of the [createProductsWorkflow](undefined/resources/references/medusa-workflows/createProductsWorkflow), 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. +- 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](undefined/learn/fundamentals/api-routes/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](undefined/learn/customization/custom-features/module) -- [Defined link between the Brand and Product data models.](undefined/learn/customization/extend-features/define-link) +- [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) *** @@ -3736,9 +4214,9 @@ To learn more about the `additional_data` property and the API routes that accep 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](undefined/learn/fundamentals/workflows/workflow-hooks). +Learn more about the workflow hooks in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). -The [createProductsWorkflow](undefined/resources/references/medusa-workflows/createProductsWorkflow) used in the [Create Product API route](undefined/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. +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: @@ -3772,7 +4250,7 @@ createProductsWorkflow.hooks.productsCreated( 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](undefined/learn/fundamentals/medusa-container) to resolve framework and commerce tools. +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. @@ -3780,7 +4258,7 @@ In the step, if a brand ID is passed in `additional_data`, you resolve the Brand 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](undefined/learn/fundamentals/module-links/link). +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: @@ -3911,7 +4389,7 @@ curl -X POST 'http://localhost:9000/admin/products' \ }' ``` -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](undefined/api/admin#shipping-profiles_getshippingprofiles). +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. @@ -3924,542 +4402,64 @@ In the Medusa application's logs, you'll find the message `Linked brand to produ 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: Define Module Link Between Brand and Product +# Guide: Schedule Syncing Brands from CMS -In this chapter, you'll learn how to define a module link between a brand defined in the [custom Brand Module](undefined/learn/customization/custom-features/module), and a product defined in the [Product Module](undefined/resources/commerce-modules/product) that's available in your Medusa application out-of-the-box. +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. -Modules are [isolated](undefined/learn/fundamentals/modules/isolation) 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. +However, when you integrate a third-party system, you want the data to be in sync between the Medusa application and the system. One way to do so is by automatically syncing the data once a day. -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. +You can create an action to be automatically executed at a specified interval using scheduled jobs. A scheduled job is an asynchronous function with a specified schedule of when the Medusa application should run it. Scheduled jobs are useful to automate repeated tasks. -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 scheduled jobs in [this chapter](https://docs.medusajs.com/learn/fundamentals/scheduled-jobs/index.html.md). -Learn more about module links in [this chapters](undefined/learn/fundamentals/module-links). +In this chapter, you'll create a scheduled job that triggers syncing the brands from the third-party CMS to Medusa once a day. You'll implement the syncing logic in a workflow, and execute that workflow in the scheduled job. ### Prerequisites -- [Brand Module having a Brand data model](undefined/learn/customization/custom-features/module) +- [CMS Module](https://docs.medusajs.com/learn/customization/integrate-systems/service/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. +## 1. Implement Syncing Workflow -So, to define a link between the `Product` and `Brand` models, create the file `src/links/product-brand.ts` with the following content: +You'll start by implementing the syncing logic in a workflow, then execute the workflow later in the scheduled job. -![The directory structure of the Medusa application after adding the link.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733329897/Medusa%20Book/brands-link-dir-overview_t1rhlp.jpg) +Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. -```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" +Learn more about workflows in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/index.html.md). -export default defineLink( - { - linkable: ProductModule.linkable.product, - isList: true, - }, - BrandModule.linkable.brand -) -``` +This workflow will have three steps: -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. +1. `retrieveBrandsFromCmsStep` to retrieve the brands from the CMS. +2. `createBrandsStep` to create the brands retrieved in the first step that don't exist in Medusa. +3. `updateBrandsStep` to update the brands retrieved in the first step that exist in Medusa. -The `defineLink` function accepts two parameters of the same type, which is either: +### retrieveBrandsFromCmsStep -- 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. +To create the step that retrieves the brands from the third-party CMS, create the file `src/workflows/sync-brands-from-cms.ts` with the following content: -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. +![Directory structure of the Medusa application after creating the file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733494196/Medusa%20Book/cms-dir-overview-6_z1omsi.jpg) -*** +```ts title="src/workflows/sync-brands-from-cms.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports" +import { + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import CmsModuleService from "../modules/cms/service" +import { CMS_MODULE } from "../modules/cms" -## 2. Sync the Link to the Database +const retrieveBrandsFromCmsStep = createStep( + "retrieve-brands-from-cms", + async (_, { container }) => { + const cmsModuleService: CmsModuleService = container.resolve( + CMS_MODULE + ) -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: + const brands = await cmsModuleService.retrieveBrands() -```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: Query Product's Brands - -In the previous chapters, you [defined a link](undefined/learn/customization/extend-features/define-link) between the [custom Brand Module](undefined/learn/customization/custom-features/module) and Medusa's [Product Module](undefined/resources/commerce-modules/product), then [extended the create-product flow](undefined/learn/customization/extend-features/extend-create-product) 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](undefined/learn/customization/custom-features/module) -- [Defined link between the Brand and Product data models.](undefined/learn/customization/extend-features/define-link) - -*** - -## 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](undefined/api/admin#products_getproducts), [Get Product](undefined/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](undefined/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](undefined/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. - -*** - -## 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](undefined/learn/fundamentals/module-links/query). - -For example, you can create an API route that retrieves brands and their products. If you followed the [Create Brands API route chapter](undefined/learn/customization/custom-features/api-route), 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](undefined/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", - // ... - } - ] - } - ] -} -``` - -*** - -## 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: Sync Brands from Medusa to CMS - -In the [previous chapter](undefined/learn/customization/integrate-systems/service), you created a CMS Module that integrates a dummy third-party system. You can now perform actions using that module within your custom flows. - -In another previous chapter, you [added a workflow](undefined/learn/customization/custom-features/workflow) that creates a brand. After integrating the CMS, you want to sync that brand to the third-party system as well. - -Medusa has an event system that emits events when an operation is performed. It allows you to listen to those events and perform an asynchronous action in a function called a [subscriber](undefined/learn/fundamentals/events-and-subscribers). This is useful to perform actions that aren't integral to the original flow, such as syncing data to a third-party system. - -Learn more about Medusa's event system and subscribers in [this chapter](undefined/learn/fundamentals/events-and-subscribers). - -In this chapter, you'll modify the `createBrandWorkflow` you created before to emit a custom event that indicates a brand was created. Then, you'll listen to that event in a subscriber to sync the brand to the third-party CMS. You'll implement the sync logic within a workflow that you execute in the subscriber. - -### Prerequisites - -- [createBrandWorkflow](undefined/learn/customization/custom-features/workflow) -- [CMS Module](undefined/learn/customization/integrate-systems/service) - -## 1. Emit Event in createBrandWorkflow - -Since syncing the brand to the third-party system isn't integral to creating a brand, you'll emit a custom event indicating that a brand was created. - -Medusa provides an `emitEventStep` that allows you to emit an event in your workflows. So, in the `createBrandWorkflow` defined in `src/workflows/create-brand.ts`, use the `emitEventStep` helper step after the `createBrandStep`: - -```ts title="src/workflows/create-brand.ts" highlights={eventHighlights} -// other imports... -import { - emitEventStep, -} from "@medusajs/medusa/core-flows" - -// ... - -export const createBrandWorkflow = createWorkflow( - "create-brand", - (input: CreateBrandInput) => { - // ... - - emitEventStep({ - eventName: "brand.created", - data: { - id: brand.id, - }, - }) - - return new WorkflowResponse(brand) - } -) -``` - -The `emitEventStep` accepts an object parameter having two properties: - -- `eventName`: The name of the event to emit. You'll use this name later to listen to the event in a subscriber. -- `data`: The data payload to emit with the event. This data is passed to subscribers that listen to the event. You add the brand's ID to the data payload, informing the subscribers which brand was created. - -You'll learn how to handle this event in a later step. - -*** - -## 2. Create Sync to Third-Party System Workflow - -The subscriber that will listen to the `brand.created` event will sync the created brand to the third-party CMS. So, you'll implement the syncing logic in a workflow, then execute the workflow in the subscriber. - -Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. - -Learn more about workflows in [this chapter](undefined/learn/fundamentals/workflows). - -You'll create a `syncBrandToSystemWorkflow` that has two steps: - -- `useQueryGraphStep`: a step that Medusa provides to retrieve data using [Query](undefined/learn/fundamentals/module-links/query). You'll use this to retrieve the brand's details using its ID. -- `syncBrandToCmsStep`: a step that you'll create to sync the brand to the CMS. - -### syncBrandToCmsStep - -To implement the step that syncs the brand to the CMS, create the file `src/workflows/sync-brands-to-cms.ts` with the following content: - -![Directory structure of the Medusa application after adding the file](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493547/Medusa%20Book/cms-dir-overview-4_u5t0ug.jpg) - -```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncStepHighlights} collapsibleLines="1-6" expandButtonLabel="Show Imports" -import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" -import { InferTypeOf } from "@medusajs/framework/types" -import { Brand } from "../modules/brand/models/brand" -import { CMS_MODULE } from "../modules/cms" -import CmsModuleService from "../modules/cms/service" - -type SyncBrandToCmsStepInput = { - brand: InferTypeOf -} - -const syncBrandToCmsStep = createStep( - "sync-brand-to-cms", - async ({ brand }: SyncBrandToCmsStepInput, { container }) => { - const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) - - await cmsModuleService.createBrand(brand) - - return new StepResponse(null, brand.id) - }, - async (id, { container }) => { - if (!id) { - return - } - - const cmsModuleService: CmsModuleService = container.resolve(CMS_MODULE) - - await cmsModuleService.deleteBrand(id) - } -) -``` - -You create the `syncBrandToCmsStep` that accepts a brand as an input. In the step, you resolve the CMS Module's service from the [Medusa container](undefined/learn/fundamentals/medusa-container) and use its `createBrand` method. This method will create the brand in the third-party CMS. - -You also pass the brand's ID to the step's compensation function. In this function, you delete the brand in the third-party CMS if an error occurs during the workflow's execution. - -Learn more about compensation functions in [this chapter](undefined/learn/fundamentals/workflows/compensation-function). - -### Create Workflow - -You can now create the workflow that uses the above step. Add the workflow to the same `src/workflows/sync-brands-to-cms.ts` file: - -```ts title="src/workflows/sync-brands-to-cms.ts" highlights={syncWorkflowHighlights} -// other imports... -import { - // ... - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -type SyncBrandToCmsWorkflowInput = { - id: string -} - -export const syncBrandToCmsWorkflow = createWorkflow( - "sync-brand-to-cms", - (input: SyncBrandToCmsWorkflowInput) => { - // @ts-ignore - const { data: brands } = useQueryGraphStep({ - entity: "brand", - fields: ["*"], - filters: { - id: input.id, - }, - options: { - throwIfKeyNotFound: true, - }, - }) - - syncBrandToCmsStep({ - brand: brands[0], - } as SyncBrandToCmsStepInput) - - return new WorkflowResponse({}) - } -) -``` - -You create a `syncBrandToCmsWorkflow` that accepts the brand's ID as input. The workflow has the following steps: - -- `useQueryGraphStep`: Retrieve the brand's details using Query. You pass the brand's ID as a filter, and set the `throwIfKeyNotFound` option to true so that the step throws an error if a brand with the specified ID doesn't exist. -- `syncBrandToCmsStep`: Create the brand in the third-party CMS. - -You'll execute this workflow in the subscriber next. - -Learn more about `useQueryGraphStep` in [this reference](undefined/resources/references/helper-steps/useQueryGraphStep). - -*** - -## 3. Handle brand.created Event - -You now have a workflow with the logic to sync a brand to the CMS. You need to execute this workflow whenever the `brand.created` event is emitted. So, you'll create a subscriber that listens to and handle the event. - -Subscribers are created in a TypeScript or JavaScript file under the `src/subscribers` directory. So, create the file `src/subscribers/brand-created.ts` with the following content: - -![Directory structure of the Medusa application after adding the subscriber](https://res.cloudinary.com/dza7lstvk/image/upload/v1733493774/Medusa%20Book/cms-dir-overview-5_iqqwvg.jpg) - -```ts title="src/subscribers/brand-created.ts" highlights={subscriberHighlights} -import type { - SubscriberConfig, - SubscriberArgs, -} from "@medusajs/framework" -import { syncBrandToCmsWorkflow } from "../workflows/sync-brands-to-cms" - -export default async function brandCreatedHandler({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - await syncBrandToCmsWorkflow(container).run({ - input: data, - }) -} - -export const config: SubscriberConfig = { - event: "brand.created", -} -``` - -A subscriber file must export: - -- The asynchronous function that's executed when the event is emitted. This must be the file's default export. -- An object that holds the subscriber's configurations. It has an `event` property that indicates the name of the event that the subscriber is listening to. - -The subscriber function accepts an object parameter that has two properties: - -- `event`: An object of event details. Its `data` property holds the event's data payload, which is the brand's ID. -- `container`: The Medusa container used to resolve framework and commerce tools. - -In the function, you execute the `syncBrandToCmsWorkflow`, passing it the data payload as an input. So, everytime a brand is created, Medusa will execute this function, which in turn executes the workflow to sync the brand to the CMS. - -Learn more about subscribers in [this chapter](undefined/learn/fundamentals/events-and-subscribers). - -*** - -## Test it Out - -To test the subscriber and workflow out, you'll use the [Create Brand API route](undefined/learn/customization/custom-features/api-route) you created in a previous chapter. - -First, start the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Since the `/admin/brands` API route has a `/admin` prefix, it's only accessible by authenticated admin users. So, to retrieve an authenticated token of your admin user, send a `POST` request to the `/auth/user/emailpass` API Route: - -```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 with your admin user's credentials. - -Don't have an admin user? Refer to [this guide](undefined/learn/installation#create-medusa-admin-user). - -Then, send a `POST` request to `/admin/brands`, passing the token received from the previous request in the `Authorization` header: - -```bash -curl -X POST 'http://localhost:9000/admin/brands' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "name": "Acme" -}' -``` - -This request returns the created brand. If you check the logs, you'll find the `brand.created` event was emitted, and that the request to the third-party system was simulated: - -```plain -info: Processing brand.created which has 1 subscribers -http: POST /admin/brands ← - (200) - 16.418 ms -info: Sending a POST request to /brands. -info: Request Data: { - "id": "01JEDWENYD361P664WRQPMC3J8", - "name": "Acme", - "created_at": "2024-12-06T11:42:32.909Z", - "updated_at": "2024-12-06T11:42:32.909Z", - "deleted_at": null -} -info: API Key: "123" -``` - -*** - -## Next Chapter: Sync Brand from Third-Party CMS to Medusa - -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. - - -# Guide: Schedule Syncing Brands from CMS - -In the previous chapters, you've [integrated a third-party CMS](undefined/learn/customization/integrate-systems/service) and implemented the logic to [sync created brands](undefined/learn/customization/integrate-systems/handle-event) from Medusa to the CMS. - -However, when you integrate a third-party system, you want the data to be in sync between the Medusa application and the system. One way to do so is by automatically syncing the data once a day. - -You can create an action to be automatically executed at a specified interval using scheduled jobs. A scheduled job is an asynchronous function with a specified schedule of when the Medusa application should run it. Scheduled jobs are useful to automate repeated tasks. - -Learn more about scheduled jobs in [this chapter](undefined/learn/fundamentals/scheduled-jobs). - -In this chapter, you'll create a scheduled job that triggers syncing the brands from the third-party CMS to Medusa once a day. You'll implement the syncing logic in a workflow, and execute that workflow in the scheduled job. - -### Prerequisites - -- [CMS Module](undefined/learn/customization/integrate-systems/service) - -*** - -## 1. Implement Syncing Workflow - -You'll start by implementing the syncing logic in a workflow, then execute the workflow later in the scheduled job. - -Workflows have a built-in durable execution engine that helps you complete tasks spanning multiple systems. Also, their rollback mechanism ensures that data is consistent across systems even when errors occur during execution. - -Learn more about workflows in [this chapter](undefined/learn/fundamentals/workflows). - -This workflow will have three steps: - -1. `retrieveBrandsFromCmsStep` to retrieve the brands from the CMS. -2. `createBrandsStep` to create the brands retrieved in the first step that don't exist in Medusa. -3. `updateBrandsStep` to update the brands retrieved in the first step that exist in Medusa. - -### retrieveBrandsFromCmsStep - -To create the step that retrieves the brands from the third-party CMS, create the file `src/workflows/sync-brands-from-cms.ts` with the following content: - -![Directory structure of the Medusa application after creating the file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1733494196/Medusa%20Book/cms-dir-overview-6_z1omsi.jpg) - -```ts title="src/workflows/sync-brands-from-cms.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports" -import { - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import CmsModuleService from "../modules/cms/service" -import { CMS_MODULE } from "../modules/cms" - -const retrieveBrandsFromCmsStep = createStep( - "retrieve-brands-from-cms", - async (_, { container }) => { - const cmsModuleService: CmsModuleService = container.resolve( - CMS_MODULE - ) - - const brands = await cmsModuleService.retrieveBrands() - - return new StepResponse(brands) - } -) + return new StepResponse(brands) + } +) ``` You create a `retrieveBrandsFromCmsStep` that resolves the CMS Module's service and uses its `retrieveBrands` method to retrieve the brands in the CMS. You return those brands in the step's response. @@ -4508,11 +4508,11 @@ export const createBrandsStep = createStep( ) ``` -The `createBrandsStep` accepts the brands to create as an input. It resolves the [Brand Module](undefined/learn/customization/custom-features/module)'s service and uses the generated `createBrands` method to create the brands. +The `createBrandsStep` accepts the brands to create as an input. It resolves the [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md)'s service and uses the generated `createBrands` method to create the brands. The step passes the created brands to the compensation function, which deletes those brands if an error occurs during the workflow's execution. -Learn more about compensation functions in [this chapter](undefined/learn/fundamentals/workflows/compensation-function). +Learn more about compensation functions in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/compensation-function/index.html.md). ### Update Brands Step @@ -4590,9 +4590,9 @@ export const syncBrandsFromCmsWorkflow = createWorkflow( In the workflow, you only use the `retrieveBrandsFromCmsStep` for now, which retrieves the brands from the third-party CMS. -Next, you need to identify which brands must be created or updated. Since workflows are constructed internally and are only evaluated during execution, you can't access values to perform data manipulation directly. Instead, use [transform](undefined/learn/fundamentals/workflows/variable-manipulation) from the Workflows SDK that gives you access to the real-time values of the data, allowing you to create new variables using those values. +Next, you need to identify which brands must be created or updated. Since workflows are constructed internally and are only evaluated during execution, you can't access values to perform data manipulation directly. Instead, use [transform](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md) from the Workflows SDK that gives you access to the real-time values of the data, allowing you to create new variables using those values. -Learn more about data manipulation using `transform` in [this chapter](undefined/learn/fundamentals/workflows/variable-manipulation). +Learn more about data manipulation using `transform` in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md). So, replace the `TODO` with the following: @@ -4686,7 +4686,7 @@ A scheduled job file must export: - `name`: A unique name for the scheduled job. - `schedule`: A string that holds a [cron expression](https://crontab.guru/) indicating the schedule to run the job. -The scheduled job function accepts as a parameter the [Medusa container](undefined/learn/fundamentals/medusa-container) used to resolve framework and commerce tools. You then execute the `syncBrandsFromCmsWorkflow` and use its result to log how many brands were created or updated. +The scheduled job function accepts as a parameter the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) used to resolve framework and commerce tools. You then execute the `syncBrandsFromCmsWorkflow` and use its result to log how many brands were created or updated. Based on the cron expression specified in `config.schedule`, Medusa will run the scheduled job every day at midnight. You can also change it to `* * * * *` to run it every minute for easier debugging. @@ -4713,9 +4713,9 @@ With Medusa, you can integrate any service from your commerce ecosystem with eas # Guide: Integrate CMS Brand System -In the previous chapters, you've created a [Brand Module](undefined/learn/customization/custom-features/module) that adds brands to your application. In this chapter, you'll integrate a dummy Content-Management System (CMS) in a new module. The module's service will provide methods to retrieve and manage brands in the CMS. You'll later use this service to sync data from and to the CMS. +In the previous chapters, you've created a [Brand Module](https://docs.medusajs.com/learn/customization/custom-features/module/index.html.md) that adds brands to your application. In this chapter, you'll integrate a dummy Content-Management System (CMS) in a new module. The module's service will provide methods to retrieve and manage brands in the CMS. You'll later use this service to sync data from and to the CMS. -Learn more about modules in [this chapter](undefined/learn/fundamentals/modules). +Learn more about modules in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). ## 1. Create Module Directory @@ -4762,7 +4762,7 @@ export default CmsModuleService You create a `CmsModuleService` that will hold the methods to connect to the third-party CMS. A service's constructor accepts two parameters: -1. The module's container. Since a module is [isolated](undefined/learn/fundamentals/modules/isolation), it has a [local container](undefined/learn/fundamentals/modules/container) different than the Medusa container you use in other customizations. This container holds framework tools like the [Logger utility](undefined/learn/debugging-and-testing/logging) and resources within the module. +1. The module's container. Since a module is [isolated](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), it has a [local container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md) different than the Medusa container you use in other customizations. This container holds framework tools like the [Logger utility](https://docs.medusajs.com/learn/debugging-and-testing/logging/index.html.md) and resources within the module. 2. Options passed to the module when it's later added in Medusa's configurations. These options are useful to pass secret keys or configurations that ensure your module is re-usable across applications. For the CMS Module, you accept the API key to connect to the dummy CMS as an option. When integrating a third-party system that has a Node.js SDK or client, you can initialize that client in the constructor to be used in the service's methods. @@ -4876,7 +4876,7 @@ In this chapter, you'll learn about `moduleIntegrationTestRunner` from Medusa's ### Prerequisites -- [Testing Tools Setup](undefined/learn/debugging-and-testing/testing-tools) +- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md) ## moduleIntegrationTestRunner Utility @@ -4925,7 +4925,7 @@ Run the following command to run your module integration tests: 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](undefined/learn/debugging-and-testing/testing-tools#add-test-commands). +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. @@ -4978,7 +4978,7 @@ jest.setTimeout(60 * 1000) ### Other Options and Inputs -Refer to [this reference in the Development Resources documentation](undefined/resources/test-tools-reference/moduleIntegrationTestRunner) for other available parameter options and inputs of the `testSuite` function. +Refer to [this reference in the Development Resources documentation](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md) for other available parameter options and inputs of the `testSuite` function. *** @@ -4986,7 +4986,7 @@ Refer to [this reference in the Development Resources documentation](undefined/r 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 [references in the Development Resources documentation](undefined/resources/test-tools-reference/moduleIntegrationTestRunner). +To manage that database, such as changing its name or perform operations on it in your tests, refer to the [references in the Development Resources documentation](https://docs.medusajs.com/resources/test-tools-reference/moduleIntegrationTestRunner/index.html.md). # Write Integration Tests @@ -4995,7 +4995,7 @@ In this chapter, you'll learn about `medusaIntegrationTestRunner` from Medusa's ### Prerequisites -- [Testing Tools Setup](undefined/learn/debugging-and-testing/testing-tools) +- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md) ## medusaIntegrationTestRunner Utility @@ -5046,7 +5046,7 @@ Run the following command to run your tests: npm run test:integration ``` -If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](undefined/learn/debugging-and-testing/testing-tools#add-test-commands). +If you don't have a `test:integration` 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 under the `src/integrations/http` directory. @@ -5054,7 +5054,7 @@ This runs your Medusa application and runs the tests available under the `src/in ## Other Options and Inputs -Refer to [this reference in the Development Resources documentation](undefined/resources/test-tools-reference/medusaIntegrationTestRunner) for other available parameter options and inputs of the `testSuite` function. +Refer to [this reference in the Development Resources documentation](https://docs.medusajs.com/resources/test-tools-reference/medusaIntegrationTestRunner/index.html.md) for other available parameter options and inputs of the `testSuite` function. *** @@ -5062,7 +5062,7 @@ Refer to [this reference in the Development Resources documentation](undefined/r The `medusaIntegrationTestRunner` 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 [references in the Development Resources documentation](undefined/resources/test-tools-reference/medusaIntegrationTestRunner). +To manage that database, such as changing its name or perform operations on it in your tests, refer to the [references in the Development Resources documentation](https://docs.medusajs.com/resources/test-tools-reference/medusaIntegrationTestRunner/index.html.md). *** @@ -5116,155 +5116,73 @@ export const config = defineWidgetConfig({ ``` -# Admin Development Tips - -In this chapter, you'll find some tips for your admin development. +# Environment Variables in Admin Customizations -## Send Requests to API Routes +In this chapter, you'll learn how to use environment variables in your admin customizations. -To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](undefined/resources/js-sdk) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default. +To learn how envirnment 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). -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. +## How to Set Environment Variables -First, create the file `src/admin/lib/config.ts` to setup the SDK for use in your customizations: +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_`. -```ts -import Medusa from "@medusajs/js-sdk" +For example: -export const sdk = new Medusa({ - baseUrl: import.meta.env.VITE_BACKEND_URL || "/", - debug: import.meta.env.DEV, - auth: { - type: "session", - }, -}) +```plain +VITE_MY_API_KEY=sk_123 ``` -Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](undefined/learn/fundamentals/admin/environment-variables). +*** -Learn more about the JS SDK's configurations [this documentation](undefined/resources/js-sdk#js-sdk-configurations). +## How to Use Environment Variables -Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests. +To access or use an environment variable starting with `VITE_`, use the `import.meta.env` object. For example: -### Query - -```tsx title="src/admin/widgets/product-widget.ts" highlights={queryHighlights} +```tsx highlights={[["8"]]} 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" +import { Container, Heading } from "@medusajs/ui" const ProductWidget = () => { - const { data, isLoading } = useQuery({ - queryFn: () => sdk.admin.product.list(), - queryKey: ["products"], - }) - return ( - {isLoading && Loading...} - {data?.products && ( -
    - {data.products.map((product) => ( -
  • {product.title}
  • - ))} -
- )} +
+ API Key: {import.meta.env.VITE_MY_API_KEY} +
) } export const config = defineWidgetConfig({ - zone: "product.list.before", + zone: "product.details.before", }) export default ProductWidget ``` -### Mutation +In this example, you display the API key in a widget using `import.meta.env.VITE_MY_API_KEY`. -```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" +### Type Error on import.meta.env -const ProductWidget = ({ - data: productData, -}: DetailWidgetProps) => { - const { mutateAsync } = useMutation({ - mutationFn: (payload: HttpTypes.AdminUpdateProduct) => - sdk.admin.product.update(productData.id, payload), - onSuccess: () => alert("updated product"), - }) +If you receive a type error on `import.meta.env`, create the file `src/admin/vite-env.d.ts` with the following content: - const handleUpdate = () => { - mutateAsync({ - title: "New Product Title", - }) - } - - return ( - - - - ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget +```ts title="src/admin/vite-env.d.ts" +/// ``` -You can also send requests to custom routes as explained in the [JS SDK reference](undefined/resources/js-sdk). +This file tells TypeScript to recognize the `import.meta.env` object and enhances the types of your custom environment variables. *** -## Routing Functionalities +## Check Node Environment in Admin Customizations -To navigate or link to other pages, or perform other routing functionalities, use the [react-router-dom](https://reactrouter.com/en/main) package. It's installed in your project through the Medusa Admin. +To check the current environment, Vite exposes two variables: -For example: +- `import.meta.env.DEV`: Returns `true` if the current environment is development. +- `import.meta.env.PROD`: Returns `true` if the current environment is production. -```tsx title="src/admin/widgets/product-widget.tsx" highlights={highlights} -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container } from "@medusajs/ui" -import { Link } from "react-router-dom" - -// The widget -const ProductWidget = () => { - return ( - - View Orders - - ) -} - -// The widget's configurations -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -This adds a widget in a product's details page with a link to the Orders page. The link's path must be without the `/app` prefix. - -Refer to [react-router-dom’s documentation](https://reactrouter.com/en/main) for other available components and hooks. - -*** - -## Admin Translations - -The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions. - -Learn how to add a new language translation for the Medusa Admin in [this guide](undefined/resources/contribution-guidelines/admin-translations). +Learn more about other Vite environment variables in the [Vite documentation](https://vite.dev/guide/env-and-mode). # Admin UI Routes @@ -5283,7 +5201,7 @@ For example, you can add a new page to show and manage product reviews, which ar ### Prerequisites -- [Medusa application installed](undefined/learn/installation) +- [Medusa application installed](https://docs.medusajs.com/learn/installation/index.html.md) You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/routes` directory. The file's path relative to `src/admin/routes` determines its path in the dashboard. The file’s default export must be the UI route’s React component. @@ -5309,7 +5227,7 @@ export default CustomPage You add a new route at `http://localhost:9000/app/custom`. The `CustomPage` component holds the page's content, which currently only shows a heading. -In the route, you use [Medusa UI](undefined/ui), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. +In the route, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. The UI route component must be created as an arrow function. @@ -5357,7 +5275,7 @@ The configuration object is created using `defineRouteConfig` from the Medusa Fr - `label`: the sidebar item’s label. - `icon`: an optional React component used as an icon in the sidebar. -The above example adds a new sidebar item with the label `Custom Route` and an icon from the [Medusa UI Icons package](undefined/ui/icons/overview). +The above example adds a new sidebar item with the label `Custom Route` and an icon from the [Medusa UI Icons package](https://docs.medusajs.com/ui/icons/overview/index.html.md). ### Nested UI Routes @@ -5494,16 +5412,16 @@ If you run the Medusa application and go to `localhost:9000/app/custom/123`, you ## Admin Components List -To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](undefined/resources/admin-components) to find common components. +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. # Admin Development Tips In this chapter, you'll find some tips for your admin development. -To learn how envirnment variables are generally loaded in Medusa based on your application's environment, check out [this chapter](undefined/learn/fundamentals/environment-variables). +## Send Requests to API Routes -To send a request to an API route in the Medusa Application, use Medusa's [JS SDK](undefined/resources/js-sdk) with [Tanstack Query](https://tanstack.com/query/latest). Both of these tools are installed in your project by default. +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. 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. @@ -5521,9 +5439,9 @@ export const sdk = new Medusa({ }) ``` -Notice that you use `import.meta.env` to access environment variables in your customizations, as explained in [this chapter](undefined/learn/fundamentals/admin/environment-variables). +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). -Learn more about the JS SDK's configurations [this documentation](undefined/resources/js-sdk#js-sdk-configurations). +Learn more about the JS SDK's configurations [this documentation](https://docs.medusajs.com/resources/js-sdk#js-sdk-configurations/index.html.md). Then, use the configured SDK with the `useQuery` Tanstack Query hook to send `GET` requests, and `useMutation` hook to send `POST` or `DELETE` requests. @@ -5603,7 +5521,7 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -You can also send requests to custom routes as explained in the [JS SDK reference](undefined/resources/js-sdk). +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). *** @@ -5645,7 +5563,7 @@ Refer to [react-router-dom’s documentation](https://reactrouter.com/en/main) f The Medusa Admin dashboard can be displayed in languages other than English, which is the default. Other languages are added through community contributions. -Learn how to add a new language translation for the Medusa Admin in [this guide](undefined/resources/contribution-guidelines/admin-translations). +Learn how to add a new language translation for the Medusa Admin in [this guide](https://docs.medusajs.com/resources/contribution-guidelines/admin-translations/index.html.md). # Admin Widgets @@ -5664,7 +5582,7 @@ For example, you can add a widget on the product details page that allow admin u ### Prerequisites -- [Medusa application installed](undefined/learn/installation) +- [Medusa application installed](https://docs.medusajs.com/learn/installation/index.html.md) You create a widget in a `.tsx` file under the `src/admin/widgets` directory. The file’s default export must be the widget, which is the React component that renders the custom content. The file must also export the widget’s configurations indicating where to insert the widget. @@ -5695,7 +5613,7 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -You export the `ProductWidget` component, which shows the heading `Product Widget`. In the widget, you use [Medusa UI](undefined/ui), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. +You export the `ProductWidget` component, which shows the heading `Product Widget`. In the widget, you use [Medusa UI](https://docs.medusajs.com/ui/index.html.md), a package that Medusa maintains to allow you to customize the dashboard with the same components used to build it. To export the widget's configurations, you use `defineWidgetConfig` from the Admin Extension SDK. It accepts as a parameter an object with the `zone` property, whose value is a string or an array of strings, each being the name of the zone to inject the widget into. @@ -5758,212 +5676,201 @@ The props type is `DetailWidgetProps`, and it accepts as a type argument the exp ## Injection Zone -Refer to [this reference](undefined/resources/admin-widget-injection-zones) for the full list of injection zones and their props. +Refer to [this reference](https://docs.medusajs.com/resources/admin-widget-injection-zones/index.html.md) for the full list of injection zones and their props. *** ## Admin Components List -To build admin customizations that match the Medusa Admin's designs and layouts, refer to [this guide](undefined/resources/admin-components) to find common components. - - -# Pass Additional Data to Medusa's API Route +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. -In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route. -## Why Pass Additional Data? +# Seed Data with Custom CLI Script -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. +In this chapter, you'll learn how to seed data using a custom CLI script. -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. +## How to Seed Data -For example, the [Create Product API Route](undefined/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. +To seed dummy data for development or demo purposes, use a custom CLI script. -### API Routes Accepting Additional Data +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. -### API Routes List +### Example: Seed Dummy Products -- Campaigns - - [Create Campaign](undefined/api/admin#campaigns_postcampaigns) - - [Update Campaign](undefined/api/admin#campaigns_postcampaignsid) -- Cart - - [Create Cart](undefined/api/store#carts_postcarts) - - [Update Cart](undefined/api/store#carts_postcartsid) -- Collections - - [Create Collection](https://docs.medusajs.com/api/admin#collections_postcollections) - - [Update Collection](https://docs.medusajs.com/api/admin#collections_postcollectionsid) -- Customers - - [Create Customer](undefined/api/admin#customers_postcustomers) - - [Update Customer](undefined/api/admin#customers_postcustomersid) - - [Create Address](undefined/api/admin#customers_postcustomersidaddresses) - - [Update Address](undefined/api/admin#customers_postcustomersidaddressesaddress_id) -- Draft Orders - - [Create Draft Order](undefined/api/admin#draft-orders_postdraftorders) -- Orders - - [Complete Orders](undefined/api/admin#orders_postordersidcomplete) - - [Cancel Order's Fulfillment](undefined/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) - - [Create Shipment](undefined/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) - - [Create Fulfillment](undefined/api/admin#orders_postordersidfulfillments) -- Products - - [Create Product](undefined/api/admin#products_postproducts) - - [Update Product](undefined/api/admin#products_postproductsid) - - [Create Product Variant](undefined/api/admin#products_postproductsidvariants) - - [Update Product Variant](undefined/api/admin#products_postproductsidvariantsvariant_id) - - [Create Product Option](undefined/api/admin#products_postproductsidoptions) - - [Update Product Option](undefined/api/admin#products_postproductsidoptionsoption_id) -- Product Tags - - [Create Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttags) - - [Update Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttagsid) -- Product Types - - [Create Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypes) - - [Update Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypesid) -- Promotions - - [Create Promotion](undefined/api/admin#promotions_postpromotions) - - [Update Promotion](undefined/api/admin#promotions_postpromotionsid) +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: -## How to Pass Additional Data +```bash npm2yarn +npm install --save-dev @faker-js/faker +``` -### 1. Specify Validation of Additional Data +Then, create the file `src/scripts/demo-products.ts` with the following content: -Before passing custom data in the `additional_data` object parameter, you must specify validation rules for the allowed properties in the object. +```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" -To do that, use the middleware route object defined in `src/api/middlewares.ts`. +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 + ) -For example, create the file `src/api/middlewares.ts` with the following content: + const defaultSalesChannel = await salesChannelModuleService + .listSalesChannels({ + name: "Default Sales Channel", + }) -```ts title="src/api/middlewares.ts" -import { defineMiddlewares } from "@medusajs/framework/http" -import { z } from "zod" + const sizeOptions = ["S", "M", "L", "XL"] + const colorOptions = ["Black", "White"] + const currency_code = "eur" + const productsNum = 50 -export default defineMiddlewares({ - routes: [ - { - method: "POST", - matcher: "/admin/products", - additionalDataValidator: { - brand: z.string().optional(), - }, - }, - ], -}) + // TODO seed products +} ``` -The middleware route object accepts an optional parameter `additionalDataValidator` whose value is an object of key-value pairs. The keys indicate the name of accepted properties in the `additional_data` parameter, and the value is [Zod](https://zod.dev/) validation rules of the property. - -In this example, you indicate that the `additional_data` parameter accepts a `brand` property whose value is an optional string. - -Refer to [Zod's documentation](https://zod.dev) for all available validation rules. - -### 2. Pass the Additional Data in a Request +So far, in the script, you: -You can now pass a `brand` property in the `additional_data` parameter of a request to the Create Product API Route. +- 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. -For example: +Next, replace the `TODO` with the following: -```bash -curl -X POST 'http://localhost:9000/admin/products' \ --H 'Content-Type: application/json' \ --H 'Authorization: Bearer {token}' \ ---data '{ - "title": "Product 1", - "options": [ +```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": "Default option", - "values": ["Default option value"] - } + title: "Size", + values: sizeOptions, + }, + { + title: "Color", + values: colorOptions, + }, ], - "shipping_profile_id": "{shipping_profile_id}", - "additional_data": { - "brand": "Acme" - } -}' -``` + 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, + }, + ], + } +}) -Make sure to replace the `{token}` in the authorization header with an admin user's authentication token, and `{shipping_profile_id}` with an existing shipping profile's ID. +// TODO seed products +``` -In this request, you pass in the `additional_data` parameter a `brand` property and set its value to `Acme`. +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. -The `additional_data` is then passed to hooks in the `createProductsWorkflow` used by the API route. +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, + }, +}) -## Use Additional Data in a Hook +logger.info(`Seeded ${products.length} products.`) -Learn about workflow hooks in [this guide](undefined/learn/fundamentals/workflows/workflow-hooks). +// TODO add inventory levels +``` -Step functions consuming the workflow hook can access the `additional_data` in the first parameter. +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. -For example, consider you want to store the data passed in `additional_data` in the product's `metadata` property. +Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following: -To do that, create the file `src/workflows/hooks/product-created.ts` with the following content: +```ts title="src/scripts/demo-products.ts" +logger.info("Seeding inventory levels.") -```ts title="src/workflows/hooks/product-created.ts" -import { StepResponse } from "@medusajs/framework/workflows-sdk" -import { createProductsWorkflow } from "@medusajs/medusa/core-flows" -import { Modules } from "@medusajs/framework/utils" +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: ["id"], +}) -createProductsWorkflow.hooks.productsCreated( - async ({ products, additional_data }, { container }) => { - if (!additional_data?.brand) { - return - } +const { data: inventoryItems } = await query.graph({ + entity: "inventory_item", + fields: ["id"], +}) - const productModuleService = container.resolve( - Modules.PRODUCT - ) +const inventoryLevels = inventoryItems.map((inventoryItem) => ({ + location_id: stockLocations[0].id, + stocked_quantity: 1000000, + inventory_item_id: inventoryItem.id, +})) - await productModuleService.upsertProducts( - products.map((product) => ({ - ...product, - metadata: { - ...product.metadata, - brand: additional_data.brand, - }, - })) - ) +await createInventoryLevelsWorkflow(container).run({ + input: { + inventory_levels: inventoryLevels, + }, +}) - return new StepResponse(products, { - products, - additional_data, - }) - } -) +logger.info("Finished seeding inventory levels data.") ``` -This consumes the `productsCreated` hook, which runs after the products are created. - -If `brand` is passed in `additional_data`, you resolve the Product Module's main service and use its `upsertProducts` method to update the products, adding the brand to the `metadata` property. - -### Compensation Function +You use Query to retrieve the stock location, to use the first location in the application, and the inventory items. -Hooks also accept a compensation function as a second parameter to undo the actions made by the step function. +Then, you generate inventory levels for each inventory item, associating it with the first stock location. -For example, pass the following second parameter to the `productsCreated` hook: +Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels. -```ts title="src/workflows/hooks/product-created.ts" -createProductsWorkflow.hooks.productsCreated( - async ({ products, additional_data }, { container }) => { - // ... - }, - async ({ products, additional_data }, { container }) => { - if (!additional_data.brand) { - return - } +### Test Script - const productModuleService = container.resolve( - Modules.PRODUCT - ) +To test out the script, run the following command in your project's directory: - await productModuleService.upsertProducts( - products - ) - } -) +```bash +npx medusa exec ./src/scripts/demo-products.ts ``` -This updates the products to their original state before adding the brand to their `metadata` property. +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. # Handling CORS in API Routes @@ -5998,7 +5905,7 @@ module.exports = defineConfig({ 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](undefined/resources/references/medusa-config#http). +Learn more about the CORS configurations in [this resource guide](https://docs.medusajs.com/resources/references/medusa-config#http/index.html.md). *** @@ -6078,6 +5985,205 @@ export default defineMiddlewares({ This retrieves the configurations exported from `medusa-config.ts` and applies the `storeCors` to routes starting with `/custom`. +# Pass Additional Data to Medusa's API Route + +In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route. + +## Why Pass Additional Data? + +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. + +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. + +### API Routes Accepting Additional Data + +### API Routes List + +- Campaigns + - [Create Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaigns) + - [Update Campaign](https://docs.medusajs.com/api/admin#campaigns_postcampaignsid) +- Cart + - [Create Cart](https://docs.medusajs.com/api/store#carts_postcarts) + - [Update Cart](https://docs.medusajs.com/api/store#carts_postcartsid) +- Collections + - [Create Collection](https://docs.medusajs.com/api/admin#collections_postcollections) + - [Update Collection](https://docs.medusajs.com/api/admin#collections_postcollectionsid) +- Customers + - [Create Customer](https://docs.medusajs.com/api/admin#customers_postcustomers) + - [Update Customer](https://docs.medusajs.com/api/admin#customers_postcustomersid) + - [Create Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddresses) + - [Update Address](https://docs.medusajs.com/api/admin#customers_postcustomersidaddressesaddress_id) +- Draft Orders + - [Create Draft Order](https://docs.medusajs.com/api/admin#draft-orders_postdraftorders) +- Orders + - [Complete Orders](https://docs.medusajs.com/api/admin#orders_postordersidcomplete) + - [Cancel Order's Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) + - [Create Shipment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) + - [Create Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments) +- Products + - [Create Product](https://docs.medusajs.com/api/admin#products_postproducts) + - [Update Product](https://docs.medusajs.com/api/admin#products_postproductsid) + - [Create Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariants) + - [Update Product Variant](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_id) + - [Create Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptions) + - [Update Product Option](https://docs.medusajs.com/api/admin#products_postproductsidoptionsoption_id) +- Product Tags + - [Create Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttags) + - [Update Product Tag](https://docs.medusajs.com/api/admin#product-tags_postproducttagsid) +- Product Types + - [Create Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypes) + - [Update Product Type](https://docs.medusajs.com/api/admin#product-types_postproducttypesid) +- Promotions + - [Create Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotions) + - [Update Promotion](https://docs.medusajs.com/api/admin#promotions_postpromotionsid) + +*** + +## How to Pass Additional Data + +### 1. Specify Validation of Additional Data + +Before passing custom data in the `additional_data` object parameter, you must specify validation rules for the allowed properties in the object. + +To do that, use the middleware route object defined in `src/api/middlewares.ts`. + +For example, create the file `src/api/middlewares.ts` with the following content: + +```ts title="src/api/middlewares.ts" +import { defineMiddlewares } from "@medusajs/framework/http" +import { z } from "zod" + +export default defineMiddlewares({ + routes: [ + { + method: "POST", + matcher: "/admin/products", + additionalDataValidator: { + brand: z.string().optional(), + }, + }, + ], +}) +``` + +The middleware route object accepts an optional parameter `additionalDataValidator` whose value is an object of key-value pairs. The keys indicate the name of accepted properties in the `additional_data` parameter, and the value is [Zod](https://zod.dev/) validation rules of the property. + +In this example, you indicate that the `additional_data` parameter accepts a `brand` property whose value is an optional string. + +Refer to [Zod's documentation](https://zod.dev) for all available validation rules. + +### 2. Pass the Additional Data in a Request + +You can now pass a `brand` property in the `additional_data` parameter of a request to the Create Product API Route. + +For example: + +```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": "Acme" + } +}' +``` + +Make sure to replace the `{token}` in the authorization header with an admin user's authentication token, and `{shipping_profile_id}` with an existing shipping profile's ID. + +In this request, you pass in the `additional_data` parameter a `brand` property and set its value to `Acme`. + +The `additional_data` is then passed to hooks in the `createProductsWorkflow` used by the API route. + +*** + +## Use Additional Data in a Hook + +Learn about workflow hooks in [this guide](https://docs.medusajs.com/learn/fundamentals/workflows/workflow-hooks/index.html.md). + +Step functions consuming the workflow hook can access the `additional_data` in the first parameter. + +For example, consider you want to store the data passed in `additional_data` in the product's `metadata` property. + +To do that, create the file `src/workflows/hooks/product-created.ts` with the following content: + +```ts title="src/workflows/hooks/product-created.ts" +import { StepResponse } from "@medusajs/framework/workflows-sdk" +import { createProductsWorkflow } from "@medusajs/medusa/core-flows" +import { Modules } from "@medusajs/framework/utils" + +createProductsWorkflow.hooks.productsCreated( + async ({ products, additional_data }, { container }) => { + if (!additional_data?.brand) { + return + } + + const productModuleService = container.resolve( + Modules.PRODUCT + ) + + await productModuleService.upsertProducts( + products.map((product) => ({ + ...product, + metadata: { + ...product.metadata, + brand: additional_data.brand, + }, + })) + ) + + return new StepResponse(products, { + products, + additional_data, + }) + } +) +``` + +This consumes the `productsCreated` hook, which runs after the products are created. + +If `brand` is passed in `additional_data`, you resolve the Product Module's main service and use its `upsertProducts` method to update the products, adding the brand to the `metadata` property. + +### Compensation Function + +Hooks also accept a compensation function as a second parameter to undo the actions made by the step function. + +For example, pass the following second parameter to the `productsCreated` hook: + +```ts title="src/workflows/hooks/product-created.ts" +createProductsWorkflow.hooks.productsCreated( + async ({ products, additional_data }, { container }) => { + // ... + }, + async ({ products, additional_data }, { container }) => { + if (!additional_data.brand) { + return + } + + const productModuleService = container.resolve( + Modules.PRODUCT + ) + + await productModuleService.upsertProducts( + products + ) + } +) +``` + +This updates the products to their original state before adding the brand to their `metadata` property. + + # 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. @@ -6435,6 +6541,151 @@ This adds two API Routes: - A `POST` route at `http://localhost:9000/hello-world`. +# 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). + + # Configure Request Body Parser In this chapter, you'll learn how to configure the request body parser for your API routes. @@ -6469,7 +6720,7 @@ export default defineMiddlewares({ 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](undefined/learn/fundamentals/api-routes/middlewares). +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: @@ -6568,7 +6819,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](undefined/resources/architectural-modules/file) is to use the [uploadFilesWorkflow](undefined/resources/references/medusa-workflows/uploadFilesWorkflow): +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" @@ -6603,152 +6854,7 @@ export async function POST( } ``` -Check out the [uploadFilesWorkflow reference](undefined/resources/references/medusa-workflows/uploadFilesWorkflow) 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](undefined/learn/fundamentals/api-routes/validation#how-to-validate-request-query-paramters). - -*** - -## 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](undefined/learn/fundamentals/api-routes/parse-body). - -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](undefined/learn/fundamentals/api-routes/validation#how-to-validate-request-body). +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. # Protected Routes @@ -6767,7 +6873,7 @@ Medusa applies an authentication guard on routes starting with `/admin`, includi Requests to `/admin` must be user-authenticated to access the route. -Refer to the API Reference for [Admin](undefined/api/admin#authentication) and [Store](undefined/api/store#authentication) authentication methods. +Refer to the API Reference for [Admin](https://docs.medusajs.com/api/admin#authentication) and [Store](https://docs.medusajs.com/api/store#authentication) authentication methods. *** @@ -7210,7 +7316,7 @@ export default defineMiddlewares({ The `validateAndTransformQuery` accepts two parameters: - The first one is the Zod schema to validate the query parameters against. -- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](undefined/learn/fundamentals/module-links/query). +- The second one is an object of options for retrieving data using Query, which you can learn more about in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). #### How the Validation Works @@ -7262,192 +7368,286 @@ 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 +# Event Data Payload -In this chapter, you'll learn how to seed data using a custom CLI script. +In this chapter, you'll learn how subscribers receive an event's data payload. -## How to Seed Data +## Access Event's Data Payload -To seed dummy data for development or demo purposes, use a custom CLI script. +When events are emitted, they’re emitted with a data payload. -In the CLI script, use your custom workflows or Medusa's existing workflows, which you can browse in [this reference](undefined/resources/medusa-workflows-reference), to seed data. +The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context. -### Example: Seed Dummy Products +For example: -In this section, you'll follow an example of creating a custom CLI script that seeds fifty dummy products. +```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" +import type { + SubscriberArgs, + SubscriberConfig, +} from "@medusajs/framework" -First, install the [Faker](https://fakerjs.dev/) library to generate random data in your script: +export default async function productCreateHandler({ + event, +}: SubscriberArgs<{ id: string }>) { + const productId = event.data.id + console.log(`The product ${productId} was created`) +} -```bash npm2yarn -npm install --save-dev @faker-js/faker +export const config: SubscriberConfig = { + event: "product.created", +} ``` -Then, create the file `src/scripts/demo-products.ts` with the following content: +The `event` object has the following properties: -```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" +- data: (\`object\`) The data payload of the event. Its properties are different for each event. +- name: (string) The name of the triggered event. +- metadata: (\`object\`) Additional data and context of the emitted event. -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 - ) +This logs the product ID received in the `product.created` event’s data payload to the console. - 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 +## List of Events with Data Payload - // TODO seed products -} +Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */} + + +# Configure Data Model Properties + +In this chapter, you’ll learn how to configure data model properties. + +## Property’s 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 MyCustom = model.define("my_custom", { + color: model + .enum(["black", "white"]) + .default("black"), + age: model + .number() + .default(0), + // ... +}) + +export default MyCustom ``` -So far, in the script, you: +In this example, you set the default value of the `color` enum property to `black`, and that of the `age` number property to `0`. -- 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: +## Nullable Property -```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, - }, - ], - } +Use the `nullable` method to indicate that a property’s value can be `null`. + +For example: + +```ts highlights={nullableHighlights} +import { model } from "@medusajs/framework/utils" + +const MyCustom = model.define("my_custom", { + price: model.bigNumber().nullable(), + // ... }) -// TODO seed products +export default MyCustom ``` -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: +## Unique Property -```ts title="src/scripts/demo-products.ts" -const { result: products } = await createProductsWorkflow(container).run({ - input: { - products: productsData, - }, +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(), + // ... }) -logger.info(`Seeded ${products.length} products.`) +export default User +``` -// TODO add inventory levels +In this example, multiple users can’t have the same email. + + +# 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. + +## Event Types + +In your customization, you can emit an event, then listen to it in a subscriber and perform an asynchronus action, such as send a notification or data to a third-party system. + +There are two types of events in Medusa: + +1. Workflow event: an event that's emitted in a workflow after a commerce feature is performed. For example, Medusa emits the `order.placed` event after a cart is completed. +2. Service event: an event that's emitted to track, trace, or debug processes under the hood. For example, you can emit an event with an audit trail. + +### Which Event Type to Use? + +**Workflow events** are the most common event type in development, as most custom features and customizations are built around workflows. + +Some examples of workflow events: + +1. When a user creates a blog post and you're emitting an event to send a newsletter email. +2. When you finish syncing products to a third-party system and you want to notify the admin user of new products added. +3. When a customer purchases a digital product and you want to generate and send it to them. + +You should only go for a **service event** if you're emitting an event for processes under the hood that don't directly affect front-facing features. + +Some examples of service events: + +1. When you're tracing data manipulation and changes, and you want to track every time some custom data is changed. +2. When you're syncing data with a search engine. + +*** + +## Emit Event in a Workflow + +To emit a workflow event, use the `emitEventStep` helper step provided in the `@medusajs/medusa/core-flows` package. + +For example: + +```ts highlights={highlights} +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + emitEventStep, +} from "@medusajs/medusa/core-flows" + +const helloWorldWorkflow = createWorkflow( + "hello-world", + () => { + // ... + + emitEventStep({ + eventName: "custom.created", + data: { + id: "123", + // other data payload + }, + }) + } +) ``` -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. +The `emitEventStep` accepts an object having the following properties: -Only thing left is to create inventory levels for the products. So, replace the last `TODO` with the following: +- `eventName`: The event's name. +- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. -```ts title="src/scripts/demo-products.ts" -logger.info("Seeding inventory levels.") +In this example, you emit the event `custom.created` and pass in the data payload an ID property. -const { data: stockLocations } = await query.graph({ - entity: "stock_location", - fields: ["id"], -}) +### Test it Out -const { data: inventoryItems } = await query.graph({ - entity: "inventory_item", - fields: ["id"], -}) +If you execute the workflow, the event is emitted and you can see it in your application's logs. -const inventoryLevels = inventoryItems.map((inventoryItem) => ({ - location_id: stockLocations[0].id, - stocked_quantity: 1000000, - inventory_item_id: inventoryItem.id, -})) +Any subscribers listening to the event are executed. -await createInventoryLevelsWorkflow(container).run({ - input: { - inventory_levels: inventoryLevels, - }, -}) +*** -logger.info("Finished seeding inventory levels data.") +## Emit Event in a Service + +To emit a service event: + +1. Resolve `event_bus` from the module's container in your service's constructor: + +### Extending Service Factory + +```ts title="src/modules/hello/service.ts" highlights={["9"]} +import { IEventBusService } from "@medusajs/framework/types" +import { MedusaService } from "@medusajs/framework/utils" + +class HelloModuleService extends MedusaService({ + MyCustom, +}){ + protected eventBusService_: AbstractEventBusModuleService + + constructor({ event_bus }) { + super(...arguments) + this.eventBusService_ = event_bus + } +} ``` -You use Query to retrieve the stock location, to use the first location in the application, and the inventory items. +### Without Service Factory -Then, you generate inventory levels for each inventory item, associating it with the first stock location. +```ts title="src/modules/hello/service.ts" highlights={["6"]} +import { IEventBusService } from "@medusajs/framework/types" -Finally, you use the `createInventoryLevelsWorkflow` from Medusa's core workflows to create the inventory levels. +class HelloModuleService { + protected eventBusService_: AbstractEventBusModuleService -### Test Script + constructor({ event_bus }) { + this.eventBusService_ = event_bus + } +} +``` -To test out the script, run the following command in your project's directory: +2. Use the event bus service's `emit` method in the service's methods to emit an event: -```bash -npx medusa exec ./src/scripts/demo-products.ts +```ts title="src/modules/hello/service.ts" highlights={serviceHighlights} +class HelloModuleService { + // ... + performAction() { + // TODO perform action + + this.eventBusService_.emit({ + name: "custom.event", + data: { + id: "123", + // other data payload + }, + }) + } +} ``` -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. +The method accepts an object having the following properties: + +- `name`: The event's name. +- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. + +3. By default, the Event Module's service isn't injected into your module's container. To add it to the container, pass it in the module's registration object in `medusa-config.ts` in the `dependencies` property: + +```ts title="medusa-config.ts" highlights={depsHighlight} +import { Modules } from "@medusajs/framework/utils" + +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/hello", + dependencies: [ + Modules.EVENT_BUS, + ], + }, + ], +}) +``` + +The `dependencies` property accepts an array of module registration keys. The specified modules' main services are injected into the module's container. + +That's how you can resolve it in your module's main service's constructor. + +### Test it Out + +If you execute the `performAction` method of your service, the event is emitted and you can see it in your application's logs. + +Any subscribers listening to the event are also executed. # Add Data Model Check Constraints @@ -7708,205 +7908,385 @@ class HelloModuleService extends MedusaService({ MyCustom }) { ``` -# Data Model’s Primary Key +# Manage Relationships -In this chapter, you’ll learn how to configure the primary key of a data model. +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. -## primaryKey Method +## Manage One-to-One Relationship -To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method. +### BelongsTo Side of One-to-One -For example: +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. -```ts highlights={highlights} -import { model } from "@medusajs/framework/utils" +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: -const MyCustom = model.define("my_custom", { - id: model.id().primaryKey(), - // ... +```ts highlights={belongsHighlights} +// when creating an email +const email = await helloModuleService.createEmails({ + // other properties... + user_id: "123", }) -export default MyCustom +// when updating an email +const email = await helloModuleService.updateEmails({ + id: "321", + // other properties... + user_id: "123", +}) ``` -In the example above, the `id` property is defined as the data model's primary key. - - -# Data Model Property Types - -In this chapter, you’ll learn about the types of properties in a data model’s schema. - -## id +In the example above, you pass the `user_id` property when creating or updating an email to specify the user it belongs to. -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. +### HasOne Side -For example: +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. -```ts highlights={idHighlights} -import { model } from "@medusajs/framework/utils" +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: -const MyCustom = model.define("my_custom", { - id: model.id(), - // ... +```ts highlights={hasOneHighlights} +// when creating a user +const user = await helloModuleService.createUsers({ + // other properties... + email: "123", }) -export default MyCustom +// 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. + *** -## text +## Manage One-to-Many Relationship -The `text` method defines a string property. +In a one-to-many relationship, you can only manage the associations from the `belongsTo` side. -For example: +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. -```ts highlights={textHighlights} -import { model } from "@medusajs/framework/utils" +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: -const MyCustom = model.define("my_custom", { - name: model.text(), - // ... +```ts highlights={manyBelongsHighlights} +// when creating a product +const product = await helloModuleService.createProducts({ + // other properties... + store_id: "123", }) -export default MyCustom +// 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. + *** -## number +## Manage Many-to-Many Relationship -The `number` method defines a number property. +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. -For example: +### Create Associations -```ts highlights={numberHighlights} -import { model } from "@medusajs/framework/utils" +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. -const MyCustom = model.define("my_custom", { - age: model.number(), - // ... +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"], }) -export default MyCustom +// 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. -## float +### Update Associations -This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2). +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. -The `float` method defines a number property that allows for values with decimal places. +However, this removes any existing associations to records whose IDs aren't included in the array. -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, 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: -For example: +```ts +const product = await helloModuleService.updateProducts({ + id: "123", + // other properties... + orders: ["321"], +}) +``` -```ts highlights={floatHighlights} -import { model } from "@medusajs/framework/utils" +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. -const MyCustom = model.define("my_custom", { - rating: model.float(), - // ... -}) +So, to add a new association without removing existing ones, retrieve the product first to pass its associated orders when updating the product: -export default MyCustom +```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. -## bigNumber +*** -The `bigNumber` method defines a number property that expects large numbers, such as prices. +## Manage Many-to-Many Relationship with pivotEntity -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). +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. -For example: +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. -```ts highlights={bigNumberHighlights} -import { model } from "@medusajs/framework/utils" +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: -const MyCustom = model.define("my_custom", { - price: model.bigNumber(), - // ... +```ts highlights={["4"]} +class HelloModuleService 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 helloModuleService.createOrderProducts({ + order_id: "123", + product_id: "123", + metadata: { + test: true, + }, }) -export default MyCustom +// update order-product association +const orderProduct = await helloModuleService.updateOrderProducts({ + id: "123", + metadata: { + test: false, + }, +}) + +// delete order-product association +await helloModuleService.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. + *** -## boolean +## Retrieve Records of Relation -The `boolean` method defines a boolean property. +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 helloModuleService.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’s Primary Key + +In this chapter, you’ll learn how to configure the primary key of a data model. + +## primaryKey Method + +To set any `id`, `text`, or `number` property as a primary key, use the `primaryKey` method. For example: -```ts highlights={booleanHighlights} +```ts highlights={highlights} import { model } from "@medusajs/framework/utils" const MyCustom = model.define("my_custom", { - hasAccount: model.boolean(), + id: model.id().primaryKey(), // ... }) export default MyCustom ``` +In the example above, the `id` property is defined as the data model's primary key. + + +# Searchable Data Model Property + +In this chapter, you'll learn what a searchable property is and how to define it. + +## What is 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. + *** -### enum +## Define a Searchable Property -The `enum` method defines a property whose value can only be one of the specified values. +Use the `searchable` method on a `text` property to indicate that it's searchable. For example: -```ts highlights={enumHighlights} +```ts highlights={searchableHighlights} import { model } from "@medusajs/framework/utils" const MyCustom = model.define("my_custom", { - color: model.enum(["black", "white"]), + name: model.text().searchable(), // ... }) export default MyCustom ``` -The `enum` method accepts an array of possible string values. +In this example, the `name` property is searchable. + +### Search Example + +If you pass a `q` filter to the `listMyCustoms` method: + +```ts +const myCustoms = await helloModuleService.listMyCustoms({ + q: "John", +}) +``` + +This retrieves records that include `John` in their `name` property. + + +# Write Migration + +In this chapter, you'll learn how to create a migration and write it manually. + +## What is a Migration? + +A migration is a class created in a TypeScript or JavaScript file under a module's `migrations` directory. It has two methods: + +- The `up` method reflects changes on the database. +- The `down` method reverts the changes made in the `up` method. *** -## dateTime +## How to Write a Migration? -The `dateTime` method defines a timestamp property. +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 the specified modules' data models. + +Alternatively, you can manually create a migration file under the `migrations` directory of your module. For example: -```ts highlights={dateTimeHighlights} -import { model } from "@medusajs/framework/utils" +```ts title="src/modules/hello/migrations/Migration20240429.ts" +import { Migration } from "@mikro-orm/migrations" -const MyCustom = model.define("my_custom", { - date_of_birth: model.dateTime(), - // ... -}) +export class Migration20240702105919 extends Migration { -export default MyCustom + async up(): Promise { + this.addSql("create table if not exists \"my_custom\" (\"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 \"my_custom_pkey\" primary key (\"id\"));") + } + + async down(): Promise { + this.addSql("drop table if exists \"my_custom\" cascade;") + } + +} ``` +The migration's file name should be of the format `Migration{YEAR}{MONTH}{DAY}.ts`. 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 `my_custom`, 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. + *** -## json +## Run the Migration -The `json` method defines a property whose value is a stringified JSON object. +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 helloModuleService +``` + +This rolls back the last ran migration on the Hello Module. + +*** + +## 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 Property Types + +In this chapter, you’ll learn about the types of properties in a data model’s schema. + +## 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={jsonHighlights} +```ts highlights={idHighlights} import { model } from "@medusajs/framework/utils" const MyCustom = model.define("my_custom", { - metadata: model.json(), + id: model.id(), // ... }) @@ -7915,17 +8295,17 @@ export default MyCustom *** -## array +## text -The `array` method defines an array of strings property. +The `text` method defines a string property. For example: -```ts highlights={arrHightlights} +```ts highlights={textHighlights} import { model } from "@medusajs/framework/utils" const MyCustom = model.define("my_custom", { - names: model.array(), + name: model.text(), // ... }) @@ -7934,227 +8314,169 @@ export default MyCustom *** -## Properties Reference - -Refer to the [Data Model API reference](undefined/resources/references/data-model) for a full reference of the properties. - +## number -# Manage Relationships +The `number` method defines a number property. -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. +For example: -## Manage One-to-One Relationship +```ts highlights={numberHighlights} +import { model } from "@medusajs/framework/utils" -### BelongsTo Side of One-to-One +const MyCustom = model.define("my_custom", { + age: model.number(), + // ... +}) -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. +export default MyCustom +``` -For example, assuming you have the [User and Email data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#one-to-one-relationship), 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", -}) +## float -// when updating an email -const email = await helloModuleService.updateEmails({ - id: "321", - // other properties... - user_id: "123", -}) -``` +This property is only available after [Medusa v2.1.2](https://github.com/medusajs/medusa/releases/tag/v2.1.2). -In the example above, you pass the `user_id` property when creating or updating an email to specify the user it belongs to. +The `float` method defines a number property that allows for values with decimal places. -### HasOne Side +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). -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: -For example, assuming you have the [User and Email data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#one-to-one-relationship), set a user's email ID as follows: +```ts highlights={floatHighlights} +import { model } from "@medusajs/framework/utils" -```ts highlights={hasOneHighlights} -// when creating a user -const user = await helloModuleService.createUsers({ - // other properties... - email: "123", +const MyCustom = model.define("my_custom", { + rating: model.float(), + // ... }) -// when updating a user -const user = await helloModuleService.updateUsers({ - id: "321", - // other properties... - email: "123", -}) +export default MyCustom ``` -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 +## bigNumber -In a one-to-many relationship, you can only manage the associations from the `belongsTo` side. +The `bigNumber` method defines a number property that expects large numbers, such as prices. -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. +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, assuming you have the [Product and Store data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#one-to-many-relationship), set a product's store ID as follows: +For example: -```ts highlights={manyBelongsHighlights} -// when creating a product -const product = await helloModuleService.createProducts({ - // other properties... - store_id: "123", -}) +```ts highlights={bigNumberHighlights} +import { model } from "@medusajs/framework/utils" -// when updating a product -const product = await helloModuleService.updateProducts({ - id: "321", - // other properties... - store_id: "123", +const MyCustom = model.define("my_custom", { + price: model.bigNumber(), + // ... }) -``` -In the example above, you pass the `store_id` property when creating or updating a product to specify the store it belongs to. +export default MyCustom +``` *** -## Manage Many-to-Many Relationship - -If your many-to-many relation is represented with a [pivotEntity](undefined/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns), refer to [this section](#manage-many-to-many-relationship-with-pivotentity) instead. +## boolean -### Create Associations +The `boolean` method defines a boolean property. -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: -For example, assuming you have the [Order and Product data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#many-to-many-relationship), set the association between products and orders as follows: +```ts highlights={booleanHighlights} +import { model } from "@medusajs/framework/utils" -```ts highlights={manyHighlights} -// when creating a product -const product = await helloModuleService.createProducts({ - // other properties... - orders: ["123", "321"], +const MyCustom = model.define("my_custom", { + hasAccount: model.boolean(), + // ... }) -// when creating an order -const order = await helloModuleService.createOrders({ - id: "321", - // other properties... - products: ["123", "321"], -}) +export default MyCustom ``` -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 +### enum -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. +The `enum` method defines a property whose value can only be one of the specified values. -However, this removes any existing associations to records whose IDs aren't included in the array. +For example: -For example, assuming you have the [Order and Product data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#many-to-many-relationship), you update the product's related orders as so: +```ts highlights={enumHighlights} +import { model } from "@medusajs/framework/utils" -```ts -const product = await helloModuleService.updateProducts({ - id: "123", - // other properties... - orders: ["321"], +const MyCustom = model.define("my_custom", { + color: model.enum(["black", "white"]), + // ... }) -``` - -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", - ], -}) +export default MyCustom ``` -This keeps existing associations between the product and orders, and adds a new one. +The `enum` method accepts an array of possible string values. *** -## Manage Many-to-Many Relationship with pivotEntity +## dateTime + +The `dateTime` method defines a timestamp property. -If your many-to-many relation is represented without a [pivotEntity](undefined/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns), refer to [this section](#manage-many-to-many-relationship) instead. +For example: -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](undefined/learn/fundamentals/modules/service-factory) that your module's service extends. +```ts highlights={dateTimeHighlights} +import { model } from "@medusajs/framework/utils" -For example, assuming you have the [Order, Product, and OrderProduct models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#many-to-many-with-custom-columns), add `OrderProduct` to `MedusaService`'s object parameter: +const MyCustom = model.define("my_custom", { + date_of_birth: model.dateTime(), + // ... +}) -```ts highlights={["4"]} -class HelloModuleService extends MedusaService({ - Order, - Product, - OrderProduct, -}) {} +export default MyCustom ``` -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. +*** + +## json + +The `json` method defines a property whose value is a stringified JSON object. For example: -```ts -// create order-product association -const orderProduct = await helloModuleService.createOrderProducts({ - order_id: "123", - product_id: "123", - metadata: { - test: true, - }, -}) +```ts highlights={jsonHighlights} +import { model } from "@medusajs/framework/utils" -// update order-product association -const orderProduct = await helloModuleService.updateOrderProducts({ - id: "123", - metadata: { - test: false, - }, +const MyCustom = model.define("my_custom", { + metadata: model.json(), + // ... }) -// delete order-product association -await helloModuleService.deleteOrderProducts("123") +export default MyCustom ``` -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](undefined/resources/service-factory-reference) for a full list of generated methods and their usages. - *** -## Retrieve Records of Relation +## array -The `list`, `listAndCount`, and `retrieve` methods of a module's main service accept as a second parameter an object of options. +The `array` method defines an array of strings property. -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: -For example, assuming you have the [Order and Product data models from the previous chapter](undefined/learn/fundamentals/data-models/relationships#many-to-many-relationship), you retrieve a product's orders as follows: +```ts highlights={arrHightlights} +import { model } from "@medusajs/framework/utils" -```ts highlights={retrieveHighlights} -const product = await helloModuleService.retrieveProducts( - "123", - { - relations: ["orders"], - } -) +const MyCustom = model.define("my_custom", { + names: model.array(), + // ... +}) + +export default MyCustom ``` -In the example above, the retrieved product has an `orders` property, whose value is an array of orders associated with the product. +*** + +## Properties Reference + +Refer to the [Data Model API reference](https://docs.medusajs.com/resources/references/data-model/index.html.md) for a full reference of the properties. # Data Model Relationships @@ -8208,7 +8530,7 @@ The `belongsTo` method also requires passing as a second parameter an object wit ### 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](undefined/learn/fundamentals/data-models/configure-properties#nullable-property). +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/configure-properties#nullable-property/index.html.md). ### One-sided One-to-One Relationship @@ -8273,7 +8595,7 @@ 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](undefined/learn/fundamentals/data-models/configure-properties#nullable-property). +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/configure-properties#nullable-property/index.html.md). ### One-to-Many Relationship in the Database @@ -8452,552 +8774,345 @@ The `cascades` method accepts an object. Its key is the operation’s name, such In the example above, when a store is deleted, its associated products are also deleted. -# Searchable Data Model Property +# Create a Plugin -In this chapter, you'll learn what a searchable property is and how to define it. +In this chapter, you'll learn how to create a Medusa plugin and publish it. -## What is a Searchable Property? +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. -Methods generated by the [service factory](undefined/learn/fundamentals/modules/service-factory) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters. +Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). -When the `q` filter is passed, the data model's searchable properties are queried to find matching records. +## 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. -## Define a Searchable Property +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: -Use the `searchable` method on a `text` property to indicate that it's searchable. +```bash +npx create-medusa-app my-plugin --plugin +``` -For example: +This will create a new Medusa plugin project in the `my-plugin` directory. -```ts highlights={searchableHighlights} -import { model } from "@medusajs/framework/utils" +### Plugin Directory Structure -const MyCustom = model.define("my_custom", { - name: model.text().searchable(), - // ... -}) +After the installation is done, the plugin structure will look like this: -export default MyCustom -``` +![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) -In this example, the `name` property is searchable. +- `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. -### Search Example +*** -If you pass a `q` filter to the `listMyCustoms` method: +## 2. Prepare Plugin -```ts -const myCustoms = await helloModuleService.listMyCustoms({ - q: "John", -}) -``` +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. -This retrieves records that include `John` in their `name` property. +For example: +```json title="package.json" +{ + "name": "@myorg/plugin-name", + // ... +} +``` -# Write Migration +In addition, make sure that the `keywords` field in `package.json` includes the keyword `medusa-plugin` and `medusa-v2`. This helps Medusa list community plugins on the Medusa website: -In this chapter, you'll learn how to create a migration and write it manually. +```json title="package.json" +{ + "keywords": [ + "medusa-plugin", + "medusa-v2" + ], + // ... +} +``` -## What is a Migration? +*** -A migration is a class created in a TypeScript or JavaScript file under a module's `migrations` directory. It has two methods: +## 3. Publish Plugin Locally for Development and Testing -- The `up` method reflects changes on the database. -- The `down` method reverts the changes made in the `up` method. +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 -## How to Write a Migration? +### Prerequisites -The Medusa CLI tool provides a [db:generate](undefined/resources/medusa-cli/commands/db#dbgenerate) command to generate a migration for the specified modules' data models. +- [Medusa application installed.](https://docs.medusajs.com/learn/installation/index.html.md) -Alternatively, you can manually create a migration file under the `migrations` directory of your module. +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. -For example: +To publish the plugin to the local registry, run the following command in your plugin project: -```ts title="src/modules/hello/migrations/Migration20240429.ts" -import { Migration } from "@mikro-orm/migrations" +```bash title="Plugin project" +npx medusa plugin:publish +``` -export class Migration20240702105919 extends Migration { +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`. - async up(): Promise { - this.addSql("create table if not exists \"my_custom\" (\"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 \"my_custom_pkey\" primary key (\"id\"));") - } - - async down(): Promise { - this.addSql("drop table if exists \"my_custom\" cascade;") - } +Next, navigate to your Medusa application: -} +```bash title="Medusa application" +cd ~/path/to/medusa-app ``` -The migration's file name should be of the format `Migration{YEAR}{MONTH}{DAY}.ts`. 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 `my_custom`, 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: +Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application. -This command also syncs module links. If you don't want that, use the `--skip-links` option. +Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency: -```bash -npx medusa db:migrate +```bash npm2yarn title="Medusa application" +npm install --save-dev yalc ``` -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: +After that, run the following Medusa CLI command to install the plugin: -```bash -npx medusa db:rollback helloModuleService +```bash title="Medusa application" +npx medusa plugin:add @myorg/plugin-name ``` -This rolls back the last ran migration on the Hello Module. - -*** - -## More Database Commands +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. -To learn more about the Medusa CLI's database commands, refer to [this CLI reference](undefined/resources/medusa-cli/commands/db). +### 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`. -# Event Data Payload +Add the plugin to the `plugins` array in the `medusa-config.ts` file: -In this chapter, you'll learn how subscribers receive an event's data payload. +```ts title="medusa-config.ts" highlights={pluginHighlights} +module.exports = defineConfig({ + // ... + plugins: [ + { + resolve: "@myorg/plugin-name", + options: {}, + }, + ], +}) +``` -## Access Event's Data Payload +The `plugins` configuration is an array of objects where each object has a `resolve` key whose value is the name of the plugin package. -Methods generated by the [service factory](undefined/learn/fundamentals/modules/service-factory) that accept filters, such as `list{ModelName}s`, accept a `q` property as part of the filters. +#### Pass Module Options through Plugin -The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context. +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="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" -import type { - SubscriberArgs, - SubscriberConfig, -} from "@medusajs/framework" - -export default async function productCreateHandler({ - event, -}: SubscriberArgs<{ id: string }>) { - const productId = event.data.id - console.log(`The product ${productId} was created`) -} - -export const config: SubscriberConfig = { - event: "product.created", -} +```ts title="medusa-config.ts" highlights={pluginOptionsHighlight} +module.exports = defineConfig({ + // ... + plugins: [ + { + resolve: "@myorg/plugin-name", + options: { + apiKey: true, + }, + }, + ], +}) ``` -The `event` object has the following properties: - -- data: (\`object\`) The data payload of the event. Its properties are different for each event. -- name: (string) The name of the triggered event. -- metadata: (\`object\`) Additional data and context of the emitted event. - -This logs the product ID received in the `product.created` event’s data payload to the console. - -{/* --- - -## List of Events with Data Payload - -Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */} - - -# 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. - -## Event Types - -In your customization, you can emit an event, then listen to it in a subscriber and perform an asynchronus action, such as send a notification or data to a third-party system. - -There are two types of events in Medusa: - -1. Workflow event: an event that's emitted in a workflow after a commerce feature is performed. For example, Medusa emits the `order.placed` event after a cart is completed. -2. Service event: an event that's emitted to track, trace, or debug processes under the hood. For example, you can emit an event with an audit trail. - -### Which Event Type to Use? - -**Workflow events** are the most common event type in development, as most custom features and customizations are built around workflows. - -Some examples of workflow events: - -1. When a user creates a blog post and you're emitting an event to send a newsletter email. -2. When you finish syncing products to a third-party system and you want to notify the admin user of new products added. -3. When a customer purchases a digital product and you want to generate and send it to them. - -You should only go for a **service event** if you're emitting an event for processes under the hood that don't directly affect front-facing features. - -Some examples of service events: - -1. When you're tracing data manipulation and changes, and you want to track every time some custom data is changed. -2. When you're syncing data with a search engine. - -*** - -## Emit Event in a Workflow - -To emit a workflow event, use the `emitEventStep` helper step provided in the `@medusajs/medusa/core-flows` package. +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). -For example: +### Watch Plugin Changes During Development -```ts highlights={highlights} -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - emitEventStep, -} from "@medusajs/medusa/core-flows" +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. -const helloWorldWorkflow = createWorkflow( - "hello-world", - () => { - // ... +To do that, run the following command in your plugin project: - emitEventStep({ - eventName: "custom.created", - data: { - id: "123", - // other data payload - }, - }) - } -) +```bash title="Plugin project" +npx medusa plugin:develop ``` -The `emitEventStep` accepts an object having the following properties: +This command will: -- `eventName`: The event's name. -- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. +- 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. -In this example, you emit the event `custom.created` and pass in the data payload an ID property. +### Start Medusa Application -### Test it Out +You can start your Medusa application's development server to test out your plugin: -If you execute the workflow, the event is emitted and you can see it in your application's logs. +```bash npm2yarn title="Medusa application" +npm run dev +``` -Any subscribers listening to the event are executed. +While your Medusa application is running and the plugin is being watched, you can test your plugin while developing it in the Medusa application. *** -## Emit Event in a Service +## 4. Create Customizations in the Plugin -To emit a service event: +You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin. -1. Resolve `event_bus` from the module's container in your service's constructor: +- [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) -### Extending Service Factory +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). -```ts title="src/modules/hello/service.ts" highlights={["9"]} -import { IEventBusService } from "@medusajs/framework/types" -import { MedusaService } from "@medusajs/framework/utils" +### Generating Migrations for Modules -class HelloModuleService extends MedusaService({ - MyCustom, -}){ - protected eventBusService_: AbstractEventBusModuleService +During your development, you may need to generate migrations for modules in your plugin. To do that, use the `plugin:db:generate` command: - constructor({ event_bus }) { - super(...arguments) - this.eventBusService_ = event_bus - } -} +```bash title="Plugin project" +npx medusa plugin:db:generate ``` -### Without Service Factory +This command generates migrations for all modules in the plugin. You can then run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command: -```ts title="src/modules/hello/service.ts" highlights={["6"]} -import { IEventBusService } from "@medusajs/framework/types" +```bash title="Medusa application" +npx medusa db:migrate +``` -class HelloModuleService { - protected eventBusService_: AbstractEventBusModuleService +### Importing Module Resources - constructor({ event_bus }) { - this.eventBusService_ = event_bus +Your plugin project should have the following exports in `package.json`: + +```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", + "./*": "./.medusa/server/src/*.js" } } ``` -2. Use the event bus service's `emit` method in the service's methods to emit an event: +Aside from the `./package.json` and `./providers`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export. -```ts title="src/modules/hello/service.ts" highlights={serviceHighlights} -class HelloModuleService { - // ... - performAction() { - // TODO perform action +The plugin exports the following files and directories: - this.eventBusService_.emit({ - name: "custom.event", - data: { - id: "123", - // other data payload - }, - }) - } -} -``` +- `./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 allows you to register the plugin's providers in the Medusa application. +- `./*`: Any other files in the plugin's `src` directory. -The method accepts an object having the following properties: +With these exports, you can import the plugin's resources in the Medusa application's code like this: -- `name`: The event's name. -- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. +`@myorg/plugin-name` is the plugin package's name. -3. By default, the Event Module's service isn't injected into your module's container. To add it to the container, pass it in the module's registration object in `medusa-config.ts` in the `dependencies` property: +```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" +``` -```ts title="medusa-config.ts" highlights={depsHighlight} -import { Modules } from "@medusajs/framework/utils" +And you can register a module provider in the Medusa application's `medusa-config.ts` like this: +```ts highlights={[["9"]]} title="medusa-config.ts" module.exports = defineConfig({ // ... modules: [ { - resolve: "./src/modules/hello", - dependencies: [ - Modules.EVENT_BUS, - ], + resolve: "@medusajs/medusa/notification", + options: { + providers: [ + { + resolve: "@myorg/plugin-name/providers/my-notification", + id: "my-notification", + options: { + channels: ["email"], + // provider options... + }, + }, + ], + }, }, ], }) ``` -The `dependencies` property accepts an array of module registration keys. The specified modules' main services are injected into the module's container. - -That's how you can resolve it in your module's main service's constructor. - -### Test it Out - -If you execute the `performAction` method of your service, the event is emitted and you can see it in your application's logs. - -Any subscribers listening to the event are also executed. - - -# Event Data Payload - -In this chapter, you'll learn how subscribers receive an event's data payload. - -## Access Event's Data Payload - -When events are emitted, they’re emitted with a data payload. - -The object that the subscriber function receives as a parameter has an `event` property, which is an object holding the event payload in a `data` property with additional context. - -For example: - -```ts title="src/subscribers/product-created.ts" highlights={highlights} collapsibleLines="1-5" expandButtonLabel="Show Imports" -import type { - SubscriberArgs, - SubscriberConfig, -} from "@medusajs/framework" - -export default async function productCreateHandler({ - event, -}: SubscriberArgs<{ id: string }>) { - const productId = event.data.id - console.log(`The product ${productId} was created`) -} - -export const config: SubscriberConfig = { - event: "product.created", -} -``` - -The `event` object has the following properties: - -- data: (\`object\`) The data payload of the event. Its properties are different for each event. -- name: (string) The name of the triggered event. -- metadata: (\`object\`) Additional data and context of the emitted event. - -This logs the product ID received in the `product.created` event’s data payload to the console. - -{/* --- - -## List of Events with Data Payload - -Refer to [this reference](!resources!/events-reference) for a full list of events emitted by Medusa and their data payloads. */} - - -# 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. - -## Event Types - -In your customization, you can emit an event, then listen to it in a subscriber and perform an asynchronus action, such as send a notification or data to a third-party system. - -There are two types of events in Medusa: - -1. Workflow event: an event that's emitted in a workflow after a commerce feature is performed. For example, Medusa emits the `order.placed` event after a cart is completed. -2. Service event: an event that's emitted to track, trace, or debug processes under the hood. For example, you can emit an event with an audit trail. - -### Which Event Type to Use? - -**Workflow events** are the most common event type in development, as most custom features and customizations are built around workflows. - -Some examples of workflow events: - -1. When a user creates a blog post and you're emitting an event to send a newsletter email. -2. When you finish syncing products to a third-party system and you want to notify the admin user of new products added. -3. When a customer purchases a digital product and you want to generate and send it to them. +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. -You should only go for a **service event** if you're emitting an event for processes under the hood that don't directly affect front-facing features. +### Create Module Providers -Some examples of service events: +To learn how to create module providers, refer to the following guides: -1. When you're tracing data manipulation and changes, and you want to track every time some custom data is changed. -2. When you're syncing data with a search engine. +- [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) *** -## Emit Event in a Workflow - -To emit a workflow event, use the `emitEventStep` helper step provided in the `@medusajs/medusa/core-flows` package. - -For example: - -```ts highlights={highlights} -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - emitEventStep, -} from "@medusajs/medusa/core-flows" +## 5. Publish Plugin to NPM -const helloWorldWorkflow = createWorkflow( - "hello-world", - () => { - // ... +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: - emitEventStep({ - eventName: "custom.created", - data: { - id: "123", - // other data payload - }, - }) - } -) +```bash +npx medusa plugin:build ``` -The `emitEventStep` accepts an object having the following properties: - -- `eventName`: The event's name. -- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. - -In this example, you emit the event `custom.created` and pass in the data payload an ID property. - -### Test it Out - -If you execute the workflow, the event is emitted and you can see it in your application's logs. - -Any subscribers listening to the event are executed. - -*** - -## Emit Event in a Service - -To emit a service event: - -1. Resolve `event_bus` from the module's container in your service's constructor: - -### Extending Service Factory - -```ts title="src/modules/hello/service.ts" highlights={["9"]} -import { IEventBusService } from "@medusajs/framework/types" -import { MedusaService } from "@medusajs/framework/utils" +The command will compile an output in the `.medusa/server` directory. -class HelloModuleService extends MedusaService({ - MyCustom, -}){ - protected eventBusService_: AbstractEventBusModuleService +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: - constructor({ event_bus }) { - super(...arguments) - this.eventBusService_ = event_bus - } -} +```bash +npm publish ``` -### Without Service Factory +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. -```ts title="src/modules/hello/service.ts" highlights={["6"]} -import { IEventBusService } from "@medusajs/framework/types" +### Install Public Plugin in Medusa Application -class HelloModuleService { - protected eventBusService_: AbstractEventBusModuleService +You install a plugin that's published publicly using your package manager. For example: - constructor({ event_bus }) { - this.eventBusService_ = event_bus - } -} +```bash npm2yarn +npm install @myorg/plugin-name ``` -2. Use the event bus service's `emit` method in the service's methods to emit an event: +Where `@myorg/plugin-name` is the name of your plugin as published on NPM. -```ts title="src/modules/hello/service.ts" highlights={serviceHighlights} -class HelloModuleService { - // ... - performAction() { - // TODO perform action +Then, register the plugin in your Medusa application's configurations as explained in [this section](#register-plugin-in-medusa-application). - this.eventBusService_.emit({ - name: "custom.event", - data: { - id: "123", - // other data payload - }, - }) - } -} -``` +*** -The method accepts an object having the following properties: +## Update a Published Plugin -- `name`: The event's name. -- `data`: The data payload as an object. You can pass any properties in the object, and subscribers listening to the event will receive this data in the event's payload. +To update the Medusa dependencies in a plugin, refer to [this documentation](https://docs.medusajs.com/learn/update#update-plugin-project/index.html.md). -3. By default, the Event Module's service isn't injected into your module's container. To add it to the container, pass it in the module's registration object in `medusa-config.ts` in the `dependencies` property: +If you've published a plugin and you've made changes to it, you'll have to publish the update to NPM again. -```ts title="medusa-config.ts" highlights={depsHighlight} -import { Modules } from "@medusajs/framework/utils" +First, run the following command to change the version of the plugin: -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "./src/modules/hello", - dependencies: [ - Modules.EVENT_BUS, - ], - }, - ], -}) +```bash +npm version ``` -The `dependencies` property accepts an array of module registration keys. The specified modules' main services are injected into the module's container. - -That's how you can resolve it in your module's main service's constructor. +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. -### Test it Out +Then, re-run the same commands for publishing a plugin: -If you execute the `performAction` method of your service, the event is emitted and you can see it in your application's logs. +```bash +npx medusa plugin:build +npm publish +``` -Any subscribers listening to the event are also executed. +This will publish an updated version of your plugin under a new version. # Add Columns to a Link @@ -9077,7 +9192,7 @@ The object you pass to Link's `create` method accepts a `data` property. Its val For example: -Learn more about Link, how to resolve it, and its methods in [this chapter](undefined/learn/fundamentals/module-links/link). +Learn more about Link, how to resolve it, and its methods in [this chapter](https://docs.medusajs.com/learn/fundamentals/module-links/link/index.html.md). ```ts await link.create({ @@ -9099,7 +9214,7 @@ await link.create({ ## Retrieve Custom Column with Link -To retrieve linked records with their custom columns, use [Query](undefined/learn/fundamentals/module-links/query). A module link's definition, exported by a file under `src/links`, has a special `entryPoint` property. Use this property when specifying the `entity` property in Query's `graph` method. +To retrieve linked records with their custom columns, use [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). A module link's definition, exported by a file under `src/links`, has a special `entryPoint` property. Use this property when specifying the `entity` property in Query's `graph` method. For example: @@ -9207,7 +9322,147 @@ export default defineLink( ``` -# Query +# Link + +In this chapter, you’ll learn what Link is and how to use it to manage links. + +As of [Medusa v2.2.0](https://github.com/medusajs/medusa/releases/tag/v2.2.0), Remote Link has been deprecated in favor of Link. They have the same usage, so you only need to change the key used to resolve the tool from the Medusa container as explained below. + +## What is Link? + +Link is a class with utility methods to manage links between data models. It’s registered in the Medusa container under the `link` registration name. + +For example: + +```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + MedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" + +export async function POST( + req: MedusaRequest, + res: MedusaResponse +): Promise { + const link = req.scope.resolve( + ContainerRegistrationKeys.LINK + ) + + // ... +} +``` + +You can use its methods to manage links, such as create or delete links. + +*** + +## Create Link + +To create a link between records of two data models, use the `create` method of Link. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.create({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) +``` + +The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules. + +The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. + +The value of each module’s property is an object, whose keys are of the format `{data_model_snake_name}_id`, and values are the IDs of the linked record. + +So, in the example above, you link a record of the `MyCustom` data model in a `hello` module to a `Product` record in the Product Module. + +*** + +## Dismiss Link + +To remove a link between records of two data models, use the `dismiss` method of Link. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await link.dismiss({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, + "helloModuleService": { + my_custom_id: "mc_123", + }, +}) +``` + +The `dismiss` method accepts the same parameter type as the [create method](#create-link). + +The keys (names of linked modules) must be in the same [direction](https://docs.medusajs.com/learn/fundamentals/module-links/directions/index.html.md) of the link definition. + +*** + +## Cascade Delete Linked Records + +If a record is deleted, use the `delete` method of Link to delete all linked records. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await productModuleService.deleteVariants([variant.id]) + +await link.delete({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, +}) +``` + +This deletes all records linked to the deleted product. + +*** + +## Restore Linked Records + +If a record that was previously soft-deleted is now restored, use the `restore` method of Link to restore all linked records. + +For example: + +```ts +import { Modules } from "@medusajs/framework/utils" + +// ... + +await productModuleService.restoreProducts(["prod_123"]) + +await link.restore({ + [Modules.PRODUCT]: { + product_id: "prod_123", + }, +}) +``` + + +# Query In this chapter, you’ll learn about Query and how to use it to fetch data from modules. @@ -9317,7 +9572,7 @@ const { data: myCustoms } = await query.graph({ Consider you want to apply filters or pagination configurations on the product(s) linked to `my_custom`. To do that, you must query the module link's table instead. -As mentioned in the [Module Link](undefined/learn/fundamentals/module-links) documentation, Medusa creates a table for your module link. So, not only can you retrieve linked records, but you can also retrieve the records in a module link's table. +As mentioned in the [Module Link](https://docs.medusajs.com/learn/fundamentals/module-links/index.html.md) documentation, Medusa creates a table for your module link. So, not only can you retrieve linked records, but you can also retrieve the records in a module link's table. A module link's definition, exported by a file under `src/links`, has a special `entryPoint` property. Use this property when specifying the `entity` property in Query's `graph` method. @@ -9342,7 +9597,7 @@ In the object passed to the `graph` method: - You pass the `entryPoint` property of the link definition as the value for `entity`. So, Query will retrieve records from the module link's table. - You pass three items to the `field` property: - - `*` to retrieve the link table's fields. This is useful if the link table has [custom columns](undefined/learn/fundamentals/module-links/custom-columns). + - `*` to retrieve the link table's fields. This is useful if the link table has [custom columns](https://docs.medusajs.com/learn/fundamentals/module-links/custom-columns/index.html.md). - `product.*` to retrieve the fields of a product record linked to a `MyCustom` record. - `my_custom.*` to retrieve the fields of a `MyCustom` record linked to a product record. @@ -9449,7 +9704,7 @@ The `order` property is an object whose keys are property names, and values are For API routes that retrieve a single or list of resources, Medusa provides a `validateAndTransformQuery` middleware that: -- Validates accepted query parameters, as explained in [this documentation](undefined/learn/fundamentals/api-routes/validation). +- Validates accepted query parameters, as explained in [this documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation/index.html.md). - Parses configurations that are received as query parameters to be passed to Query. Using this middleware allows you to have default configurations for retrieved fields and relations or pagination, while allowing clients to customize them per request. @@ -9492,7 +9747,7 @@ export default defineMiddlewares({ The `validateAndTransformQuery` accepts two parameters: -1. A Zod validation schema for the query parameters, which you can learn more about in the [API Route Validation documentation](undefined/learn/fundamentals/api-routes/validation). Medusa has a `createFindParams` utility that generates a Zod schema that accepts four query parameters: +1. A Zod validation schema for the query parameters, which you can learn more about in the [API Route Validation documentation](https://docs.medusajs.com/learn/fundamentals/api-routes/validation/index.html.md). Medusa has a `createFindParams` utility that generates a Zod schema that accepts four query parameters: 1. `fields`: The fields and relations to retrieve in the returned resources. 2. `offset`: The number of items to skip before retrieving the returned items. 3. `limit`: The maximum number of items to return. @@ -9558,158 +9813,18 @@ To test it out, start your Medusa application and send a `GET` request to the `/ Try passing one of the Query configuration parameters, like `fields` or `limit`, and you'll see its impact on the returned result. -Learn more about [specifing fields and relations](undefined/api/store#select-fields-and-relations) and [pagination](undefined/api/store#pagination) in the API reference. - - -# Link - -In this chapter, you’ll learn what Link is and how to use it to manage links. - -As of [Medusa v2.2.0](https://github.com/medusajs/medusa/releases/tag/v2.2.0), Remote Link has been deprecated in favor of Link. They have the same usage, so you only need to change the key used to resolve the tool from the Medusa container as explained below. - -## What is Link? - -Link is a class with utility methods to manage links between data models. It’s registered in the Medusa container under the `link` registration name. - -For example: - -```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - MedusaRequest, - MedusaResponse, -} from "@medusajs/framework/http" -import { - ContainerRegistrationKeys, -} from "@medusajs/framework/utils" - -export async function POST( - req: MedusaRequest, - res: MedusaResponse -): Promise { - const link = req.scope.resolve( - ContainerRegistrationKeys.LINK - ) - - // ... -} -``` - -You can use its methods to manage links, such as create or delete links. - -*** - -## Create Link - -To create a link between records of two data models, use the `create` method of Link. - -For example: - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, -}) -``` - -The `create` method accepts as a parameter an object. The object’s keys are the names of the linked modules. - -The keys (names of linked modules) must be in the same [direction](undefined/learn/fundamentals/module-links/directions) of the link definition. - -The value of each module’s property is an object, whose keys are of the format `{data_model_snake_name}_id`, and values are the IDs of the linked record. - -So, in the example above, you link a record of the `MyCustom` data model in a `hello` module to a `Product` record in the Product Module. - -*** - -## Dismiss Link - -To remove a link between records of two data models, use the `dismiss` method of Link. - -For example: - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.dismiss({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - "helloModuleService": { - my_custom_id: "mc_123", - }, -}) -``` - -The `dismiss` method accepts the same parameter type as the [create method](#create-link). - -The keys (names of linked modules) must be in the same [direction](undefined/learn/fundamentals/module-links/directions) of the link definition. - -*** - -## Cascade Delete Linked Records - -If a record is deleted, use the `delete` method of Link to delete all linked records. - -For example: - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await productModuleService.deleteVariants([variant.id]) - -await link.delete({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, -}) -``` - -This deletes all records linked to the deleted product. - -*** - -## Restore Linked Records - -If a record that was previously soft-deleted is now restored, use the `restore` method of Link to restore all linked records. - -For example: - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await productModuleService.restoreProducts(["prod_123"]) - -await link.restore({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, -}) -``` +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. # Query Context -In this chapter, you'll learn how to pass contexts when retrieving data with [Query](undefined/learn/fundamentals/module-links/query). +In this chapter, you'll learn how to pass contexts when retrieving data with [Query](https://docs.medusajs.com/learn/fundamentals/module-links/query/index.html.md). ## What is Query Context? Query context is a way to pass additional information when retrieving data with Query. This data can be useful when applying custom transformations to the retrieved data based on the current context. -For example, consider you have a Blog Module with posts and authors. You can accept the user's language as a context and return the posts in the user's language. Another example is how Medusa uses Query Context to [retrieve product variants' prices based on the customer's currency](undefined/resources/commerce-modules/product/guides/price). +For example, consider you have a Blog Module with posts and authors. You can accept the user's language as a context and return the posts in the user's language. Another example is how Medusa uses Query Context to [retrieve product variants' prices based on the customer's currency](https://docs.medusajs.com/resources/commerce-modules/product/guides/price/index.html.md). *** @@ -9780,7 +9895,7 @@ You extract the context from `filters`, then retrieve the posts using the parent All posts returned will now have their titles appended with "en español". -Learn more about the generated `list` method in [this reference](undefined/resources/service-factory-reference/methods/list). +Learn more about the generated `list` method in [this reference](https://docs.medusajs.com/resources/service-factory-reference/methods/list/index.html.md). ### Using Pagination with Query @@ -9955,7 +10070,7 @@ There are different architectural module types including: ## Architectural Modules List -Refer to the [Architectural Modules reference](undefined/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 [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. # Commerce Modules @@ -9964,25 +10079,25 @@ In this chapter, you'll learn about Medusa's commerce modules. ## What is a Commerce Module? -Commerce modules are built-in [modules](undefined/learn/fundamentals/modules) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more. +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](undefined/resources/medusa-workflows-reference) and [APIs](undefined/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](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](undefined/resources/references/medusa-workflows/addToCartWorkflow) +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](undefined/resources/commerce-modules) for a full list of commerce modules in Medusa. +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](undefined/learn/fundamentals/modules), the Medusa application registers a commerce module's service in the [container](undefined/learn/fundamentals/medusa-container). 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](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](undefined/learn/fundamentals/workflows) (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: +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" @@ -10002,396 +10117,11 @@ 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. -# Architectural Modules - -In this chapter, you’ll learn about architectural modules. - -## What is an Architectural Module? - -An architectural 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 - -There are different architectural module types including: - -![Diagram illustrating how the modules connect to third-party services](https://res.cloudinary.com/dza7lstvk/image/upload/v1727095814/Medusa%20Book/architectural-modules_bj9bb9.jpg) - -- 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](undefined/resources/architectural-modules) for a list of Medusa’s architectural modules, available modules to install, and how to create an architectural module. - - -# Module Container - -In this chapter, you'll learn about the module's container and how to resolve resources in that container. - -Since modules are isolated, each module has a local container only used by the resources of that module. - -So, resources in the module, such as services or loaders, can only resolve other resources registered in the module's container. - -### List of Registered Resources - -Find a list of resources or dependencies registered in a module's container in [this Development Resources reference](undefined/resources/medusa-container-resources). - -*** - -## Resolve Resources - -### Services - -A service's constructor accepts as a first parameter an object used to resolve resources registered in the module's container. - -For example: - -```ts highlights={[["4"], ["10"]]} -import { Logger } from "@medusajs/framework/types" - -type InjectedDependencies = { - logger: Logger -} - -export default class HelloModuleService { - protected logger_: Logger - - constructor({ logger }: InjectedDependencies) { - this.logger_ = logger - - this.logger_.info("[HelloModuleService]: Hello World!") - } - - // ... -} -``` - -### Loader - -A loader function accepts as a parameter an object having the property `container`. Its value is the module's container used to resolve resources. - -For example: - -```ts highlights={[["9"]]} -import { - LoaderOptions, -} from "@medusajs/framework/types" -import { - ContainerRegistrationKeys, -} from "@medusajs/framework/utils" - -export default async function helloWorldLoader({ - container, -}: LoaderOptions) { - const logger = container.resolve(ContainerRegistrationKeys.LOGGER) - - logger.info("[helloWorldLoader]: Hello, World!") -} -``` - - -# Loaders - -In this chapter, you’ll learn about loaders and how to use them. - -## What is a Loader? - -When building a commerce application, you'll often need to execute an action the first time the application starts. For example, if your application needs to connect to databases other than Medusa's PostgreSQL database, you might need to establish a connection on application startup. - -In Medusa, you can execute an action when the application starts using a loader. A loader is a function exported by a [module](undefined/learn/fundamentals/modules), which is a package of business logic for a single domain. When the Medusa application starts, it executes all loaders exported by configured modules. - -Loaders are useful to register custom resources, such as database connections, in the [module's container](undefined/learn/fundamentals/modules/container), which is similar to the [Medusa container](undefined/learn/fundamentals/medusa-container) but includes only [resources available to the module](undefined/resources/medusa-container-resources#module-container-resources). Modules are isolated, so they can't access resources outside of them, such as a service in another module. - -Medusa isolates modules to ensure that they're re-usable across applications, aren't tightly coupled to other resources, and don't have implications when integrated into the Medusa application. Learn more about why modules are isolated in [this chapter](undefined/learn/fundamentals/modules/isolation), and check out [this reference for the list of resources in the module's container](undefined/resources/medusa-container-resources#module-container-resources). - -*** - -## How to Create a Loader? - -### 1. Implement Loader Function - -You create a loader function in a TypeScript or JavaScript file under a module's `loaders` directory. - -For example, consider you have a `hello` module, you can create a loader at `src/modules/hello/loaders/hello-world.ts` with the following content: - -![Example of loader file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732865671/Medusa%20Book/loader-dir-overview_eg6vtu.jpg) - -Learn how to create a module in [this chapter](undefined/learn/fundamentals/modules). - -```ts title="src/modules/hello/loaders/hello-world.ts" -import { - LoaderOptions, -} from "@medusajs/framework/types" - -export default async function helloWorldLoader({ - container, -}: LoaderOptions) { - const logger = container.resolve("logger") - - logger.info("[helloWorldLoader]: Hello, World!") -} -``` - -The loader file exports an async function, which is the function executed when the application loads. - -The function receives an object parameter that has a `container` property, which is the module's container that you can use to resolve resources from. In this example, you resolve the Logger utility to log a message in the terminal. - -Find the list of resources in the module's container in [this reference](undefined/resources/medusa-container-resources#module-container-resources). - -### 2. Export Loader in Module Definition - -After implementing the loader, you must export it in the module's definition in the `index.ts` file at the root of the module's directory. Otherwise, the Medusa application will not run it. - -So, to export the loader you implemented above in the `hello` module, add the following to `src/modules/hello/index.ts`: - -```ts title="src/modules/hello/index.ts" -// other imports... -import helloWorldLoader from "./loaders/hello-world" - -export default Module("hello", { - // ... - loaders: [helloWorldLoader], -}) -``` - -The second parameter of the `Module` function accepts a `loaders` property whose value is an array of loader functions. The Medusa application will execute these functions when it starts. - -### Test the Loader - -Assuming your module is [added to Medusa's configuration](undefined/learn/fundamentals/modules#4-add-module-to-medusas-configurations), you can test the loader by starting the Medusa application: - -```bash npm2yarn -npm run dev -``` - -Then, you'll find the following message logged in the terminal: - -```plain -info: [HELLO MODULE] Just started the Medusa application! -``` - -This indicates that the loader in the `hello` module ran and logged this message. - -*** - -## Example: Register Custom MongoDB Connection - -As mentioned in this chapter's introduction, loaders are most useful when you need to register a custom resource in the module's container to re-use it in other customizations in the module. - -Consider your have a MongoDB module that allows you to perform operations on a MongoDB database. - -### Prerequisites - -- [MongoDB database that you can connect to from a local machine.](https://www.mongodb.com) -- [Install the MongoDB SDK in your Medusa application.](https://www.mongodb.com/docs/drivers/node/current/quick-start/download-and-install/#install-the-node.js-driver) - -To connect to the database, you create the following loader in your module: - -```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights} -import { LoaderOptions } from "@medusajs/framework/types" -import { asValue } from "awilix" -import { MongoClient } from "mongodb" - -type ModuleOptions = { - connection_url?: string - db_name?: string -} - -export default async function mongoConnectionLoader({ - container, - options, -}: LoaderOptions) { - if (!options.connection_url) { - throw new Error(`[MONGO MDOULE]: connection_url option is required.`) - } - if (!options.db_name) { - throw new Error(`[MONGO MDOULE]: db_name option is required.`) - } - const logger = container.resolve("logger") - - try { - const clientDb = ( - await (new MongoClient(options.connection_url)).connect() - ).db(options.db_name) - - logger.info("Connected to MongoDB") - - container.register( - "mongoClient", - asValue(clientDb) - ) - } catch (e) { - logger.error( - `[MONGO MDOULE]: An error occurred while connecting to MongoDB: ${e}` - ) - } -} -``` - -The loader function accepts in its object parameter an `options` property, which is the options passed to the module in Medusa's configurations. For example: - -```ts title="medusa-config.ts" highlights={optionHighlights} -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "./src/modules/mongo", - options: { - connection_url: process.env.MONGO_CONNECTION_URL, - db_name: process.env.MONGO_DB_NAME, - }, - }, - ], -}) -``` - -Passing options is useful when your module needs informations like connection URLs or API keys, as it ensures your module can be re-usable across applications. For the MongoDB Module, you expect two options: - -- `connection_url`: the URL to connect to the MongoDB database. -- `db_name`: The name of the database to connect to. - -In the loader, you check first that these options are set before proceeding. Then, you create an instance of the MongoDB client and connect to the database specified in the options. - -After creating the client, you register it in the module's container using the container's `register` method. The method accepts two parameters: - -1. The key to register the resource under, which in this case is `mongoClient`. You'll use this name later to resolve the client. -2. The resource to register in the container, which is the MongoDB client you created. However, you don't pass the resource as-is. Instead, you need to use an `asValue` function imported from the [awilix package](https://github.com/jeffijoe/awilix), which is the package used to implement the container functionality in Medusa. - -### Use Custom Registered Resource in Module's Service - -After registering the custom MongoDB client in the module's container, you can now resolve and use it in the module's service. - -For example: - -```ts title="src/modules/mongo/service.ts" -import type { Db } from "mongodb" - -type InjectedDependencies = { - mongoClient: Db -} - -export default class MongoModuleService { - private mongoClient_: Db - - constructor({ mongoClient }: InjectedDependencies) { - this.mongoClient_ = mongoClient - } - - async createMovie({ title }: { - title: string - }) { - const moviesCol = this.mongoClient_.collection("movie") - - const insertedMovie = await moviesCol.insertOne({ - title, - }) - - const movie = await moviesCol.findOne({ - _id: insertedMovie.insertedId, - }) - - return movie - } - - async deleteMovie(id: string) { - const moviesCol = this.mongoClient_.collection("movie") - - await moviesCol.deleteOne({ - _id: { - equals: id, - }, - }) - } -} -``` - -The service `MongoModuleService` resolves the `mongoClient` resource you registered in the loader and sets it as a class property. You then use it in the `createMovie` and `deleteMovie` methods, which create and delete a document in a `movie` collection in the MongoDB database, respectively. - -Make sure to export the loader in the module's definition in the `index.ts` file at the root directory of the module: - -```ts title="src/modules/mongo/index.ts" highlights={[["9"]]} -import { Module } from "@medusajs/framework/utils" -import MongoModuleService from "./service" -import mongoConnectionLoader from "./loaders/connection" - -export const MONGO_MODULE = "mongo" - -export default Module(MONGO_MODULE, { - service: MongoModuleService, - loaders: [mongoConnectionLoader], -}) -``` - -### Test it Out - -You can test the connection out by starting the Medusa application. If it's successful, you'll see the following message logged in the terminal: - -```bash -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 about Medusa's commerce 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](undefined/learn/fundamentals/module-links), to extend a module's data models and retrieve data across modules. - -Commerce modules are built-in [modules](undefined/learn/fundamentals/modules) 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](undefined/resources/medusa-workflows-reference) and [APIs](undefined/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](undefined/resources/references/medusa-workflows/addToCartWorkflow) - -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](undefined/resources/commerce-modules) for a full list of commerce modules in Medusa. - -*** - -## Use Commerce Modules in Custom Flows - -Similar to your [custom modules](undefined/learn/fundamentals/modules), the Medusa application registers a commerce module's service in the [container](undefined/learn/fundamentals/medusa-container). 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](undefined/learn/fundamentals/workflows) (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") - -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](undefined/learn/fundamentals/module-links). - - 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. -This chapter is intended for more advanced database use-cases where you need more control over queries and operations. For basic database operations, such as creating or retrieving data of a model, use the [Service Factory](undefined/learn/fundamentals/modules/service-factory) instead. +This chapter is intended for more advanced database use-cases where you need more control over queries and operations. For basic database operations, such as creating or retrieving data of a model, use the [Service Factory](https://docs.medusajs.com/learn/fundamentals/modules/service-factory/index.html.md) instead. ## Run Queries @@ -10808,28 +10538,100 @@ class HelloModuleService { ``` +# Module Container + +In this chapter, you'll learn about the module's container and how to resolve resources in that container. + +Since modules are isolated, each module has a local container only used by the resources of that module. + +So, resources in the module, such as services or loaders, can only resolve other resources registered in the module's container. + +### List of Registered Resources + +Find a list of resources or dependencies registered in a module's container in [this Development Resources reference](https://docs.medusajs.com/resources/medusa-container-resources/index.html.md). + +*** + +## Resolve Resources + +### Services + +A service's constructor accepts as a first parameter an object used to resolve resources registered in the module's container. + +For example: + +```ts highlights={[["4"], ["10"]]} +import { Logger } from "@medusajs/framework/types" + +type InjectedDependencies = { + logger: Logger +} + +export default class HelloModuleService { + protected logger_: Logger + + constructor({ logger }: InjectedDependencies) { + this.logger_ = logger + + this.logger_.info("[HelloModuleService]: Hello World!") + } + + // ... +} +``` + +### Loader + +A loader function accepts as a parameter an object having the property `container`. Its value is the module's container used to resolve resources. + +For example: + +```ts highlights={[["9"]]} +import { + LoaderOptions, +} from "@medusajs/framework/types" +import { + ContainerRegistrationKeys, +} from "@medusajs/framework/utils" + +export default async function helloWorldLoader({ + container, +}: LoaderOptions) { + const logger = container.resolve(ContainerRegistrationKeys.LOGGER) + + logger.info("[helloWorldLoader]: Hello, World!") +} +``` + + # 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](undefined/learn/fundamentals/module-links), to extend a module's data models and retrieve data across 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? -The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](undefined/learn/fundamentals/modules). +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 -A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](undefined/learn/fundamentals/modules). +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](undefined/learn/fundamentals/module-links). +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). *** @@ -11031,206 +10833,275 @@ The `configModule` has a `modules` property that includes all registered modules If its value is not a `boolean`, set the service's options to the module configuration's `options` property. -# Module Options +# Modules Directory Structure -In this chapter, you’ll learn about passing options to your module from the Medusa application’s configurations and using them in the module’s resources. +In this document, you'll learn about the expected files and directories in your module. -## What are Module Options? +![Module Directory Structure Example](https://res.cloudinary.com/dza7lstvk/image/upload/v1714379976/Medusa%20Book/modules-dir-overview_nqq7ne.jpg) -A module can receive options to customize or configure its functionality. For example, if you’re creating a module that integrates a third-party service, you’ll want to receive the integration credentials in the options rather than adding them directly in your code. +## index.ts + +The `index.ts` file in the root of your module's directory is the only required file. It must export the module's definition as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). *** -## How to Pass Options to a Module? +## service.ts -To pass options to a module, add an `options` property to the module’s configuration in `medusa-config.ts`. +A module must have a main service. It's created in the `service.ts` file at the root of your module directory as explained in a [previous chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). -For example: +*** -```ts title="medusa-config.ts" -module.exports = defineConfig({ - // ... - modules: [ - { - resolve: "./src/modules/hello", - options: { - capitalize: true, - }, - }, - ], -}) -``` +## Other Directories -The `options` property’s value is an object. You can pass any properties you want. +The following directories are optional and their content are explained more in the following chapters: -### Pass Options to a Module in a Plugin +- `models`: Holds the data models representing tables in the database. +- `migrations`: Holds the migration files used to reflect changes on the database. +- `loaders`: Holds the scripts to run on the Medusa application's start-up. -If your module is part of a plugin, you can pass options to the module in the plugin’s configuration. -For example: +# Loaders -```ts title="medusa-config.ts" -import { defineConfig } from "@medusajs/framework/utils" -module.exports = defineConfig({ - plugins: [ - { - resolve: "@myorg/plugin-name", - options: { - capitalize: true, - }, - }, - ], -}) -``` +In this chapter, you’ll learn about loaders and how to use them. -The `options` property in the plugin configuration is passed to all modules in a plugin. +## What is a Loader? -*** +When building a commerce application, you'll often need to execute an action the first time the application starts. For example, if your application needs to connect to databases other than Medusa's PostgreSQL database, you might need to establish a connection on application startup. -## Access Module Options in Main Service +In Medusa, you can execute an action when the application starts using a loader. A loader is a function exported by a [module](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md), which is a package of business logic for a single domain. When the Medusa application starts, it executes all loaders exported by configured modules. -The module’s main service receives the module options as a second parameter. +Loaders are useful to register custom resources, such as database connections, in the [module's container](https://docs.medusajs.com/learn/fundamentals/modules/container/index.html.md), which is similar to the [Medusa container](https://docs.medusajs.com/learn/fundamentals/medusa-container/index.html.md) but includes only [resources available to the module](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). Modules are isolated, so they can't access resources outside of them, such as a service in another module. -For example: +Medusa isolates modules to ensure that they're re-usable across applications, aren't tightly coupled to other resources, and don't have implications when integrated into the Medusa application. Learn more about why modules are isolated in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/isolation/index.html.md), and check out [this reference for the list of resources in the module's container](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). -```ts title="src/modules/hello/service.ts" highlights={[["12"], ["14", "options?: ModuleOptions"], ["17"], ["18"], ["19"]]} -import { MedusaService } from "@medusajs/framework/utils" -import MyCustom from "./models/my-custom" +*** -// recommended to define type in another file -type ModuleOptions = { - capitalize?: boolean -} +## How to Create a Loader? -export default class HelloModuleService extends MedusaService({ - MyCustom, -}){ - protected options_: ModuleOptions +### 1. Implement Loader Function - constructor({}, options?: ModuleOptions) { - super(...arguments) +You create a loader function in a TypeScript or JavaScript file under a module's `loaders` directory. - this.options_ = options || { - capitalize: false, - } - } +For example, consider you have a `hello` module, you can create a loader at `src/modules/hello/loaders/hello-world.ts` with the following content: - // ... -} -``` +![Example of loader file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732865671/Medusa%20Book/loader-dir-overview_eg6vtu.jpg) -*** +Learn how to create a module in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/index.html.md). -## Access Module Options in Loader - -The object that a module’s loaders receive as a parameter has an `options` property holding the module's options. - -For example: - -```ts title="src/modules/hello/loaders/hello-world.ts" highlights={[["11"], ["12", "ModuleOptions", "The type of expected module options."], ["16"]]} +```ts title="src/modules/hello/loaders/hello-world.ts" import { LoaderOptions, } from "@medusajs/framework/types" -// recommended to define type in another file -type ModuleOptions = { - capitalize?: boolean -} - export default async function helloWorldLoader({ - options, -}: LoaderOptions) { - - console.log( - "[HELLO MODULE] Just started the Medusa application!", - options - ) + container, +}: LoaderOptions) { + const logger = container.resolve("logger") + + logger.info("[helloWorldLoader]: Hello, World!") } ``` +The loader file exports an async function, which is the function executed when the application loads. + +The function receives an object parameter that has a `container` property, which is the module's container that you can use to resolve resources from. In this example, you resolve the Logger utility to log a message in the terminal. + +Find the list of resources in the module's container in [this reference](https://docs.medusajs.com/resources/medusa-container-resources#module-container-resources/index.html.md). + +### 2. Export Loader in Module Definition + +After implementing the loader, you must export it in the module's definition in the `index.ts` file at the root of the module's directory. Otherwise, the Medusa application will not run it. + +So, to export the loader you implemented above in the `hello` module, add the following to `src/modules/hello/index.ts`: + +```ts title="src/modules/hello/index.ts" +// other imports... +import helloWorldLoader from "./loaders/hello-world" + +export default Module("hello", { + // ... + loaders: [helloWorldLoader], +}) +``` + +The second parameter of the `Module` function accepts a `loaders` property whose value is an array of loader functions. The Medusa application will execute these functions when it starts. + +### Test the Loader + +Assuming your module is [added to Medusa's configuration](https://docs.medusajs.com/learn/fundamentals/modules#4-add-module-to-medusas-configurations/index.html.md), you can test the loader by starting the Medusa application: + +```bash npm2yarn +npm run dev +``` + +Then, you'll find the following message logged in the terminal: + +```plain +info: [HELLO MODULE] Just started the Medusa application! +``` + +This indicates that the loader in the `hello` module ran and logged this message. + *** -## Validate Module Options +## Example: Register Custom MongoDB Connection -If you expect a certain option and want to throw an error if it's not provided or isn't valid, it's recommended to perform the validation in a loader. The module's service is only instantiated when it's used, whereas the loader runs the when the Medusa application starts. +As mentioned in this chapter's introduction, loaders are most useful when you need to register a custom resource in the module's container to re-use it in other customizations in the module. -So, by performing the validation in the loader, you ensure you can throw an error at an early point, rather than when the module is used. +Consider your have a MongoDB module that allows you to perform operations on a MongoDB database. -For example, to validate that the Hello Module received an `apiKey` option, create the loader `src/modules/loaders/validate.ts`: +### Prerequisites -```ts title="src/modules/hello/loaders/validate.ts" +- [MongoDB database that you can connect to from a local machine.](https://www.mongodb.com) +- [Install the MongoDB SDK in your Medusa application.](https://www.mongodb.com/docs/drivers/node/current/quick-start/download-and-install/#install-the-node.js-driver) + +To connect to the database, you create the following loader in your module: + +```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights} import { LoaderOptions } from "@medusajs/framework/types" -import { MedusaError } from "@medusajs/framework/utils" +import { asValue } from "awilix" +import { MongoClient } from "mongodb" -// recommended to define type in another file type ModuleOptions = { - apiKey?: string + connection_url?: string + db_name?: string } -export default async function validationLoader({ +export default async function mongoConnectionLoader({ + container, options, }: LoaderOptions) { - if (!options.apiKey) { - throw new MedusaError( - MedusaError.Types.INVALID_DATA, - "Hello Module requires an apiKey option." + if (!options.connection_url) { + throw new Error(`[MONGO MDOULE]: connection_url option is required.`) + } + if (!options.db_name) { + throw new Error(`[MONGO MDOULE]: db_name option is required.`) + } + const logger = container.resolve("logger") + + try { + const clientDb = ( + await (new MongoClient(options.connection_url)).connect() + ).db(options.db_name) + + logger.info("Connected to MongoDB") + + container.register( + "mongoClient", + asValue(clientDb) + ) + } catch (e) { + logger.error( + `[MONGO MDOULE]: An error occurred while connecting to MongoDB: ${e}` ) } } ``` -Then, export the loader in the module's definition file, as explained in [this chapter](undefined/learn/fundamentals/modules/loaders): - -```ts title="src/modules/hello/index.ts" -// other imports... -import validationLoader from "./loaders/validate" +The loader function accepts in its object parameter an `options` property, which is the options passed to the module in Medusa's configurations. For example: -export default Module("hello", { +```ts title="medusa-config.ts" highlights={optionHighlights} +module.exports = defineConfig({ // ... - loaders: [validationLoader], + modules: [ + { + resolve: "./src/modules/mongo", + options: { + connection_url: process.env.MONGO_CONNECTION_URL, + db_name: process.env.MONGO_DB_NAME, + }, + }, + ], }) ``` -Now, when the Medusa application starts, the loader will run, validating the module's options and throwing an error if the `apiKey` option is missing. +Passing options is useful when your module needs informations like connection URLs or API keys, as it ensures your module can be re-usable across applications. For the MongoDB Module, you expect two options: +- `connection_url`: the URL to connect to the MongoDB database. +- `db_name`: The name of the database to connect to. -# Service Constraints +In the loader, you check first that these options are set before proceeding. Then, you create an instance of the MongoDB client and connect to the database specified in the options. -This chapter lists constraints to keep in mind when creating a service. +After creating the client, you register it in the module's container using the container's `register` method. The method accepts two parameters: -## Use Async Methods +1. The key to register the resource under, which in this case is `mongoClient`. You'll use this name later to resolve the client. +2. The resource to register in the container, which is the MongoDB client you created. However, you don't pass the resource as-is. Instead, you need to use an `asValue` function imported from the [awilix package](https://github.com/jeffijoe/awilix), which is the package used to implement the container functionality in Medusa. -Medusa wraps service method executions to inject useful context or transactions. However, since Medusa can't detect whether the method is asynchronous, it always executes methods in the wrapper with the `await` keyword. +### Use Custom Registered Resource in Module's Service -For example, if you have a synchronous `getMessage` method, and you use it in other resources like workflows, Medusa executes it as an async method: +After registering the custom MongoDB client in the module's container, you can now resolve and use it in the module's service. -```ts -await helloModuleService.getMessage() -``` +For example: -So, make sure your service's methods are always async to avoid unexpected errors or behavior. +```ts title="src/modules/mongo/service.ts" +import type { Db } from "mongodb" -```ts highlights={[["8", "", "Method must be async."], ["13", "async", "Correct way of defining the method."]]} -import { MedusaService } from "@medusajs/framework/utils" -import MyCustom from "./models/my-custom" +type InjectedDependencies = { + mongoClient: Db +} -class HelloModuleService extends MedusaService({ - MyCustom, -}){ - // Don't - getMessage(): string { - return "Hello, World!" +export default class MongoModuleService { + private mongoClient_: Db + + constructor({ mongoClient }: InjectedDependencies) { + this.mongoClient_ = mongoClient } - // Do - async getMessage(): Promise { - return "Hello, World!" + async createMovie({ title }: { + title: string + }) { + const moviesCol = this.mongoClient_.collection("movie") + + const insertedMovie = await moviesCol.insertOne({ + title, + }) + + const movie = await moviesCol.findOne({ + _id: insertedMovie.insertedId, + }) + + return movie + } + + async deleteMovie(id: string) { + const moviesCol = this.mongoClient_.collection("movie") + + await moviesCol.deleteOne({ + _id: { + equals: id, + }, + }) } } +``` -export default HelloModuleService +The service `MongoModuleService` resolves the `mongoClient` resource you registered in the loader and sets it as a class property. You then use it in the `createMovie` and `deleteMovie` methods, which create and delete a document in a `movie` collection in the MongoDB database, respectively. + +Make sure to export the loader in the module's definition in the `index.ts` file at the root directory of the module: + +```ts title="src/modules/mongo/index.ts" highlights={[["9"]]} +import { Module } from "@medusajs/framework/utils" +import MongoModuleService from "./service" +import mongoConnectionLoader from "./loaders/connection" + +export const MONGO_MODULE = "mongo" + +export default Module(MONGO_MODULE, { + service: MongoModuleService, + loaders: [mongoConnectionLoader], +}) +``` + +### Test it Out + +You can test the connection out by starting the Medusa application. If it's successful, you'll see the following message logged in the terminal: + +```bash +info: Connected to MongoDB ``` +You can now resolve the MongoDB Module's main service in your customizations to perform operations on the MongoDB database. + # Service Factory @@ -11279,7 +11150,7 @@ The method's names are the operation's name, suffixed by the data model's key in For example, the following methods are generated for the service above: -Find a complete reference of each of the methods in [this documentation](undefined/resources/service-factory-reference) +Find a complete reference of each of the methods in [this documentation](https://docs.medusajs.com/resources/service-factory-reference/index.html.md) ### listMyCustoms @@ -11407,384 +11278,244 @@ export default HelloModuleService ``` -# Create a Plugin +# Module Options -In this chapter, you'll learn how to create a Medusa plugin and publish it. +In this chapter, you’ll learn about passing options to your module from the Medusa application’s configurations and using them in the module’s resources. -A [plugin](undefined/learn/fundamentals/plugins) 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. +## What are Module Options? -Plugins are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). +A module can receive options to customize or configure its functionality. For example, if you’re creating a module that integrates a third-party service, you’ll want to receive the integration credentials in the options rather than adding them directly in your code. -## 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. +## How to Pass Options to a Module? -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: +To pass options to a module, add an `options` property to the module’s configuration in `medusa-config.ts`. -```bash -npx create-medusa-app my-plugin --plugin +For example: + +```ts title="medusa-config.ts" +module.exports = defineConfig({ + // ... + modules: [ + { + resolve: "./src/modules/hello", + options: { + capitalize: true, + }, + }, + ], +}) ``` -This will create a new Medusa plugin project in the `my-plugin` directory. +The `options` property’s value is an object. You can pass any properties you want. -### Plugin Directory Structure +### Pass Options to a Module in a Plugin -After the installation is done, the plugin structure will look like this: +If your module is part of a plugin, you can pass options to the module in the plugin’s configuration. -![Directory structure of a plugin project](https://res.cloudinary.com/dza7lstvk/image/upload/v1737019441/Medusa%20Book/project-dir_q4xtri.jpg) +For example: -- `src/`: Contains the Medusa customizations. -- `src/admin`: Contains [admin extensions](undefined/learn/fundamentals/admin). -- `src/api`: Contains [API routes](undefined/learn/fundamentals/api-routes) and [middlewares](undefined/learn/fundamentals/api-routes/middlewares). You can add store, admin, or any custom API routes. -- `src/jobs`: Contains [scheduled jobs](undefined/learn/fundamentals/scheduled-jobs). -- `src/links`: Contains [module links](undefined/learn/fundamentals/module-links). -- `src/modules`: Contains [modules](undefined/learn/fundamentals/modules). -- `src/provider`: Contains [module providers](#create-module-providers). -- `src/subscribers`: Contains [subscribers](undefined/learn/fundamentals/events-and-subscribers). -- `src/workflows`: Contains [workflows](undefined/learn/fundamentals/workflows). You can also add [hooks](undefined/learn/fundamentals/workflows/add-workflow-hook) 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. +```ts title="medusa-config.ts" +import { defineConfig } from "@medusajs/framework/utils" +module.exports = defineConfig({ + plugins: [ + { + resolve: "@myorg/plugin-name", + options: { + capitalize: true, + }, + }, + ], +}) +``` + +The `options` property in the plugin configuration is passed to all modules in a plugin. *** -## 2. Prepare Plugin +## Access Module Options in Main Service -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. +The module’s main service receives the module options as a second parameter. For example: -```json title="package.json" -{ - "name": "@myorg/plugin-name", - // ... +```ts title="src/modules/hello/service.ts" highlights={[["12"], ["14", "options?: ModuleOptions"], ["17"], ["18"], ["19"]]} +import { MedusaService } from "@medusajs/framework/utils" +import MyCustom from "./models/my-custom" + +// recommended to define type in another file +type ModuleOptions = { + capitalize?: boolean } -``` -In addition, make sure that the `keywords` field in `package.json` includes the keyword `medusa-plugin` and `medusa-v2`. This helps Medusa list community plugins on the Medusa website: +export default class HelloModuleService extends MedusaService({ + MyCustom, +}){ + protected options_: ModuleOptions + + constructor({}, options?: ModuleOptions) { + super(...arguments) + + this.options_ = options || { + capitalize: false, + } + } -```json title="package.json" -{ - "keywords": [ - "medusa-plugin", - "medusa-v2" - ], // ... } ``` *** -## 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 +## Access Module Options in Loader -### Prerequisites +The object that a module’s loaders receive as a parameter has an `options` property holding the module's options. -- [Medusa application installed.](undefined/learn/installation) +For example: -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. +```ts title="src/modules/hello/loaders/hello-world.ts" highlights={[["11"], ["12", "ModuleOptions", "The type of expected module options."], ["16"]]} +import { + LoaderOptions, +} from "@medusajs/framework/types" -To publish the plugin to the local registry, run the following command in your plugin project: +// recommended to define type in another file +type ModuleOptions = { + capitalize?: boolean +} -```bash title="Plugin project" -npx medusa plugin:publish +export default async function helloWorldLoader({ + options, +}: LoaderOptions) { + + console.log( + "[HELLO MODULE] Just started the Medusa application!", + options + ) +} ``` -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: +## Validate Module Options -```bash title="Medusa application" -cd ~/path/to/medusa-app -``` +If you expect a certain option and want to throw an error if it's not provided or isn't valid, it's recommended to perform the validation in a loader. The module's service is only instantiated when it's used, whereas the loader runs the when the Medusa application starts. -Make sure to replace `~/path/to/medusa-app` with the path to your Medusa application. +So, by performing the validation in the loader, you ensure you can throw an error at an early point, rather than when the module is used. -Then, if your project was created before v2.3.1 of Medusa, make sure to install `yalc` as a development dependency: +For example, to validate that the Hello Module received an `apiKey` option, create the loader `src/modules/loaders/validate.ts`: -```bash npm2yarn title="Medusa application" -npm install --save-dev yalc -``` +```ts title="src/modules/hello/loaders/validate.ts" +import { LoaderOptions } from "@medusajs/framework/types" +import { MedusaError } from "@medusajs/framework/utils" -After that, run the following Medusa CLI command to install the plugin: +// recommended to define type in another file +type ModuleOptions = { + apiKey?: string +} -```bash title="Medusa application" -npx medusa plugin:add @myorg/plugin-name +export default async function validationLoader({ + options, +}: LoaderOptions) { + if (!options.apiKey) { + throw new MedusaError( + MedusaError.Types.INVALID_DATA, + "Hello Module requires an apiKey option." + ) + } +} ``` -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`. +Then, export the loader in the module's definition file, as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/modules/loaders/index.html.md): -Add the plugin to the `plugins` array in the `medusa-config.ts` file: +```ts title="src/modules/hello/index.ts" +// other imports... +import validationLoader from "./loaders/validate" -```ts title="medusa-config.ts" highlights={pluginHighlights} -module.exports = defineConfig({ +export default Module("hello", { // ... - plugins: [ - { - resolve: "@myorg/plugin-name", - options: {}, - }, - ], + loaders: [validationLoader], }) ``` -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. +Now, when the Medusa application starts, the loader will run, validating the module's options and throwing an error if the `apiKey` option is missing. -For example: -```ts title="medusa-config.ts" highlights={pluginOptionsHighlight} -module.exports = defineConfig({ - // ... - plugins: [ - { - resolve: "@myorg/plugin-name", - options: { - apiKey: true, - }, - }, - ], -}) -``` +# Service Constraints -The `options` property in the plugin configuration is passed to all modules in the plugin. Learn more about module options in [this chapter](undefined/learn/fundamentals/modules/options). +This chapter lists constraints to keep in mind when creating a service. -### Watch Plugin Changes During Development +## Use Async Methods -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. +Medusa wraps service method executions to inject useful context or transactions. However, since Medusa can't detect whether the method is asynchronous, it always executes methods in the wrapper with the `await` keyword. -To do that, run the following command in your plugin project: +For example, if you have a synchronous `getMessage` method, and you use it in other resources like workflows, Medusa executes it as an async method: -```bash title="Plugin project" -npx medusa plugin:develop +```ts +await helloModuleService.getMessage() ``` -This command will: +So, make sure your service's methods are always async to avoid unexpected errors or behavior. -- 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. +```ts highlights={[["8", "", "Method must be async."], ["13", "async", "Correct way of defining the method."]]} +import { MedusaService } from "@medusajs/framework/utils" +import MyCustom from "./models/my-custom" -### Start Medusa Application +class HelloModuleService extends MedusaService({ + MyCustom, +}){ + // Don't + getMessage(): string { + return "Hello, World!" + } -You can start your Medusa application's development server to test out your plugin: + // Do + async getMessage(): Promise { + return "Hello, World!" + } +} -```bash npm2yarn title="Medusa application" -npm run dev +export default HelloModuleService ``` -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 +# Scheduled Jobs Number of Executions -You can now build your plugin's customizations. The following guide explains how to build different customizations in your plugin. +In this chapter, you'll learn how to set a limit on the number of times a scheduled job is executed. -- [Create a module](undefined/learn/fundamentals/modules) -- [Create a module link](undefined/learn/fundamentals/module-links) -- [Create a workflow](undefined/learn/fundamentals/workflows) -- [Add a workflow hook](undefined/learn/fundamentals/workflows/add-workflow-hook) -- [Create an API route](undefined/learn/fundamentals/api-routes) -- [Add a subscriber](undefined/learn/fundamentals/events-and-subscribers) -- [Add a scheduled job](undefined/learn/fundamentals/scheduled-jobs) -- [Add an admin widget](undefined/learn/fundamentals/admin/widgets) -- [Add an admin UI route](undefined/learn/fundamentals/admin/ui-routes) +## numberOfExecutions Option -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). +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. -### Generating Migrations for Modules +For example: -During your development, you may need to generate migrations for modules in your plugin. To do that, use the `plugin:db:generate` command: +```ts highlights={highlights} +export default async function myCustomJob() { + console.log("I'll be executed three times only.") +} -```bash title="Plugin project" -npx medusa plugin:db:generate +export const config = { + name: "hello-world", + // execute every minute + schedule: "* * * * *", + numberOfExecutions: 3, +} ``` -This command generates migrations for all modules in the plugin. You can then run these migrations on the Medusa application that the plugin is installed in using the `db:migrate` command: +The above scheduled job has the `numberOfExecutions` configuration set to `3`. -```bash title="Medusa application" -npx medusa db:migrate -``` +So, it'll only execute 3 times, each every minute, then it won't be executed anymore. -### Importing Module Resources +If you restart the Medusa application, the scheduled job will be executed again until reaching the number of executions specified. -Your plugin project should have the following exports in `package.json`: -```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", - "./*": "./.medusa/server/src/*.js" - } -} -``` +# Access Workflow Errors -Aside from the `./package.json` and `./providers`, these exports are only a recommendation. You can cherry-pick the files and directories you want to export. +In this chapter, you’ll learn how to access errors that occur during a workflow’s execution. -The plugin exports the following files and directories: +## How to Access Workflow Errors? -- `./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 allows you to register the plugin's providers in the Medusa application. -- `./*`: Any other files in the plugin's `src` directory. - -With these exports, you can import the plugin's resources in the Medusa application's code like this: - -`@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" -``` - -And you can register a module provider in the Medusa application's `medusa-config.ts` like this: - -```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. - -### Create Module Providers - -To learn how to create module providers, refer to the following guides: - -- [File Module Provider](undefined/resources/references/file-provider-module) -- [Notification Module Provider](undefined/resources/references/notification-provider-module) -- [Auth Module Provider](undefined/resources/references/auth/provider) -- [Payment Module Provider](undefined/resources/references/payment/provider) -- [Fulfillment Module Provider](undefined/resources/references/fulfillment/provider) -- [Tax Module Provider](undefined/resources/references/tax/provider) - -*** - -## 5. Publish Plugin to NPM - -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](undefined/learn/update#update-plugin-project). - -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. - - -# Access Workflow Errors - -In this chapter, you’ll learn how to access errors that occur during a workflow’s execution. - -## How to Access Workflow Errors? - -By default, when an error occurs in a workflow, it throws that error, and the execution stops. +By default, when an error occurs in a workflow, it throws that error, and the execution stops. You can configure the workflow to return the errors instead so that you can access and handle them differently. @@ -11823,6 +11554,75 @@ The object passed to the `run` method accepts a `throwOnError` property. When di The value of `errors` is an array of error objects. Each object has an `error` property, whose value is the name or text of the thrown error. +# Expose a Workflow Hook + +In this chapter, you'll learn how to expose a hook in your workflow. + +## When to Expose a Hook + +Your workflow is reusable in other applications, and you allow performing an external action at some point in your workflow. + +Your workflow isn't reusable by other applications. Use a step that performs what a hook handler would instead. + +*** + +## How to Expose a Hook in a Workflow? + +To expose a hook in your workflow, use `createHook` from the Workflows SDK. + +For example: + +```ts title="src/workflows/my-workflow/index.ts" highlights={hookHighlights} +import { + createStep, + createHook, + createWorkflow, + WorkflowResponse, +} from "@medusajs/framework/workflows-sdk" +import { createProductStep } from "./steps/create-product" + +export const myWorkflow = createWorkflow( + "my-workflow", + function (input) { + const product = createProductStep(input) + const productCreatedHook = createHook( + "productCreated", + { productId: product.id } + ) + + return new WorkflowResponse(product, { + hooks: [productCreatedHook], + }) + } +) +``` + +The `createHook` function accepts two parameters: + +1. The first is a string indicating the hook's name. You use this to consume the hook later. +2. The second is the input to pass to the hook handler. + +The workflow must also pass an object having a `hooks` property as a second parameter to the `WorkflowResponse` constructor. Its value is an array of the workflow's hooks. + +### How to Consume the Hook? + +To consume the hook of the workflow, create the file `src/workflows/hooks/my-workflow.ts` with the following content: + +```ts title="src/workflows/hooks/my-workflow.ts" highlights={handlerHighlights} +import { myWorkflow } from "../my-workflow" + +myWorkflow.hooks.productCreated( + async ({ productId }, { container }) => { + // TODO perform an action + } +) +``` + +The hook is available on the workflow's `hooks` property using its name `productCreated`. + +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. @@ -12077,75 +11877,6 @@ The `StepResponse.permanentFailure` fails the step and its workflow, triggering 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. -# Expose a Workflow Hook - -In this chapter, you'll learn how to expose a hook in your workflow. - -## When to Expose a Hook - -Your workflow is reusable in other applications, and you allow performing an external action at some point in your workflow. - -Your workflow isn't reusable by other applications. Use a step that performs what a hook handler would instead. - -*** - -## How to Expose a Hook in a Workflow? - -To expose a hook in your workflow, use `createHook` from the Workflows SDK. - -For example: - -```ts title="src/workflows/my-workflow/index.ts" highlights={hookHighlights} -import { - createStep, - createHook, - createWorkflow, - WorkflowResponse, -} from "@medusajs/framework/workflows-sdk" -import { createProductStep } from "./steps/create-product" - -export const myWorkflow = createWorkflow( - "my-workflow", - function (input) { - const product = createProductStep(input) - const productCreatedHook = createHook( - "productCreated", - { productId: product.id } - ) - - return new WorkflowResponse(product, { - hooks: [productCreatedHook], - }) - } -) -``` - -The `createHook` function accepts two parameters: - -1. The first is a string indicating the hook's name. You use this to consume the hook later. -2. The second is the input to pass to the hook handler. - -The workflow must also pass an object having a `hooks` property as a second parameter to the `WorkflowResponse` constructor. Its value is an array of the workflow's hooks. - -### How to Consume the Hook? - -To consume the hook of the workflow, create the file `src/workflows/hooks/my-workflow.ts` with the following content: - -```ts title="src/workflows/hooks/my-workflow.ts" highlights={handlerHighlights} -import { myWorkflow } from "../my-workflow" - -myWorkflow.hooks.productCreated( - async ({ productId }, { container }) => { - // TODO perform an action - } -) -``` - -The hook is available on the workflow's `hooks` property using its name `productCreated`. - -You invoke the hook, passing a step function (the hook handler) as a parameter. - - # 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. @@ -12339,7 +12070,7 @@ const myWorkflow = createWorkflow( You can’t directly manipulate variables within the workflow's constructor function. -Learn more about why you can't manipulate variables [in this chapter](undefined/learn/fundamentals/workflows/variable-manipulation) +Learn more about why you can't manipulate variables [in this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md) Instead, use `transform` from the Workflows SDK: @@ -12383,7 +12114,7 @@ When you use `new Date()` in a workflow's constructor function, the date is eval Instead, create the date using `transform`. -Learn more about how Medusa creates an internal representation of a workflow [in this chapter](undefined/learn/fundamentals/workflows/variable-manipulation). +Learn more about how Medusa creates an internal representation of a workflow [in this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md). For example: @@ -12415,7 +12146,7 @@ const myWorkflow = createWorkflow( You can't use if-conditions in a workflow. -Learn more about why you can't use if-conditions [in this chapter](undefined/learn/fundamentals/workflows/conditions#why-if-conditions-arent-allowed-in-workflows) +Learn more about why you can't use if-conditions [in this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions#why-if-conditions-arent-allowed-in-workflows/index.html.md) Instead, use when-then from the Workflows SDK: @@ -12442,13 +12173,13 @@ const myWorkflow = createWorkflow( }) ``` -You can also pair multiple `when-then` blocks to implement an `if-else` condition as explained in [this chapter](undefined/learn/fundamentals/workflows/conditions). +You can also pair multiple `when-then` blocks to implement an `if-else` condition as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md). ### No Conditional Operators You can't use conditional operators in a workflow, such as `??` or `||`. -Learn more about why you can't use conditional operators [in this chapter](undefined/learn/fundamentals/workflows/conditions#why-if-conditions-arent-allowed-in-workflows) +Learn more about why you can't use conditional operators [in this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions#why-if-conditions-arent-allowed-in-workflows/index.html.md) Instead, use `transform` to store the desired value in a variable. @@ -12653,6 +12384,136 @@ const step1 = createStep( ``` +# Execute Another Workflow + +In this chapter, you'll learn how to execute a workflow in another. + +## Execute in a Workflow + +To execute a workflow in another, use the `runAsStep` method that every workflow has. + +For example: + +```ts highlights={workflowsHighlights} collapsibleLines="1-7" expandMoreButton="Show Imports" +import { + createWorkflow, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" + +const workflow = createWorkflow( + "hello-world", + async (input) => { + const products = createProductsWorkflow.runAsStep({ + input: { + products: [ + // ... + ], + }, + }) + + // ... + } +) +``` + +Instead of invoking the workflow and passing it the container, you use its `runAsStep` method and pass it an object as a parameter. + +The object has an `input` property to pass input to the workflow. + +*** + +## Preparing Input Data + +If you need to perform some data manipulation to prepare the other workflow's input data, use `transform` from the Workflows SDK. + +Learn about transform in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/variable-manipulation/index.html.md). + +For example: + +```ts highlights={transformHighlights} collapsibleLines="1-12" +import { + createWorkflow, + transform, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" + +type WorkflowInput = { + title: string +} + +const workflow = createWorkflow( + "hello-product", + async (input: WorkflowInput) => { + const createProductsData = transform({ + input, + }, (data) => [ + { + title: `Hello ${data.input.title}`, + }, + ]) + + const products = createProductsWorkflow.runAsStep({ + input: { + products: createProductsData, + }, + }) + + // ... + } +) +``` + +In this example, you use the `transform` function to prepend `Hello` to the title of the product. Then, you pass the result as an input to the `createProductsWorkflow`. + +*** + +## Run Workflow Conditionally + +To run a workflow in another based on a condition, use when-then from the Workflows SDK. + +Learn about when-then in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md). + +For example: + +```ts highlights={whenHighlights} collapsibleLines="1-16" +import { + createWorkflow, + when, +} from "@medusajs/framework/workflows-sdk" +import { + createProductsWorkflow, +} from "@medusajs/medusa/core-flows" +import { + CreateProductWorkflowInputDTO, +} from "@medusajs/framework/types" + +type WorkflowInput = { + product?: CreateProductWorkflowInputDTO + should_create?: boolean +} + +const workflow = createWorkflow( + "hello-product", + async (input: WorkflowInput) => { + const product = when(input, ({ should_create }) => should_create) + .then(() => { + return createProductsWorkflow.runAsStep({ + input: { + products: [input.product], + }, + }) + }) + } +) +``` + +In this example, you use when-then to run the `createProductsWorkflow` only if `should_create` (passed in the `input`) is enabled. + + # Long-Running Workflows In this chapter, you’ll learn what a long-running workflow is and how to configure it. @@ -12723,7 +12584,7 @@ The second step has in its configuration object `async` set to `true` and it doe So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second step. -A workflow is also considered long-running if one of its steps has their `retryInterval` option set as explained in [this chapter](undefined/learn/fundamentals/workflows/retry-failed-steps). +A workflow is also considered long-running if one of its steps has their `retryInterval` option set as explained in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/retry-failed-steps/index.html.md). *** @@ -12866,7 +12727,7 @@ You use this step in another workflow that changes the status of an async step i To access the status and result of a long-running workflow execution, use the `subscribe` and `unsubscribe` methods of the Workflow Engine Module's main service. -To retrieve the workflow execution's details at a later point, you must enable [storing the workflow's executions](undefined/learn/fundamentals/workflows/store-executions). +To retrieve the workflow execution's details at a later point, you must enable [storing the workflow's executions](https://docs.medusajs.com/learn/fundamentals/workflows/store-executions/index.html.md). For example: @@ -12936,271 +12797,11 @@ However, instead of the `subscriber` property, it requires a `subscriberOrId` pr ## Example: Restaurant-Delivery Recipe -To find a full example of a long-running workflow, refer to the [restaurant-delivery recipe](undefined/resources/recipes/marketplace/examples/restaurant-delivery). +To find a full example of a long-running workflow, refer to the [restaurant-delivery recipe](https://docs.medusajs.com/resources/recipes/marketplace/examples/restaurant-delivery/index.html.md). In the recipe, you use a long-running workflow that moves an order from placed to completed. The workflow waits for the restaurant to accept the order, the driver to pick up the order, and other external actions. -# Execute Another Workflow - -In this chapter, you'll learn how to execute a workflow in another. - -## Execute in a Workflow - -To execute a workflow in another, use the `runAsStep` method that every workflow has. - -For example: - -```ts highlights={workflowsHighlights} collapsibleLines="1-7" expandMoreButton="Show Imports" -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -const workflow = createWorkflow( - "hello-world", - async (input) => { - const products = createProductsWorkflow.runAsStep({ - input: { - products: [ - // ... - ], - }, - }) - - // ... - } -) -``` - -Instead of invoking the workflow and passing it the container, you use its `runAsStep` method and pass it an object as a parameter. - -The object has an `input` property to pass input to the workflow. - -*** - -## Preparing Input Data - -If you need to perform some data manipulation to prepare the other workflow's input data, use `transform` from the Workflows SDK. - -Learn about transform in [this chapter](undefined/learn/fundamentals/workflows/variable-manipulation). - -For example: - -```ts highlights={transformHighlights} collapsibleLines="1-12" -import { - createWorkflow, - transform, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -type WorkflowInput = { - title: string -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const createProductsData = transform({ - input, - }, (data) => [ - { - title: `Hello ${data.input.title}`, - }, - ]) - - const products = createProductsWorkflow.runAsStep({ - input: { - products: createProductsData, - }, - }) - - // ... - } -) -``` - -In this example, you use the `transform` function to prepend `Hello` to the title of the product. Then, you pass the result as an input to the `createProductsWorkflow`. - -*** - -## Run Workflow Conditionally - -To run a workflow in another based on a condition, use when-then from the Workflows SDK. - -Learn about when-then in [this chapter](undefined/learn/fundamentals/workflows/conditions). - -For example: - -```ts highlights={whenHighlights} collapsibleLines="1-16" -import { - createWorkflow, - when, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" -import { - CreateProductWorkflowInputDTO, -} from "@medusajs/framework/types" - -type WorkflowInput = { - product?: CreateProductWorkflowInputDTO - should_create?: boolean -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const product = when(input, ({ should_create }) => should_create) - .then(() => { - return createProductsWorkflow.runAsStep({ - input: { - products: [input.product], - }, - }) - }) - } -) -``` - -In this example, you use when-then to run the `createProductsWorkflow` only if `should_create` (passed in the `input`) is enabled. - - -# Multiple Step Usage in Workflow - -In this chapter, you'll learn how to execute a workflow in another. - -## Execute in a Workflow - -To execute a workflow in another, use the `runAsStep` method that every workflow has. - -For example: - -```ts highlights={workflowsHighlights} collapsibleLines="1-7" expandMoreButton="Show Imports" -import { - createWorkflow, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -const workflow = createWorkflow( - "hello-world", - async (input) => { - const products = createProductsWorkflow.runAsStep({ - input: { - products: [ - // ... - ], - }, - }) - - // ... - } -) -``` - -Instead of invoking the workflow and passing it the container, you use its `runAsStep` method and pass it an object as a parameter. - -The object has an `input` property to pass input to the workflow. - -*** - -## Preparing Input Data - -If you need to perform some data manipulation to prepare the other workflow's input data, use `transform` from the Workflows SDK. - -Learn about transform in [this chapter](undefined/learn/fundamentals/workflows/variable-manipulation). - -For example: - -```ts highlights={transformHighlights} collapsibleLines="1-12" -import { - createWorkflow, - transform, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" - -type WorkflowInput = { - title: string -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const createProductsData = transform({ - input, - }, (data) => [ - { - title: `Hello ${data.input.title}`, - }, - ]) - - const products = createProductsWorkflow.runAsStep({ - input: { - products: createProductsData, - }, - }) - - // ... - } -) -``` - -In this example, you use the `transform` function to prepend `Hello` to the title of the product. Then, you pass the result as an input to the `createProductsWorkflow`. - -*** - -## Run Workflow Conditionally - -To run a workflow in another based on a condition, use when-then from the Workflows SDK. - -Learn about when-then in [this chapter](undefined/learn/fundamentals/workflows/conditions). - -For example: - -```ts highlights={whenHighlights} collapsibleLines="1-16" -import { - createWorkflow, - when, -} from "@medusajs/framework/workflows-sdk" -import { - createProductsWorkflow, -} from "@medusajs/medusa/core-flows" -import { - CreateProductWorkflowInputDTO, -} from "@medusajs/framework/types" - -type WorkflowInput = { - product?: CreateProductWorkflowInputDTO - should_create?: boolean -} - -const workflow = createWorkflow( - "hello-product", - async (input: WorkflowInput) => { - const product = when(input, ({ should_create }) => should_create) - .then(() => { - return createProductsWorkflow.runAsStep({ - input: { - products: [input.product], - }, - }) - }) - } -) -``` - -In this example, you use when-then to run the `createProductsWorkflow` only if `should_create` (passed in the `input`) is enabled. - - # Run Workflow Steps in Parallel In this chapter, you’ll learn how to run workflow steps in parallel. @@ -13336,9 +12937,83 @@ const step1 = createStep( ### Interval Changes Workflow to Long-Running -By setting `retryInterval` on a step, a workflow becomes a [long-running workflow](undefined/learn/fundamentals/workflows/long-running-workflow) that runs asynchronously in the background. So, you won't receive its result or errors immediately when you execute the workflow. +By setting `retryInterval` on a step, a workflow becomes a [long-running workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md) that runs asynchronously in the background. So, you won't receive its result or errors immediately when you execute the workflow. -Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](undefined/learn/fundamentals/workflows/long-running-workflow#access-long-running-workflow-status-and-result). +Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow#access-long-running-workflow-status-and-result/index.html.md). + + +# Multiple Step Usage in Workflow + +In this chapter, you'll learn how to use a step multiple times in a workflow. + +## Problem Reusing a Step in a Workflow + +In some cases, you may need to use a step multiple times in the same workflow. + +The most common example is using the `useQueryGraphStep` multiple times in a workflow to retrieve multiple unrelated data, such as customers and products. + +Each workflow step must have a unique ID, which is the ID passed as a first parameter when creating the step: + +```ts +const useQueryGraphStep = createStep( + "use-query-graph" + // ... +) +``` + +This causes an error when you use the same step multiple times in a workflow, as it's registered in the workflow as two steps having the same ID: + +```ts +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"], + }) + + // ERROR OCCURS HERE: A STEP HAS THE SAME ID AS ANOTHER IN THE WORKFLOW + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"], + }) + } +) +``` + +The next section explains how to fix this issue to use the same step multiple times in a workflow. + +*** + +## How to Use a Step Multiple Times in a Workflow? + +When you execute a step in a workflow, you can chain a `config` method to it to change the step's config. + +Use the `config` method to change a step's ID for a single execution. + +So, this is the correct way to write the example above: + +```ts highlights={highlights} +const helloWorkflow = createWorkflow( + "hello", + () => { + const { data: products } = useQueryGraphStep({ + entity: "product", + fields: ["id"], + }) + + // ✓ No error occurs, the step has a different ID. + const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: ["id"], + }).config({ name: "fetch-customers" }) + } +) +``` + +The `config` method accepts an object with a `name` property. Its value is a new ID of the step to use for this execution only. + +The first `useQueryGraphStep` usage has the ID `use-query-graph`, and the second `useQueryGraphStep` usage has the ID `fetch-customers`. # Store Workflow Executions @@ -13359,11 +13034,11 @@ You can view stored workflow executions from the Medusa Admin dashboard by going ### Prerequisites -- [Redis Workflow Engine must be installed and configured.](undefined/resources/architectural-modules/workflow-engine/redis) +- [Redis Workflow Engine must be installed and configured.](https://docs.medusajs.com/resources/architectural-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: -- Enable the `store` option. If your workflow is a [Long-Running Workflow](undefined/learn/fundamentals/workflows/long-running-workflow), this option is enabled by default. +- Enable the `store` option. If your workflow is a [Long-Running Workflow](https://docs.medusajs.com/learn/fundamentals/workflows/long-running-workflow/index.html.md), this option is enabled by default. - Set the `retentionTime` option to the number of seconds that the workflow execution should be stored in the database. For example: @@ -13486,159 +13161,73 @@ if (workflowExecution.state === "failed") { Other state values include `done`, `invoking`, and `compensating`. -# Retry Failed Steps +# Variable Manipulation in Workflows with transform -In this chapter, you’ll learn how to configure steps to allow retrial on failure. +In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate variables in a workflow. -## Configure a Step’s Retrial +## Why Variable Manipulation isn't Allowed in Workflows -By default, when an error occurs in a step, the step and the workflow fail, and the execution stops. +Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. -You can configure the step to retry on failure. The `createStep` function can accept a configuration object instead of the step’s name as a first parameter. +At that point, variables in the workflow don't have any values. They only do when you execute the workflow. -For example: +So, you can only pass variables as parameters to steps. But, in a workflow, you can't change a variable's value or, if the variable is an array, loop over its items. -```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" +Instead, use `transform` from the Workflows SDK. + +Restrictions for variable manipulation is only applicable in a workflow's definition. You can still manipulate variables in your step's code. + +*** + +## What is the transform Utility? + +`transform` creates a new variable as the result of manipulating other variables. + +For example, consider you have two strings as the output of two steps: + +```ts +const str1 = step1() +const str2 = step2() +``` + +To concatenate the strings, you create a new variable `str3` using the `transform` function: + +```ts highlights={highlights} import { - createStep, createWorkflow, WorkflowResponse, + transform, } from "@medusajs/framework/workflows-sdk" - -const step1 = createStep( - { - name: "step-1", - maxRetries: 2, - }, - async () => { - console.log("Executing step 1") - - throw new Error("Oops! Something happened.") - } -) +// step imports... const myWorkflow = createWorkflow( "hello-world", - function () { - const str1 = step1() + function (input) { + const str1 = step1(input) + const str2 = step2(input) - return new WorkflowResponse({ - message: str1, - }) -}) + const str3 = transform( + { str1, str2 }, + (data) => `${data.str1}${data.str2}` + ) -export default myWorkflow + return new WorkflowResponse(str3) + } +) ``` -The step’s configuration object accepts a `maxRetries` property, which is a number indicating the number of times a step can be retried when it fails. +`transform` accepts two parameters: -When you execute the above workflow, you’ll see the following result in the terminal: +1. The first parameter is an object of variables to manipulate. The object is passed as a parameter to `transform`'s second parameter function. +2. The second parameter is the function performing the variable manipulation. -```bash -Executing step 1 -Executing step 1 -Executing step 1 -error: Oops! Something happened. -Error: Oops! Something happened. -``` +The value returned by the second parameter function is returned by `transform`. So, the `str3` variable holds the concatenated string. -The first line indicates the first time the step was executed, and the next two lines indicate the times the step was retried. After that, the step and workflow fail. +You can use the returned value in the rest of the workflow, either to pass it as an input to other steps or to return it in the workflow's response. *** -## Step Retry Intervals - -By default, a step is retried immediately after it fails. To specify a wait time before a step is retried, pass a `retryInterval` property to the step's configuration object. Its value is a number of seconds to wait before retrying the step. - -For example: - -```ts title="src/workflows/hello-world.ts" highlights={[["5"]]} -const step1 = createStep( - { - name: "step-1", - maxRetries: 2, - retryInterval: 2, // 2 seconds - }, - async () => { - // ... - } -) -``` - -### Interval Changes Workflow to Long-Running - -By setting `retryInterval` on a step, a workflow becomes a [long-running workflow](undefined/learn/fundamentals/workflows/long-running-workflow) that runs asynchronously in the background. So, you won't receive its result or errors immediately when you execute the workflow. - -Instead, you must subscribe to the workflow's execution using the Workflow Engine Module Service. Learn more about it in [this chapter](undefined/learn/fundamentals/workflows/long-running-workflow#access-long-running-workflow-status-and-result). - - -# Variable Manipulation in Workflows with transform - -In this chapter, you'll learn how to use `transform` from the Workflows SDK to manipulate variables in a workflow. - -## Why Variable Manipulation isn't Allowed in Workflows - -Medusa creates an internal representation of the workflow definition you pass to `createWorkflow` to track and store its steps. - -At that point, variables in the workflow don't have any values. They only do when you execute the workflow. - -So, you can only pass variables as parameters to steps. But, in a workflow, you can't change a variable's value or, if the variable is an array, loop over its items. - -Instead, use `transform` from the Workflows SDK. - -Restrictions for variable manipulation is only applicable in a workflow's definition. You can still manipulate variables in your step's code. - -*** - -## What is the transform Utility? - -`transform` creates a new variable as the result of manipulating other variables. - -For example, consider you have two strings as the output of two steps: - -```ts -const str1 = step1() -const str2 = step2() -``` - -To concatenate the strings, you create a new variable `str3` using the `transform` function: - -```ts highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - transform, -} from "@medusajs/framework/workflows-sdk" -// step imports... - -const myWorkflow = createWorkflow( - "hello-world", - function (input) { - const str1 = step1(input) - const str2 = step2(input) - - const str3 = transform( - { str1, str2 }, - (data) => `${data.str1}${data.str2}` - ) - - return new WorkflowResponse(str3) - } -) -``` - -`transform` accepts two parameters: - -1. The first parameter is an object of variables to manipulate. The object is passed as a parameter to `transform`'s second parameter function. -2. The second parameter is the function performing the variable manipulation. - -The value returned by the second parameter function is returned by `transform`. So, the `str3` variable holds the concatenated string. - -You can use the returned value in the rest of the workflow, either to pass it as an input to other steps or to return it in the workflow's response. - -*** - -## Example: Looping Over Array +## Example: Looping Over Array Use `transform` to loop over arrays to create another variable from the array's items. @@ -13733,7 +13322,7 @@ Since `str`'s value isn't used as a step's input or passed to `WorkflowResponse` `transform` should only be used to perform variable or data manipulation. -If you want to perform some validation on the data, use a step or [when-then](undefined/learn/fundamentals/workflows/conditions) instead. +If you want to perform some validation on the data, use a step or [when-then](https://docs.medusajs.com/learn/fundamentals/workflows/conditions/index.html.md) instead. For example: @@ -13787,7 +13376,7 @@ A workflow hook is a point in a workflow where you can inject custom functionali Medusa exposes hooks in many of its workflows that are used in its API routes. You can consume those hooks to add your custom logic. -Refer to the [Workflows Reference](undefined/resources/medusa-workflows-reference) to view all workflows and their hooks. +Refer to the [Workflows Reference](https://docs.medusajs.com/resources/medusa-workflows-reference/index.html.md) to view all workflows and their hooks. You want to perform a custom action during a workflow's execution, such as when a product is created. @@ -13819,11 +13408,11 @@ The `productsCreated` hook is available on the workflow's `hooks` property by it You invoke the hook, passing a step function (the hook handler) as a parameter. -Now, when a product is created using the [Create Product API route](undefined/api/admin#products_postproducts), your hook handler is executed after the product is created. +Now, when a product is created using the [Create Product API route](https://docs.medusajs.com/api/admin#products_postproducts), your hook handler is executed after the product is created. A hook can have only one handler. -Refer to the [createProductsWorkflow reference](undefined/resources/references/medusa-workflows/createProductsWorkflow) to see at which point the hook handler is executed. +Refer to the [createProductsWorkflow reference](https://docs.medusajs.com/resources/references/medusa-workflows/createProductsWorkflow/index.html.md) to see at which point the hook handler is executed. ### Hook Handler Parameter @@ -13874,7 +13463,7 @@ createProductsWorkflow.hooks.productsCreated( This property is an object that holds additional data passed to the workflow through the request sent to the API route using the workflow. -Learn how to pass `additional_data` in requests to API routes in [this chapter](undefined/learn/fundamentals/api-routes/additional-data). +Learn how to pass `additional_data` in requests to API routes in [this chapter](https://docs.medusajs.com/learn/fundamentals/api-routes/additional-data/index.html.md). ### Pass Additional Data to Workflow @@ -13956,7 +13545,7 @@ export default myWorkflow This workflow's executions fail if they run longer than two seconds. -A workflow’s timeout error is returned in the `errors` property of the workflow’s execution, as explained in [this chapter](undefined/learn/fundamentals/workflows/access-workflow-errors). The error’s name is `TransactionTimeoutError`. +A workflow’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 `TransactionTimeoutError`. *** @@ -13984,16 +13573,16 @@ const step1 = createStep( 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](undefined/learn/fundamentals/workflows/access-workflow-errors). The error’s name is `TransactionStepTimeoutError`. +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`. # Example: Integration Tests for a Module -In this chapter, find an example of writing an integration test for a module using [moduleIntegrationTestRunner](undefined/learn/debugging-and-testing/testing-tools/modules-tests) from Medusa's Testing Framework. +In this chapter, find an example of writing an integration test for a module using [moduleIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/modules-tests/index.html.md) from Medusa's Testing Framework. ### Prerequisites -- [Testing Tools Setup](undefined/learn/debugging-and-testing/testing-tools) +- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md) ## Write Integration Test for Module @@ -14052,18 +13641,18 @@ Run the following command to run your module integration tests: 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](undefined/learn/debugging-and-testing/testing-tools#add-test-commands). +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. # Example: Write Integration Tests for Workflows -In this chapter, you'll learn how to write integration tests for workflows using [medusaIntegrationTestRunner](undefined/learn/debugging-and-testing/testing-tools/integration-tests) from Medusa's Testing Framwork. +In this chapter, you'll learn how to write integration tests for workflows using [medusaIntegrationTestRunner](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests/index.html.md) from Medusa's Testing Framwork. ### Prerequisites -- [Testing Tools Setup](undefined/learn/debugging-and-testing/testing-tools) +- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md) ## Write Integration Test for Workflow @@ -14134,7 +13723,7 @@ Run the following command to run your tests: npm run test:integration ``` -If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](undefined/learn/debugging-and-testing/testing-tools#add-test-commands). +If you don't have a `test:integration` 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 under the `integrations/http` directory. @@ -14190,11 +13779,11 @@ If you threw a `MedusaError`, then you can check the error message in `errors[0] # Example: Write Integration Tests for API Routes -In this chapter, you'll learn how to write integration tests for API routes using [medusaIntegrationTestRunner](undefined/learn/debugging-and-testing/testing-tools/integration-tests) from Medusa's Testing Framework. +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. ### Prerequisites -- [Testing Tools Setup](undefined/learn/debugging-and-testing/testing-tools) +- [Testing Tools Setup](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/index.html.md) ## Test a GET API Route @@ -14255,7 +13844,7 @@ Run the following command to run your tests: npm run test:integration ``` -If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](undefined/learn/debugging-and-testing/testing-tools#add-test-commands). +If you don't have a `test:integration` 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 under the `src/integrations/http` directory. @@ -14524,7 +14113,7 @@ medusaIntegrationTestRunner({ jest.setTimeout(60 * 1000) ``` -In your test suit, you add a `beforeAll` hook to create a publishable API key before the tests run. To create the API key, you can use the `createApiKeysWorkflow` or the [API Key Module's service](undefined/resources/commerce-modules/api-key). +In your test suit, you add a `beforeAll` hook to create a publishable API key before the tests run. To create the API key, you can use the `createApiKeysWorkflow` or the [API Key Module's service](https://docs.medusajs.com/resources/commerce-modules/api-key/index.html.md). Then, in the test, you pass an object as the last parameter to `api.get` with a `headers` property. The `headers` property is an object with the key `x-publishable-api-key` and the value of the API key's token. @@ -14779,22 +14368,22 @@ The Commerce Modules can be used in many use cases, including: In this section of the documentation, you will find resources to learn more about the API Key Module and how to use it in your application. -Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## API Key Features -- [API Key Types and Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/api-key/concepts): Manage API keys in your store. You can create both publishable and secret API keys for different use cases. -- [Token Verification](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/api-key/concepts#token-verification): Verify tokens of secret API keys to authenticate users or actions. -- [Revoke Keys](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/api-key/concepts#api-key-expiration): Revoke keys to disable their use permanently. -- Roll API Keys: Roll API keys by [revoking](undefined/references/api-key/revoke) a key then [re-creating it](undefined/references/api-key/createApiKeys). +- [API Key Types and Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/api-key/concepts/index.html.md): Manage API keys in your store. You can create both publishable and secret API keys for different use cases. +- [Token Verification](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/api-key/concepts#token-verification/index.html.md): Verify tokens of secret API keys to authenticate users or actions. +- [Revoke Keys](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/api-key/concepts#api-key-expiration/index.html.md): Revoke keys to disable their use permanently. +- Roll API Keys: Roll API keys by [revoking](https://docs.medusajs.com/references/api-key/revoke/index.html.md) a key then [re-creating it](https://docs.medusajs.com/references/api-key/createApiKeys/index.html.md). *** ## How to Use the API Key Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -14908,7 +14497,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -14917,22 +14506,22 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen 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](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## Auth Features -- [Basic User Authentication](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#1-basic-authentication-flow): Authenticate users using their email and password credentials. -- [Third-Party and Social Authentication](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow): Authenticate users using third-party services and social platforms, such as [Google](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google) and [GitHub](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github). -- [Authenticate Custom Actor Types](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type): Create custom user or actor types, such as managers, authenticate them in your application, and guard routes based on the custom user types. -- [Custom Authentication Providers](undefined/references/auth/provider): Integrate third-party services with custom authentication providors. +- [Basic User Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#1-basic-authentication-flow/index.html.md): Authenticate users using their email and password credentials. +- [Third-Party and Social Authentication](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow/index.html.md): Authenticate users using third-party services and social platforms, such as [Google](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google/index.html.md) and [GitHub](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github/index.html.md). +- [Authenticate Custom Actor Types](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type/index.html.md): Create custom user or actor types, such as managers, authenticate them in your application, and guard routes based on the custom user types. +- [Custom Authentication Providers](https://docs.medusajs.com/references/auth/provider/index.html.md): Integrate third-party services with custom authentication providors. *** ## How to Use the Auth Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -15026,13 +14615,13 @@ export async function GET( } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** ## Configure Auth Module -The Auth Module accepts options for further configurations. Refer to [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/module-options) for details on the module's options. +The Auth Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/module-options/index.html.md) for details on the module's options. *** @@ -15047,20 +14636,20 @@ Medusa provides the following authentication providers out-of-the-box. You can u 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. -Medusa has currency related features available out-of-the-box through the Currency Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +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](undefined/references/currency/listAndCountCurrencies): This module adds all common currencies to your application and allows you to retrieve them. -- [Support Currencies in Modules](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/currency/links-to-other-modules): Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details. +- [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](undefined/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](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. @@ -15184,35 +14773,37 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** -# Customer Module +# Cart 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. +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 customer related features available out-of-the-box through the Customer Module. A [module](undefined/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 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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## Customer Features +## Cart Features -- [Customer Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/customer/customer-accounts): Store and manage guest and registered customers in your store. -- [Customer Organization](undefined/references/customer/models): 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion). +- [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. *** -## How to Use the Customer Module +## How to Use the Cart Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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} +```ts title="src/workflows/create-cart.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -15221,36 +14812,45 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createCustomerStep = createStep( - "create-customer", +const createCartStep = createStep( + "create-cart", async ({}, { container }) => { - const customerModuleService = container.resolve(Modules.CUSTOMER) + const cartModuleService = container.resolve(Modules.CART) - const customer = await customerModuleService.createCustomers({ - first_name: "Peter", - last_name: "Hayes", - email: "peter.hayes@example.com", + const cart = await cartModuleService.createCarts({ + currency_code: "usd", + shipping_address: { + address_1: "1512 Barataria Blvd", + country_code: "us", + }, + items: [ + { + title: "Shirt", + unit_price: 1000, + quantity: 1, + }, + ], }) - return new StepResponse({ customer }, customer.id) + return new StepResponse({ cart }, cart.id) }, - async (customerId, { container }) => { - if (!customerId) { + async (cartId, { container }) => { + if (!cartId) { return } - const customerModuleService = container.resolve(Modules.CUSTOMER) + const cartModuleService = container.resolve(Modules.CART) - await customerModuleService.deleteCustomers([customerId]) + await cartModuleService.deleteCarts([cartId]) } ) -export const createCustomerWorkflow = createWorkflow( - "create-customer", +export const createCartWorkflow = createWorkflow( + "create-cart", () => { - const { customer } = createCustomerStep() + const { cart } = createCartStep() return new WorkflowResponse({ - customer, + cart, }) } ) @@ -15265,13 +14865,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createCustomerWorkflow } from "../../workflows/create-customer" +import { createCartWorkflow } from "../../workflows/create-cart" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createCustomerWorkflow(req.scope) + const { result } = await createCartWorkflow(req.scope) .run() res.send(result) @@ -15285,13 +14885,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createCustomerWorkflow } from "../workflows/create-customer" +import { createCartWorkflow } from "../workflows/create-cart" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createCustomerWorkflow(container) + const { result } = await createCartWorkflow(container) .run() console.log(result) @@ -15306,12 +14906,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createCustomerWorkflow } from "../workflows/create-customer" +import { createCartWorkflow } from "../workflows/create-cart" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createCustomerWorkflow(container) + const { result } = await createCartWorkflow(container) .run() console.log(result) @@ -15323,37 +14923,35 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** -# Cart Module +# Customer 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. +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. -Medusa has cart related features available out-of-the-box through the Cart Module. A [module](undefined/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 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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## Cart Features +## Customer Features -- [Cart Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart/concepts): Store and manage carts, including their addresses, line items, shipping methods, and more. -- [Apply Promotion Adjustments](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart/promotions): Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals. -- [Apply Tax Lines](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart/tax-lines): Apply tax lines to line items and shipping methods. -- [Cart Scoping](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart/links-to-other-modules): When used in the Medusa application, Medusa creates links to other commerce modules, scoping a cart to a sales channel, region, and a customer. +- [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 Cart Module +## How to Use the Customer Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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-cart.ts" highlights={highlights} +```ts title="src/workflows/create-customer.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -15362,45 +14960,36 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createCartStep = createStep( - "create-cart", +const createCustomerStep = createStep( + "create-customer", async ({}, { container }) => { - const cartModuleService = container.resolve(Modules.CART) + const customerModuleService = container.resolve(Modules.CUSTOMER) - const cart = await cartModuleService.createCarts({ - currency_code: "usd", - shipping_address: { - address_1: "1512 Barataria Blvd", - country_code: "us", - }, - items: [ - { - title: "Shirt", - unit_price: 1000, - quantity: 1, - }, - ], + const customer = await customerModuleService.createCustomers({ + first_name: "Peter", + last_name: "Hayes", + email: "peter.hayes@example.com", }) - return new StepResponse({ cart }, cart.id) + return new StepResponse({ customer }, customer.id) }, - async (cartId, { container }) => { - if (!cartId) { + async (customerId, { container }) => { + if (!customerId) { return } - const cartModuleService = container.resolve(Modules.CART) + const customerModuleService = container.resolve(Modules.CUSTOMER) - await cartModuleService.deleteCarts([cartId]) + await customerModuleService.deleteCustomers([customerId]) } ) -export const createCartWorkflow = createWorkflow( - "create-cart", +export const createCustomerWorkflow = createWorkflow( + "create-customer", () => { - const { cart } = createCartStep() + const { customer } = createCustomerStep() return new WorkflowResponse({ - cart, + customer, }) } ) @@ -15415,13 +15004,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createCartWorkflow } from "../../workflows/create-cart" +import { createCustomerWorkflow } from "../../workflows/create-customer" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createCartWorkflow(req.scope) + const { result } = await createCustomerWorkflow(req.scope) .run() res.send(result) @@ -15435,13 +15024,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createCartWorkflow } from "../workflows/create-cart" +import { createCustomerWorkflow } from "../workflows/create-customer" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createCartWorkflow(container) + const { result } = await createCustomerWorkflow(container) .run() console.log(result) @@ -15456,12 +15045,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createCartWorkflow } from "../workflows/create-cart" +import { createCustomerWorkflow } from "../workflows/create-customer" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createCartWorkflow(container) + const { result } = await createCustomerWorkflow(container) .run() console.log(result) @@ -15473,7 +15062,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -15482,22 +15071,22 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen In this section of the documentation, you will find resources to learn more about the Fulfillment Module and how to use it in your application. -Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## Fulfillment Features -- [Fulfillment Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/item-fulfillment): Create fulfillments and keep track of their status, items, and more. -- [Integrate Third-Party Fulfillment Providers](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/fulfillment-provider): Create third-party fulfillment providers to provide customers with shipping options and fulfill their orders. -- [Restrict By Location and Rules](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/shipping-option): Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options. -- [Support Different Fulfillment Forms](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/concepts): Support various fulfillment forms, such as shipping or pick up. +- [Fulfillment Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/item-fulfillment/index.html.md): Create fulfillments and keep track of their status, items, and more. +- [Integrate Third-Party Fulfillment Providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/fulfillment-provider/index.html.md): Create third-party fulfillment providers to provide customers with shipping options and fulfill their orders. +- [Restrict By Location and Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/shipping-option/index.html.md): Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options. +- [Support Different Fulfillment Forms](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/concepts/index.html.md): Support various fulfillment forms, such as shipping or pick up. *** ## How to Use the Fulfillment Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -15628,13 +15217,13 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** ## Configure Fulfillment Module -The Fulfillment Module accepts options for further configurations. Refer to [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options) for details on the module's options. +The Fulfillment Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options/index.html.md) for details on the module's options. *** @@ -15643,23 +15232,23 @@ The Fulfillment Module accepts options for further configurations. Refer to [thi 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. -Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## Inventory Features -- [Inventory Items Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/concepts): Store and manage inventory of any stock-kept item, such as product variants. -- [Inventory Across Locations](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventorylevel): Manage inventory levels across different locations, such as warehouses. -- [Reservation Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#reservationitem): Reserve quantities of inventory items at specific locations for orders or other purposes. -- [Check Inventory Availability](undefined/references/inventory-next/confirmInventory): Check whether an inventory item has the necessary quantity for purchase. -- [Inventory Kits](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. +- [Inventory Items Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts/index.html.md): Store and manage inventory of any stock-kept item, such as product variants. +- [Inventory Across Locations](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#inventorylevel/index.html.md): Manage inventory levels across different locations, such as warehouses. +- [Reservation Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts#reservationitem/index.html.md): Reserve quantities of inventory items at specific locations for orders or other purposes. +- [Check Inventory Availability](https://docs.medusajs.com/references/inventory-next/confirmInventory/index.html.md): Check whether an inventory item has the necessary quantity for purchase. +- [Inventory Kits](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. *** ## How to Use the Inventory Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -15776,7 +15365,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -15785,23 +15374,23 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen In this section of the documentation, you will find resources to learn more about the Order Module and how to use it in your application. -Medusa has order related features available out-of-the-box through the Order Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## Order Features -- [Order Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/concepts): Store and manage your orders to retrieve, create, cancel, and perform other operations. +- [Order Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/concepts/index.html.md): Store and manage your orders to retrieve, create, cancel, and perform other operations. - Draft Orders: Allow merchants to create orders on behalf of their customers as draft orders that later are transformed to regular orders. -- [Apply Promotion Adjustments](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/promotion-adjustments): Apply promotions or discounts to the order's items and shipping methods by adding adjustment lines that are factored into their subtotals. -- [Apply Tax Lines](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/tax-lines): Apply tax lines to an order's line items and shipping methods. -- [Returns](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return), [Edits](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/edit), [Exchanges](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/exchange), and [Claims](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/claim): Make [changes](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/order-change) to an order to edit, return, or exchange its items, with [version-based control](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/order-versioning) over the order's timeline. +- [Apply Promotion Adjustments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/promotion-adjustments/index.html.md): Apply promotions or discounts to the order's 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/order/tax-lines/index.html.md): Apply tax lines to an order's line items and shipping methods. +- [Returns](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md), [Edits](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/edit/index.html.md), [Exchanges](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/exchange/index.html.md), and [Claims](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/claim/index.html.md): Make [changes](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/order-change/index.html.md) to an order to edit, return, or exchange its items, with [version-based control](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/order-versioning/index.html.md) over the order's timeline. *** ## How to Use the Order Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -15930,160 +15519,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). - -*** - - -# Payment Module - -In this section of the documentation, you will find resources to learn more about the Payment Module and how to use it in your application. - -Medusa has payment related features available out-of-the-box through the Payment Module. A [module](undefined/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. - -Learn more about why modules are isolated in [this documentation](undefined/docs/learn/fundamentals/modules/isolation). - -## Payment Features - -- [Authorize, Capture, and Refund Payments](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment): Authorize, capture, and refund payments for a single resource. -- [Payment Collection Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection): Store and manage all payments of a single resources, such as a cart, in payment collections. -- [Integrate Third-Party Payment Providers](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider): Use payment providers like [Stripe](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe) to handle and process payments, or integrate custom payment providers. -- [Saved Payment Methods](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/account-holder): Save payment methods for customers in third-party payment providers. -- [Handle Webhook Events](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/webhook-events): Handle webhook events from third-party providers and process the associated payment. - -*** - -## How to Use the Payment Module - -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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. - -For example: - -```ts title="src/workflows/create-payment-collection.ts" highlights={highlights} -import { - createWorkflow, - WorkflowResponse, - createStep, - StepResponse, -} from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" - -const createPaymentCollectionStep = createStep( - "create-payment-collection", - async ({}, { container }) => { - const paymentModuleService = container.resolve(Modules.PAYMENT) - - const paymentCollection = await paymentModuleService.createPaymentCollections({ - currency_code: "usd", - amount: 5000, - }) - - return new StepResponse({ paymentCollection }, paymentCollection.id) - }, - async (paymentCollectionId, { container }) => { - if (!paymentCollectionId) { - return - } - const paymentModuleService = container.resolve(Modules.PAYMENT) - - await paymentModuleService.deletePaymentCollections([paymentCollectionId]) - } -) - -export const createPaymentCollectionWorkflow = createWorkflow( - "create-payment-collection", - () => { - const { paymentCollection } = createPaymentCollectionStep() - - return new WorkflowResponse({ - paymentCollection, - }) - } -) -``` - -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 { createPaymentCollectionWorkflow } from "../../workflows/create-payment-collection" - -export async function GET( - req: MedusaRequest, - res: MedusaResponse -) { - const { result } = await createPaymentCollectionWorkflow(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 { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection" - -export default async function handleUserCreated({ - event: { data }, - container, -}: SubscriberArgs<{ id: string }>) { - const { result } = await createPaymentCollectionWorkflow(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 { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection" - -export default async function myCustomJob( - container: MedusaContainer -) { - const { result } = await createPaymentCollectionWorkflow(container) - .run() - - console.log(result) -} - -export const config = { - name: "run-once-a-day", - schedule: `0 0 * * *`, -} -``` - -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). - -*** - -## Configure Payment Module - -The Payment Module accepts options for further configurations. Refer to [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/module-options) for details on the module's options. - -*** - -## Providers - -Medusa provides the following payment providers out-of-the-box. You can use them to process payments for orders, returns, and other resources. +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -16092,23 +15528,23 @@ Medusa provides the following payment providers out-of-the-box. You can use them 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. -Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/concepts): Store and manage prices of a resource, such as a product or a variant. -- [Advanced Rule Engine](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-rules): Create prices with custom rules to condition prices based on different contexts. -- [Price Lists](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/concepts#price-list): Group prices and apply them only in specific conditions with price lists. -- [Price Calculation Strategy](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation): Retrieve the best price in a given context and for the specified rule values. -- [Tax-Inclusive Pricing](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/tax-inclusive-pricing): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically. +- [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](undefined/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](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. @@ -16235,36 +15671,37 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** -# Product Module +# Promotion 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. +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. -Medusa has product related features available out-of-the-box through the Product Module. A [module](undefined/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 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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## Product Features +## Promotion Features -- [Products Management](undefined/references/product/models/Product): Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options. -- [Product Organization](undefined/references/product/models): The Product Module provides different data models used to organize products, including categories, collections, tags, and more. -- [Bundled and Multi-Part Products](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. +- [Discount Functionalities](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts/index.html.md): A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order. +- [Flexible Promotion Rules](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/concepts#flexible-rules/index.html.md): A promotion has rules that restricts when the promotion is applied. +- [Campaign Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/campaign/index.html.md): A campaign combines promotions under the same conditions, such as start and end dates, and budget configurations. +- [Apply Promotion on Carts and Orders](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/actions/index.html.md): Apply promotions on carts and orders to discount items, shipping methods, or the entire order. *** -## How to Use the Product Module +## How to Use the Promotion Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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-product.ts" highlights={highlights} +```ts title="src/workflows/create-promotion.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -16273,48 +15710,41 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createProductStep = createStep( - "create-product", +const createPromotionStep = createStep( + "create-promotion", async ({}, { container }) => { - const productService = container.resolve(Modules.PRODUCT) + const promotionModuleService = container.resolve(Modules.PROMOTION) - const product = await productService.createProducts({ - title: "Medusa Shirt", - options: [ - { - title: "Color", - values: ["Black", "White"], - }, - ], - variants: [ - { - title: "Black Shirt", - options: { - Color: "Black", - }, - }, - ], + const promotion = await promotionModuleService.createPromotions({ + code: "10%OFF", + type: "standard", + application_method: { + type: "percentage", + target_type: "order", + value: 10, + currency_code: "usd", + }, }) - return new StepResponse({ product }, product.id) + return new StepResponse({ promotion }, promotion.id) }, - async (productId, { container }) => { - if (!productId) { + async (promotionId, { container }) => { + if (!promotionId) { return } - const productService = container.resolve(Modules.PRODUCT) + const promotionModuleService = container.resolve(Modules.PROMOTION) - await productService.deleteProducts([productId]) + await promotionModuleService.deletePromotions(promotionId) } ) -export const createProductWorkflow = createWorkflow( - "create-product", +export const createPromotionWorkflow = createWorkflow( + "create-promotion", () => { - const { product } = createProductStep() + const { promotion } = createPromotionStep() return new WorkflowResponse({ - product, + promotion, }) } ) @@ -16329,13 +15759,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createProductWorkflow } from "../../workflows/create-product" +import { createPromotionWorkflow } from "../../workflows/create-cart" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createProductWorkflow(req.scope) + const { result } = await createPromotionWorkflow(req.scope) .run() res.send(result) @@ -16349,13 +15779,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createProductWorkflow } from "../workflows/create-product" +import { createPromotionWorkflow } from "../workflows/create-cart" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createProductWorkflow(container) + const { result } = await createPromotionWorkflow(container) .run() console.log(result) @@ -16370,12 +15800,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createProductWorkflow } from "../workflows/create-product" +import { createPromotionWorkflow } from "../workflows/create-cart" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createProductWorkflow(container) + const { result } = await createPromotionWorkflow(container) .run() console.log(result) @@ -16387,37 +15817,38 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** -# Promotion Module +# Payment 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. +In this section of the documentation, you will find resources to learn more about the Payment Module and how to use it in your application. -Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](undefined/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 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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). -## Promotion Features +## Payment Features -- [Discount Functionalities](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/concepts): A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order. -- [Flexible Promotion Rules](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/concepts#flexible-rules): A promotion has rules that restricts when the promotion is applied. -- [Campaign Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/campaign): A campaign combines promotions under the same conditions, such as start and end dates, and budget configurations. -- [Apply Promotion on Carts and Orders](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/actions): Apply promotions on carts and orders to discount items, shipping methods, or the entire order. +- [Authorize, Capture, and Refund Payments](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment/index.html.md): Authorize, capture, and refund payments for a single resource. +- [Payment Collection Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection/index.html.md): Store and manage all payments of a single resources, such as a cart, in payment collections. +- [Integrate Third-Party Payment Providers](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/index.html.md): Use payment providers like [Stripe](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe/index.html.md) to handle and process payments, or integrate custom payment providers. +- [Saved Payment Methods](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/account-holder/index.html.md): Save payment methods for customers in third-party payment providers. +- [Handle Webhook Events](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/webhook-events/index.html.md): Handle webhook events from third-party providers and process the associated payment. *** -## How to Use the Promotion Module +## How to Use the Payment Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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-promotion.ts" highlights={highlights} +```ts title="src/workflows/create-payment-collection.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -16426,41 +15857,35 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createPromotionStep = createStep( - "create-promotion", +const createPaymentCollectionStep = createStep( + "create-payment-collection", async ({}, { container }) => { - const promotionModuleService = container.resolve(Modules.PROMOTION) + const paymentModuleService = container.resolve(Modules.PAYMENT) - const promotion = await promotionModuleService.createPromotions({ - code: "10%OFF", - type: "standard", - application_method: { - type: "percentage", - target_type: "order", - value: 10, - currency_code: "usd", - }, + const paymentCollection = await paymentModuleService.createPaymentCollections({ + currency_code: "usd", + amount: 5000, }) - return new StepResponse({ promotion }, promotion.id) + return new StepResponse({ paymentCollection }, paymentCollection.id) }, - async (promotionId, { container }) => { - if (!promotionId) { + async (paymentCollectionId, { container }) => { + if (!paymentCollectionId) { return } - const promotionModuleService = container.resolve(Modules.PROMOTION) + const paymentModuleService = container.resolve(Modules.PAYMENT) - await promotionModuleService.deletePromotions(promotionId) + await paymentModuleService.deletePaymentCollections([paymentCollectionId]) } ) -export const createPromotionWorkflow = createWorkflow( - "create-promotion", +export const createPaymentCollectionWorkflow = createWorkflow( + "create-payment-collection", () => { - const { promotion } = createPromotionStep() + const { paymentCollection } = createPaymentCollectionStep() return new WorkflowResponse({ - promotion, + paymentCollection, }) } ) @@ -16475,13 +15900,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createPromotionWorkflow } from "../../workflows/create-cart" +import { createPaymentCollectionWorkflow } from "../../workflows/create-payment-collection" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createPromotionWorkflow(req.scope) + const { result } = await createPaymentCollectionWorkflow(req.scope) .run() res.send(result) @@ -16495,13 +15920,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createPromotionWorkflow } from "../workflows/create-cart" +import { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createPromotionWorkflow(container) + const { result } = await createPaymentCollectionWorkflow(container) .run() console.log(result) @@ -16516,12 +15941,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createPromotionWorkflow } from "../workflows/create-cart" +import { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createPromotionWorkflow(container) + const { result } = await createPaymentCollectionWorkflow(container) .run() console.log(result) @@ -16533,35 +15958,48 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** +## Configure Payment Module -# Stock Location Module +The Payment Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/module-options/index.html.md) for details on the module's options. -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. +*** -Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](undefined/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. +## Providers -Learn more about why modules are isolated in [this documentation](undefined/docs/learn/fundamentals/modules/isolation). +Medusa provides the following payment providers out-of-the-box. You can use them to process payments for orders, returns, and other resources. -## Stock Location Features +*** -- [Stock Location Management](undefined/references/stock-location-next/models): 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules). -- [Address Management](undefined/references/stock-location-next/models/StockLocationAddress): Manage the address of each stock location. + +# 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. + +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). + +## Product Features + +- [Products Management](https://docs.medusajs.com/references/product/models/Product/index.html.md): Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options. +- [Product Organization](https://docs.medusajs.com/references/product/models/index.html.md): The Product Module provides different data models used to organize products, including categories, collections, tags, and more. +- [Bundled and Multi-Part Products](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Create and manage inventory kits for a single product, allowing you to implement use cases like bundled or multi-part products. *** -## How to Use Stock Location Module's Service +## How to Use the Product Module -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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} +```ts title="src/workflows/create-product.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -16570,33 +16008,49 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createStockLocationStep = createStep( - "create-stock-location", +const createProductStep = createStep( + "create-product", async ({}, { container }) => { - const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION) + const productService = container.resolve(Modules.PRODUCT) - const stockLocation = await stockLocationModuleService.createStockLocations({ - name: "Warehouse 1", + const product = await productService.createProducts({ + title: "Medusa Shirt", + options: [ + { + title: "Color", + values: ["Black", "White"], + }, + ], + variants: [ + { + title: "Black Shirt", + options: { + Color: "Black", + }, + }, + ], }) - return new StepResponse({ stockLocation }, stockLocation.id) + return new StepResponse({ product }, product.id) }, - async (stockLocationId, { container }) => { - if (!stockLocationId) { + async (productId, { container }) => { + if (!productId) { return } - const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION) + const productService = container.resolve(Modules.PRODUCT) - await stockLocationModuleService.deleteStockLocations([stockLocationId]) + await productService.deleteProducts([productId]) } ) -export const createStockLocationWorkflow = createWorkflow( - "create-stock-location", +export const createProductWorkflow = createWorkflow( + "create-product", () => { - const { stockLocation } = createStockLocationStep() + const { product } = createProductStep() - return new WorkflowResponse({ stockLocation }) + return new WorkflowResponse({ + product, + }) } ) ``` @@ -16610,13 +16064,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createStockLocationWorkflow } from "../../workflows/create-stock-location" +import { createProductWorkflow } from "../../workflows/create-product" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createStockLocationWorkflow(req.scope) + const { result } = await createProductWorkflow(req.scope) .run() res.send(result) @@ -16630,13 +16084,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createStockLocationWorkflow } from "../workflows/create-stock-location" +import { createProductWorkflow } from "../workflows/create-product" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createStockLocationWorkflow(container) + const { result } = await createProductWorkflow(container) .run() console.log(result) @@ -16651,12 +16105,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createStockLocationWorkflow } from "../workflows/create-stock-location" +import { createProductWorkflow } from "../workflows/create-product" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createStockLocationWorkflow(container) + const { result } = await createProductWorkflow(container) .run() console.log(result) @@ -16668,7 +16122,148 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +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. + +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). + +*** + +## Region Features + +- [Region Management](https://docs.medusajs.com/references/region/models/Region/index.html.md): Manage regions in your store. You can create regions with different currencies and settings. +- [Multi-Currency Support](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has a currency. You can support multiple currencies in your store by creating multiple regions. +- [Different Settings Per Region](https://docs.medusajs.com/references/region/models/Region/index.html.md): Each region has its own settings, such as what countries belong to a region or its tax settings. + +*** + +## 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. + +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-region.ts" highlights={highlights} +import { + createWorkflow, + WorkflowResponse, + createStep, + StepResponse, +} from "@medusajs/framework/workflows-sdk" +import { Modules } from "@medusajs/framework/utils" + +const createRegionStep = createStep( + "create-region", + async ({}, { container }) => { + const regionModuleService = container.resolve(Modules.REGION) + + const region = await regionModuleService.createRegions({ + name: "Europe", + currency_code: "eur", + }) + + return new StepResponse({ region }, region.id) + }, + async (regionId, { container }) => { + if (!regionId) { + return + } + const regionModuleService = container.resolve(Modules.REGION) + + await regionModuleService.deleteRegions([regionId]) + } +) + +export const createRegionWorkflow = createWorkflow( + "create-region", + () => { + const { region } = createRegionStep() + + return new WorkflowResponse({ + region, + }) + } +) +``` + +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 { createRegionWorkflow } from "../../workflows/create-region" + +export async function GET( + req: MedusaRequest, + res: MedusaResponse +) { + const { result } = await createRegionWorkflow(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 { createRegionWorkflow } from "../workflows/create-region" + +export default async function handleUserCreated({ + event: { data }, + container, +}: SubscriberArgs<{ id: string }>) { + const { result } = await createRegionWorkflow(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 { createRegionWorkflow } from "../workflows/create-region" + +export default async function myCustomJob( + container: MedusaContainer +) { + const { result } = await createRegionWorkflow(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). *** @@ -16677,9 +16272,9 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen 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. -Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## What's a Sales Channel? @@ -16695,16 +16290,16 @@ Some use case examples for using a sales channel: ## Sales Channel Features -- [Sales Channel Management](undefined/references/sales-channel/models/SalesChannel): Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels. -- [Product Availability](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules): Medusa uses the Product and Sales Channel modules to allow merchants to specify a product's availability per sales channel. -- [Cart and Order Scoping](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules): Carts, available through the Cart Module, are scoped to a sales channel. Paired with the product availability feature, you benefit from more features like allowing only products available in sales channel in a cart. -- [Inventory Availability Per Sales Channel](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules): Medusa links sales channels to stock locations, allowing you to retrieve available inventory of products based on the specified sales channel. +- [Sales Channel Management](https://docs.medusajs.com/references/sales-channel/models/SalesChannel/index.html.md): Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels. +- [Product Availability](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/index.html.md): Medusa uses the Product and Sales Channel modules to allow merchants to specify a product's availability per sales channel. +- [Cart and Order Scoping](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/index.html.md): Carts, available through the Cart Module, are scoped to a sales channel. Paired with the product availability feature, you benefit from more features like allowing only products available in sales channel in a cart. +- [Inventory Availability Per Sales Channel](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/index.html.md): Medusa links sales channels to stock locations, allowing you to retrieve available inventory of products based on the specified 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](undefined/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](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. @@ -16826,38 +16421,35 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +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. +# Stock Location Module -Medusa has region related features available out-of-the-box through the Region Module. A [module](undefined/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. +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. -Learn more about why modules are isolated in [this documentation](undefined/docs/learn/fundamentals/modules/isolation). +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). -## Region Features +## Stock Location Features -- [Region Management](undefined/references/region/models/Region): Manage regions in your store. You can create regions with different currencies and settings. -- [Multi-Currency Support](undefined/references/region/models/Region): Each region has a currency. You can support multiple currencies in your store by creating multiple regions. -- [Different Settings Per Region](undefined/references/region/models/Region): Each region has its own settings, such as what countries belong to a region or its tax settings. +- [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 Region Module's Service +## 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](undefined/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](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-region.ts" highlights={highlights} +```ts title="src/workflows/create-stock-location.ts" highlights={highlights} import { createWorkflow, WorkflowResponse, @@ -16866,36 +16458,33 @@ import { } from "@medusajs/framework/workflows-sdk" import { Modules } from "@medusajs/framework/utils" -const createRegionStep = createStep( - "create-region", +const createStockLocationStep = createStep( + "create-stock-location", async ({}, { container }) => { - const regionModuleService = container.resolve(Modules.REGION) + const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION) - const region = await regionModuleService.createRegions({ - name: "Europe", - currency_code: "eur", + const stockLocation = await stockLocationModuleService.createStockLocations({ + name: "Warehouse 1", }) - return new StepResponse({ region }, region.id) + return new StepResponse({ stockLocation }, stockLocation.id) }, - async (regionId, { container }) => { - if (!regionId) { + async (stockLocationId, { container }) => { + if (!stockLocationId) { return } - const regionModuleService = container.resolve(Modules.REGION) + const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION) - await regionModuleService.deleteRegions([regionId]) + await stockLocationModuleService.deleteStockLocations([stockLocationId]) } ) -export const createRegionWorkflow = createWorkflow( - "create-region", +export const createStockLocationWorkflow = createWorkflow( + "create-stock-location", () => { - const { region } = createRegionStep() + const { stockLocation } = createStockLocationStep() - return new WorkflowResponse({ - region, - }) + return new WorkflowResponse({ stockLocation }) } ) ``` @@ -16909,13 +16498,13 @@ import type { MedusaRequest, MedusaResponse, } from "@medusajs/framework/http" -import { createRegionWorkflow } from "../../workflows/create-region" +import { createStockLocationWorkflow } from "../../workflows/create-stock-location" export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const { result } = await createRegionWorkflow(req.scope) + const { result } = await createStockLocationWorkflow(req.scope) .run() res.send(result) @@ -16929,13 +16518,13 @@ import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/framework" -import { createRegionWorkflow } from "../workflows/create-region" +import { createStockLocationWorkflow } from "../workflows/create-stock-location" export default async function handleUserCreated({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const { result } = await createRegionWorkflow(container) + const { result } = await createStockLocationWorkflow(container) .run() console.log(result) @@ -16950,12 +16539,12 @@ export const config: SubscriberConfig = { ```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]} import { MedusaContainer } from "@medusajs/framework/types" -import { createRegionWorkflow } from "../workflows/create-region" +import { createStockLocationWorkflow } from "../workflows/create-stock-location" export default async function myCustomJob( container: MedusaContainer ) { - const { result } = await createRegionWorkflow(container) + const { result } = await createStockLocationWorkflow(container) .run() console.log(result) @@ -16967,7 +16556,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -16976,20 +16565,20 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen 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. -Medusa has store related features available out-of-the-box through the Store Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +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](undefined/references/store/models/Store): Create and manage stores in your application. -- [Multi-Tenancy Support](undefined/references/store/models/Store): Create multiple stores, each having its own configurations. +- [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](undefined/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](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. @@ -17106,7 +16695,7 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** @@ -17115,21 +16704,21 @@ Learn more about workflows in [this documentation](undefined/docs/learn/fundamen 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. -Medusa has tax related features available out-of-the-box through the Tax Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/tax/tax-region): Set different tax settings for each tax region. -- [Tax Rates and Rules](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/tax/tax-rates-and-rules): Manage each region's default tax rates and override them with conditioned tax rates. -- [Retrieve Tax Lines for carts and orders](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/tax/tax-calculation-with-provider): Calculate and retrieve the tax lines of a cart or order's line items and shipping methods with tax providers. +- [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](undefined/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](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. @@ -17242,13 +16831,13 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/tax/module-options) for details on the module's options. +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. *** @@ -17257,20 +16846,20 @@ The Tax Module accepts options for further configurations. Refer to [this docume 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. -Medusa has user related features available out-of-the-box through the User Module. A [module](undefined/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](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](undefined/docs/learn/fundamentals/modules/isolation). +Learn more about why modules are isolated in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/isolation/index.html.md). ## User Features -- [User Management](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows): Store and manage users in your store. -- [Invite Users](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows#invite-users): Invite users to join your store and manage those invites. +- [User Management](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows/index.html.md): Store and manage users in your store. +- [Invite Users](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/user-creation-flows#invite-users/index.html.md): Invite users to join your store and manage those invites. *** ## How to Use User Module's Service -In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](undefined/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](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. @@ -17387,13 +16976,13 @@ export const config = { } ``` -Learn more about workflows in [this documentation](undefined/docs/learn/fundamentals/workflows). +Learn more about workflows in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md). *** ## Configure User Module -The User Module accepts options for further configurations. Refer to [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/user/module-options) for details on the module's options. +The User Module accepts options for further configurations. Refer to [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/user/module-options/index.html.md) for details on the module's options. *** @@ -17409,13 +16998,13 @@ There are two types of API keys: - `publishable`: A public key used in client applications, such as a storefront. - `secret`: A secret key used for authentication and verification purposes, such as an admin user’s authentication token or a password reset token. -The API key’s type is stored in the `type` property of the [ApiKey data model](undefined/references/api-key/models/ApiKey). +The API key’s type is stored in the `type` property of the [ApiKey data model](https://docs.medusajs.com/references/api-key/models/ApiKey/index.html.md). *** ## API Key Expiration -An API key expires when it’s revoked using the [revoke method of the module’s main service](undefined/references/api-key/revoke). +An API key expires when it’s revoked using the [revoke method of the module’s main service](https://docs.medusajs.com/references/api-key/revoke/index.html.md). The associated token is no longer usable or verifiable. @@ -17423,7 +17012,7 @@ The associated token is no longer usable or verifiable. ## Token Verification -To verify a token received as an input or in a request, use the [authenticate method of the module’s main service](undefined/references/api-key/authenticate) which validates the token against all non-expired tokens. +To verify a token received as an input or in a request, use the [authenticate method of the module’s main service](https://docs.medusajs.com/references/api-key/authenticate/index.html.md) which validates the token against all non-expired tokens. # Links between API Key Module and Other Modules @@ -17446,11 +17035,11 @@ You can create a publishable API key and associate it with a sales channel. Medu This is useful to avoid passing the sales channel's ID as a parameter of every request, and instead pass the publishable API key in the header of any request to the Store API route. -Learn more about this in the [Sales Channel Module's documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys). +Learn more about this in the [Sales Channel Module's documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys/index.html.md). ### Retrieve with Query -To retrieve the sales channels of an API key with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `sales_channels.*` in `fields`: +To retrieve the sales channels of an API key with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channels.*` in `fields`: ### query.graph @@ -17484,7 +17073,7 @@ const { data: apiKeys } = useQueryGraphStep({ ### Manage with Link -To manage the sales channels of an API key, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the sales channels of an API key, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -17522,6 +17111,75 @@ createRemoteLinkStep({ ``` +# 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. @@ -17530,7 +17188,7 @@ In this document, you'll learn how to use the Auth Module's main service's metho ### Register -The [register method of the Auth Module's main service](undefined/references/auth/register) creates an auth identity that can be authenticated later. +The [register method of the Auth Module's main service](https://docs.medusajs.com/references/auth/register/index.html.md) creates an auth identity that can be authenticated later. For example: @@ -17548,7 +17206,7 @@ This method calls the `register` method of the provider specified in the first p ### Authenticate -To authenticate a user, you use the [authenticate method of the Auth Module's main service](undefined/references/auth/authenticate). For example: +To authenticate a user, you use the [authenticate method of the Auth Module's main service](https://docs.medusajs.com/references/auth/authenticate/index.html.md). For example: ```ts const data = await authModuleService.authenticate( @@ -17601,7 +17259,7 @@ If `success` is true and `location` isn't set, the user is authenticated success The next section explains the flow if `location` is set. -Check out the [AuthIdentity](undefined/references/auth/models/AuthIdentity) reference for the received properties in `authIdentity`. +Check out the [AuthIdentity](https://docs.medusajs.com/references/auth/models/AuthIdentity/index.html.md) reference for the received properties in `authIdentity`. ![Diagram showcasing the basic authentication flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1711373749/Medusa%20Resources/basic-auth_lgpqsj.jpg) @@ -17665,7 +17323,7 @@ const { success, authIdentity, location } = await authModuleService.authenticate Providers handling this authentication flow must implement the `validateCallback` method. It implements the logic to validate the authentication with the third-party service. -So, once the user performs the required action with the third-party service (for example, log-in with Google), the frontend must redirect to an API route that uses the [validateCallback method of the Auth Module's main service](undefined/references/auth/validateCallback). +So, once the user performs the required action with the third-party service (for example, log-in with Google), the frontend must redirect to an API route that uses the [validateCallback method of the Auth Module's main service](https://docs.medusajs.com/references/auth/validateCallback/index.html.md). The method calls the specified provider’s `validateCallback` method passing it the authentication details it received in the second parameter: @@ -17736,9 +17394,9 @@ For example, the EmailPass Auth Module Provider authenticates a user using their ### Auth Providers List -- [Emailpass](undefined/commerce-modules/auth/auth-providers/emailpass) -- [Google](undefined/commerce-modules/auth/auth-providers/google) -- [GitHub](undefined/commerce-modules/auth/auth-providers/github) +- [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) *** @@ -17746,7 +17404,7 @@ For example, the EmailPass Auth Module Provider authenticates a user using their 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](undefined/references/medusa-config#http-authMethodsPerActor-1-3) in Medusa's configurations: +To restrict the auth providers used for actor types, use the [authMethodsPerActor option](https://docs.medusajs.com/references/medusa-config#http-authMethodsPerActor-1-3/index.html.md) in Medusa's configurations: ```ts title="medusa-config.ts" module.exports = defineConfig({ @@ -17769,7 +17427,7 @@ When you specify the `authMethodsPerActor` configuration, it overrides the defau ## How to Create an Auth Module Provider -Refer to [this guide](undefined/references/auth/provider) to learn 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 @@ -17784,7 +17442,7 @@ These routes are added by Medusa's HTTP layer, not the Auth Module. This authentication flow doesn't require validation with third-party services. -[How to register customer in storefront using basic authentication flow](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/register). +[How to register customer in storefront using basic authentication flow](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/register/index.html.md). The steps are: @@ -17792,8 +17450,8 @@ The steps are: 1. Register the user with the [Register Route](#register-route). 2. Use the authentication token to create the user with their respective API route. - - For example, for customers you would use the [Create Customer API route](undefined/api/store#customers_postcustomers). - - For admin users, you accept an invite using the [Accept Invite API route](undefined/api/admin#invites_postinvitesaccept) + - For example, for customers you would use the [Create Customer API route](https://docs.medusajs.com/api/store#customers_postcustomers). + - For admin users, you accept an invite using the [Accept Invite API route](https://docs.medusajs.com/api/admin#invites_postinvitesaccept) 3. Authenticate the user with the [Auth Route](#login-route). After registration, you only use the [Auth Route](#login-route) for subsequent authentication. @@ -17804,7 +17462,7 @@ To handle errors related to existing identities, refer to [this section](#handli This authentication flow authenticates the user with a third-party service, such as Google. -[How to authenticate customer with a third-party provider in the storefront.](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/third-party-login). +[How to authenticate customer with a third-party provider in the storefront.](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/third-party-login/index.html.md). It requires the following steps: @@ -17819,8 +17477,8 @@ It requires the following steps: - If the decoded data has an `actor_id` property, then the user is already registered. So, use this token for subsequent authenticated requests. - If not, follow the rest of the steps. 7. The frontend uses the authentication token to create the user with their respective API route. - - For example, for customers you would use the [Create Customer API route](undefined/api/store#customers_postcustomers). - - For admin users, you accept an invite using the [Accept Invite API route](undefined/api/admin#invites_postinvitesaccept) + - For example, for customers you would use the [Create Customer API route](https://docs.medusajs.com/api/store#customers_postcustomers). + - For admin users, you accept an invite using the [Accept Invite API route](https://docs.medusajs.com/api/admin#invites_postinvitesaccept) 8. The frontend sends a request to the [Refresh Token Route](#refresh-token-route) to retrieve a new token with the user information populated. *** @@ -17843,7 +17501,7 @@ This API route is useful for providers like `emailpass` that uses custom logic t For example, if you're registering a customer, you: 1. Send a request to `/auth/customer/emailpass/register` to retrieve the registration JWT token. -2. Send a request to the [Create Customer API route](undefined/api/store#customers_postcustomers) to create the customer, passing the [JWT token in the header](undefined/api/store#authentication). +2. Send a request to the [Create Customer API route](https://docs.medusajs.com/api/store#customers_postcustomers) to create the customer, passing the [JWT token in the header](https://docs.medusajs.com/api/store#authentication). ### Path Parameters @@ -17903,7 +17561,7 @@ You can show that error message to the customer. ## Login Route -The Medusa application defines an API route at `/auth/{actor_type}/{provider}` that authenticates a user of an actor type. It returns a JWT token that can be passed in [the header of subsequent requests](undefined/api/store#authentication) to send authenticated requests. +The Medusa application defines an API route at `/auth/{actor_type}/{provider}` that authenticates a user of an actor type. It returns a JWT token that can be passed in [the header of subsequent requests](https://docs.medusajs.com/api/store#authentication) to send authenticated requests. ```bash curl -X POST http://localhost:9000/auth/{actor_type}/{providers} @@ -17931,7 +17589,7 @@ For example, the EmailPass provider requires an `email` and `password` fields in #### Overriding Callback URL -For the [GitHub](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github) and [Google](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google) providers, you can pass a `callback_url` body parameter that overrides the `callbackUrl` set in the provider's configurations. +For the [GitHub](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/github/index.html.md) and [Google](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers/google/index.html.md) providers, you can pass a `callback_url` body parameter that overrides the `callbackUrl` set in the provider's configurations. This is useful if you want to redirect the user to a different URL after authentication based on their actor type. For example, you can set different `callback_url` for admin users and customers. @@ -17957,7 +17615,7 @@ If the authentication requires more action with a third-party service, you'll re Redirect to that URL in the frontend to continue the authentication process with the third-party service. -[How to login Customers using the authentication route](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/login). +[How to login Customers using the authentication route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/login/index.html.md). *** @@ -17995,7 +17653,7 @@ If the authentication is successful, you'll receive a `token` field in the respo In your frontend, decode the token using tools like [react-jwt](https://www.npmjs.com/package/react-jwt): - If the decoded data has an `actor_id` property, the user is already registered. So, use this token for subsequent authenticated requests. -- If not, use the token in the header of a request that creates the user, such as the [Create Customer API route](undefined/api/store#customers_postcustomers). +- If not, use the token in the header of a request that creates the user, such as the [Create Customer API route](https://docs.medusajs.com/api/store#customers_postcustomers). *** @@ -18030,11 +17688,11 @@ To reset a user's password: 1. Generate a token using the [Generate Reset Password Token API route](#generate-reset-password-token-route). - The API route emits the `auth.password_reset` event, passing the token in the payload. - - You can create a subscriber, as seen in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/reset-password), that listens to the event and send a notification to the user. + - You can create a subscriber, as seen in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/reset-password/index.html.md), that listens to the event and send a notification to the user. 2. Pass the token to the [Reset Password API route](#reset-password-route) to reset the password. - The URL in the user's notification should direct them to a frontend URL, which sends a request to this route. -[Storefront Development: How to Reset a Customer's Password.](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/reset-password) +[Storefront Development: How to Reset a Customer's Password.](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/reset-password/index.html.md) ### Generate Reset Password Token Route @@ -18113,75 +17771,6 @@ If the authentication is successful, the request returns an object with a `succe ``` -# 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](undefined/references/auth/models/AuthIdentity) represents a user registered by an [authentication provider](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers). 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/customer). - -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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/create-actor-type). - - # How to Create an Actor Type In this document, learn how to create an actor type and authenticate its associated data model. @@ -18190,7 +17779,7 @@ In this document, learn how to create an actor type and authenticate its associa Before creating an actor type, you must have a module with a data model representing the actor type. -Learn how to create a module in [this guide](undefined/docs/learn/fundamentals/modules). +Learn how to create a module in [this guide](https://docs.medusajs.com/docs/learn/fundamentals/modules/index.html.md). The rest of this guide uses this `Manager` data model as an example: @@ -18332,7 +17921,7 @@ export async function POST( Since the manager must be associated with an `AuthIdentity` record, the request is expected to be authenticated, even if the manager isn’t created yet. This can be achieved by: -1. Obtaining a token usng the [/auth route](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route). +1. Obtaining a token usng the [/auth route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route/index.html.md). 2. Passing the token in the bearer header of the request to this route. In the API route, you create the manager using the workflow from the previous section and return it in the response. @@ -18630,7 +18219,7 @@ The Medusa application's authentication API routes are defined under the `/auth` 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](undefined/references/medusa-config#authCors) to learn more about the `authCors` configuration. +Refer to [Medusa's configuration guide](https://docs.medusajs.com/references/medusa-config#authCors/index.html.md) to learn more about the `authCors` configuration. *** @@ -18638,18 +18227,18 @@ Refer to [Medusa's configuration guide](undefined/references/medusa-config#authC 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/auth-providers#configure-allowed-auth-providers-of-actor-types). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#generate-reset-password-token-route). +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). You'll create a subscriber that listens to the event. When the event is emitted, the subscriber sends an email notification to the user. ### Prerequisites -- [A notification provider module, such as SendGrid](undefined/architectural-modules/notification/sendgrid) +- [A notification provider module, such as SendGrid](https://docs.medusajs.com/architectural-modules/notification/sendgrid/index.html.md) ## 1. Create Subscriber @@ -18746,11 +18335,11 @@ The notification is sent to the user with the frontend URL to enter a new passwo In your frontend, you must have a page that accepts `token` and `email` query parameters. -The page shows the user password fields to enter their new password, then submits the new password, token, and email to the [Reset Password Route](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#reset-password-route). +The page shows the user password fields to enter their new password, then submits the new password, token, and email to the [Reset Password Route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#reset-password-route/index.html.md). ### Examples -- [Storefront Guide: Reset Customer Password](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/reset-password) +- [Storefront Guide: Reset Customer Password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/reset-password/index.html.md) # Links between Currency Module and Other Modules @@ -18771,11 +18360,11 @@ Read-only links are used to query data across modules, but the relations aren't 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's `Currency` data model and the [Store Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/store)'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` data model in the Store Module. +Instead, Medusa defines a read-only link between the Currency Module's `Currency` data model and the [Store Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/store/index.html.md)'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` data model in the Store Module. ### Retrieve with Query -To retrieve the details of a store's currencies with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `supported_currencies.currency.*` in `fields`: +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 @@ -18808,209 +18397,13 @@ const { data: stores } = useQueryGraphStep({ ``` -# Customer Accounts - -In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application. - -## `has_account` Property - -The [Customer data model](undefined/references/customer/models/Customer) 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. - -- [`Customer` data model \<> `AccountHolder` data model of Payment Module](#payment-module). -- [`Cart` data model of Cart Module \<> `Customer` data model](#cart-module). (Read-only). -- [`Order` data model of Order Module \<> `Customer` data model](#order-module). (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](undefined/docs/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: - -### query.graph - -```ts -const { data: customers } = await query.graph({ - entity: "customer", - fields: [ - "account_holder.*", - ], -}) - -// customers.account_holder -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: [ - "account_holder.*", - ], -}) - -// customers.account_holder -``` - -### Manage with Link - -To manage the account holders of a customer, use [Link](undefined/docs/learn/fundamentals/module-links/link): - -### 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 `Customer` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `Cart` data model. This means you can retrieve the details of a customer's carts, but you don't manage the links in a pivot table in the database. The customer of a cart is determined by the `customer_id` property of the `Cart` data model. - -### Retrieve with Query - -To retrieve a customer's carts with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `carts.*` in `fields`: - -### query.graph - -```ts -const { data: customers } = await query.graph({ - entity: "customer", - fields: [ - "carts.*", - ], -}) - -// customers.carts -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: [ - "carts.*", - ], -}) - -// customers.carts -``` - -*** - -## Order Module - -Medusa defines a read-only link between the `Customer` data model and the [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order)'s `Order` data model. This means you can retrieve the details of a customer's orders, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. - -### Retrieve with Query - -To retrieve a customer's orders with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `orders.*` in `fields`: - -### query.graph - -```ts -const { data: customers } = await query.graph({ - entity: "customer", - fields: [ - "orders.*", - ], -}) - -// customers.orders -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: customers } = useQueryGraphStep({ - entity: "customer", - fields: [ - "orders.*", - ], -}) - -// customers.orders -``` - - # Cart Concepts In this document, you’ll get an overview of the main concepts of a cart. ## Shipping and Billing Addresses -A cart has a shipping and billing address. Both of these addresses are represented by the [Address data model](undefined/references/cart/models/Address). +A cart has a shipping and billing address. Both of these addresses are represented by the [Address data model](https://docs.medusajs.com/references/cart/models/Address/index.html.md). ![A diagram showcasing the relation between the Cart and Address data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711532392/Medusa%20Resources/cart-addresses_ls6qmv.jpg) @@ -19018,19 +18411,19 @@ A cart has a shipping and billing address. Both of these addresses are represent ## Line Items -A line item, represented by the [LineItem](undefined/references/cart/models/LineItem) data model, is a quantity of a product variant added to the cart. A cart has multiple line items. +A line item, represented by the [LineItem](https://docs.medusajs.com/references/cart/models/LineItem/index.html.md) data model, is a quantity of a product variant added to the cart. A cart has multiple line items. A line item stores some of the product variant’s properties, such as the `product_title` and `product_description`. It also stores data related to the item’s quantity and price. -In the Medusa application, a product variant is implemented in the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product). +In the Medusa application, a product variant is implemented in the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md). *** ## Shipping Methods -A shipping method, represented by the [ShippingMethod data model](undefined/references/cart/models/ShippingMethod), is used to fulfill the items in the cart after the order is placed. A cart can have more than one shipping method. +A shipping method, represented by the [ShippingMethod data model](https://docs.medusajs.com/references/cart/models/ShippingMethod/index.html.md), is used to fulfill the items in the cart after the order is placed. A cart can have more than one shipping method. -In the Medusa application, the shipping method is created from a shipping option, available through the [Fulfillment Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment). Its ID is stored in the `shipping_option_id` property of the method. +In the Medusa application, the shipping method is created from a shipping option, available through the [Fulfillment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/fulfillment/index.html.md). Its ID is stored in the `shipping_option_id` property of the method. ### data Property @@ -19064,11 +18457,11 @@ Read-only links are used to query data across modules, but the relations aren't ## Customer Module -Medusa defines a read-only link between the `Cart` data model and the [Customer Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/customer)'s `Customer` data model. This means you can retrieve the details of a cart's customer, but you don't manage the links in a pivot table in the database. The customer of a cart is determined by the `customer_id` property of the `Cart` data model. +Medusa defines a read-only link between the `Cart` data model and the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md)'s `Customer` data model. This means you can retrieve the details of a cart's customer, but you don't manage the links in a pivot table in the database. The customer of a cart is determined by the `customer_id` property of the `Cart` data model. ### Retrieve with Query -To retrieve the customer of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: +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 @@ -19104,7 +18497,7 @@ const { data: carts } = useQueryGraphStep({ ## Order Module -The [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order) provides order-management features. +The [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md) provides order-management features. Medusa defines a link between the `Cart` and `Order` data models. The cart is linked to the order created once the cart is completed. @@ -19112,7 +18505,7 @@ Medusa defines a link between the `Cart` and `Order` data models. The cart is li ### Retrieve with Query -To retrieve the order of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `order.*` in `fields`: +To retrieve the order of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order.*` in `fields`: ### query.graph @@ -19146,7 +18539,7 @@ const { data: carts } = useQueryGraphStep({ ### Manage with Link -To manage the order of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the order of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -19187,7 +18580,7 @@ createRemoteLinkStep({ ## Payment Module -The [Payment Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment) handles payment processing and management. +The [Payment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/index.html.md) handles payment processing and management. Medusa defines a link between the `Cart` and `PaymentCollection` data models. A cart has a payment collection which holds all the authorized payment sessions and payments made related to the cart. @@ -19195,7 +18588,7 @@ Medusa defines a link between the `Cart` and `PaymentCollection` data models. A ### Retrieve with Query -To retrieve the payment collection of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `payment_collection.*` in `fields`: +To retrieve the payment collection of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_collection.*` in `fields`: ### query.graph @@ -19229,7 +18622,7 @@ const { data: carts } = useQueryGraphStep({ ### Manage with Link -To manage the payment collection of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the payment collection of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -19271,12 +18664,12 @@ createRemoteLinkStep({ Medusa defines read-only links between: -- the `LineItem` data model and the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `LineItem` data model. -- the `LineItem` data model and the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `LineItem` data model. +- the `LineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `LineItem` data model. +- the `LineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `LineItem` data model. ### Retrieve with Query -To retrieve the variant of a line item with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `variant.*` in `fields`: +To retrieve the variant of a line item with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: To retrieve the product, pass `product.*` in `fields`. @@ -19314,7 +18707,7 @@ const { data: lineItems } = useQueryGraphStep({ ## Promotion Module -The [Promotion Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion) provides discount features. +The [Promotion Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/promotion/index.html.md) provides discount features. Medusa defines a link between the `Cart` and `Promotion` data models. This indicates the promotions applied on a cart. @@ -19324,7 +18717,7 @@ Medusa also defines a read-only link between the `LineItemAdjustment` and `Promo ### Retrieve with Query -To retrieve the promotions of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `promotions.*` in `fields`: +To retrieve the promotions of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `promotions.*` in `fields`: To retrieve the promotion of a line item adjustment, pass `promotion.*` in `fields`. @@ -19360,7 +18753,7 @@ const { data: carts } = useQueryGraphStep({ ### Manage with Link -To manage the promotions of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the promotions of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -19401,11 +18794,11 @@ createRemoteLinkStep({ ## Region Module -Medusa defines a read-only link between the `Cart` data model and the [Region Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/region)'s `Region` data model. This means you can retrieve the details of a cart's region, but you don't manage the links in a pivot table in the database. The region of a cart is determined by the `region_id` property of the `Cart` data model. +Medusa defines a read-only link between the `Cart` data model and the [Region Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md)'s `Region` data model. This means you can retrieve the details of a cart's region, but you don't manage the links in a pivot table in the database. The region of a cart is determined by the `region_id` property of the `Cart` data model. ### Retrieve with Query -To retrieve the region of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `region.*` in `fields`: +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 @@ -19441,11 +18834,11 @@ const { data: carts } = useQueryGraphStep({ ## Sales Channel Module -Medusa defines a read-only link between the `Cart` data model and the [Sales Channel Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel)'s `SalesChannel` data model. This means you can retrieve the details of a cart's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of a cart is determined by the `sales_channel_id` property of the `Cart` data model. +Medusa defines a read-only link between the `Cart` data model and the [Sales Channel Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md)'s `SalesChannel` data model. This means you can retrieve the details of a cart's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of a cart is determined by the `sales_channel_id` property of the `Cart` data model. ### Retrieve with Query -To retrieve the sales channel of a cart with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `sales_channel.*` in `fields`: +To retrieve the sales channel of a cart with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channel.*` in `fields`: ### query.graph @@ -19478,6 +18871,81 @@ 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. + +![A diagram showcasing the relation between other data models and the tax line models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711534431/Medusa%20Resources/cart-tax-lines_oheaq6.jpg) + +*** + +## 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. + +![A diagram showing an example of calculating the subtotal of a line item using its taxes](https://res.cloudinary.com/dza7lstvk/image/upload/v1711535295/Medusa%20Resources/cart-tax-inclusive_shpr3t.jpg) + +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. @@ -19486,7 +18954,7 @@ In this document, you’ll learn how a promotion is applied to a cart’s line i 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](undefined/references/cart/models/LineItemAdjustment) data model represents changes on a line item, and the [ShippingMethodAdjustment](undefined/references/cart/models/ShippingMethodAdjustment) data model represents changes on a shipping method. +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. ![A diagram showcasing the relations between other data models and adjustment line models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711534248/Medusa%20Resources/cart-adjustments_k4sttb.jpg) @@ -19496,7 +18964,7 @@ The `amount` property of the adjustment line indicates the amount to be discount ## Discountable Option -The [LineItem](undefined/references/cart/models/LineItem) data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default. +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. @@ -19504,9 +18972,9 @@ When disabled, a promotion can’t be applied to a line item. In the context of ## 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](undefined/references/promotion/computeActions). It retrieves the actions of line items and shipping methods. +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/actions). +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: @@ -19596,78 +19064,199 @@ await cartModuleService.setShippingMethodAdjustments( ``` -# Tax Lines in Cart Module +# Customer Accounts -In this document, you’ll learn about tax lines in a cart and how to retrieve tax lines with the Tax Module. +In this document, you’ll learn how registered and unregistered accounts are distinguished in the Medusa application. -## What are Tax Lines? +## `has_account` Property -A tax line indicates the tax rate of a line item or a shipping method. The [LineItemTaxLine data model](undefined/references/cart/models/LineItemTaxLine) represents a line item’s tax line, and the [ShippingMethodTaxLine data model](undefined/references/cart/models/ShippingMethodTaxLine) represents a shipping method’s tax line. +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. -![A diagram showcasing the relation between other data models and the tax line models](https://res.cloudinary.com/dza7lstvk/image/upload/v1711534431/Medusa%20Resources/cart-tax-lines_oheaq6.jpg) +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`. *** -## Tax Inclusivity +## Email Uniqueness -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. +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. -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, there can only be one guest customer (having `has_account=false`) and one registered customer (having `has_account=true`) with the same email. -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. +# Links between Customer Module and Other Modules -![A diagram showing an example of calculating the subtotal of a line item using its taxes](https://res.cloudinary.com/dza7lstvk/image/upload/v1711535295/Medusa%20Resources/cart-tax-inclusive_shpr3t.jpg) +This document showcases the module links defined between the Customer Module and other commerce modules. -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`. +## 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. + +- [`Customer` data model \<> `AccountHolder` data model of Payment Module](#payment-module). +- [`Cart` data model of Cart Module \<> `Customer` data model](#cart-module). (Read-only). +- [`Order` data model of Order Module \<> `Customer` data model](#order-module). (Read-only). *** -## Retrieve Tax Lines +## Payment Module -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. +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 -// retrieve the cart -const cart = await cartModuleService.retrieveCart("cart_123", { - relations: [ - "items.tax_lines", - "shipping_methods.tax_lines", - "shipping_address", +const { data: customers } = await query.graph({ + entity: "customer", + fields: [ + "account_holder.*", ], }) -// retrieve the tax lines -const taxLines = await taxModuleService.getTaxLines( - [ - ...(cart.items as TaxableItemDTO[]), - ...(cart.shipping_methods as TaxableShippingDTO[]), +// customers.account_holder +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: [ + "account_holder.*", ], - { - address: { - ...cart.shipping_address, - country_code: - cart.shipping_address.country_code || "us", - }, - } -) +}) + +// customers.account_holder ``` -Then, use the returned tax lines to set the line items and shipping methods’ tax lines: +### 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 -// set line item tax lines -await cartModuleService.setLineItemTaxLines( - cart.id, - taxLines.filter((line) => "line_item_id" in line) -) +import { Modules } from "@medusajs/framework/utils" -// set shipping method tax lines -await cartModuleService.setLineItemTaxLines( - cart.id, - taxLines.filter((line) => "shipping_line_id" in line) -) +// ... + +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 `Customer` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model. This means you can retrieve the details of a customer's carts, but you don't manage the links in a pivot table in the database. The customer of a cart is determined by the `customer_id` property of the `Cart` data model. + +### Retrieve with Query + +To retrieve a customer's carts with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: + +### query.graph + +```ts +const { data: customers } = await query.graph({ + entity: "customer", + fields: [ + "carts.*", + ], +}) + +// customers.carts +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: [ + "carts.*", + ], +}) + +// customers.carts +``` + +*** + +## Order Module + +Medusa defines a read-only link between the `Customer` data model and the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model. This means you can retrieve the details of a customer's orders, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. + +### Retrieve with Query + +To retrieve a customer's orders with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: + +### query.graph + +```ts +const { data: customers } = await query.graph({ + entity: "customer", + fields: [ + "orders.*", + ], +}) + +// customers.orders +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: customers } = useQueryGraphStep({ + entity: "customer", + fields: [ + "orders.*", + ], +}) + +// customers.orders ``` @@ -19679,7 +19268,7 @@ In this document, you’ll learn about some basic fulfillment concepts. A fulfillment set is a general form or way of fulfillment. For example, shipping is a form of fulfillment, and pick-up is another form of fulfillment. Each of these can be created as fulfillment sets. -A fulfillment set is represented by the [FulfillmentSet data model](undefined/references/fulfillment/models/FulfillmentSet). All other configurations, options, and management features are related to a fulfillment set, in one way or another. +A fulfillment set is represented by the [FulfillmentSet data model](https://docs.medusajs.com/references/fulfillment/models/FulfillmentSet/index.html.md). All other configurations, options, and management features are related to a fulfillment set, in one way or another. ```ts const fulfillmentSets = await fulfillmentModuleService.createFulfillmentSets( @@ -19702,11 +19291,11 @@ const fulfillmentSets = await fulfillmentModuleService.createFulfillmentSets( A service zone is a collection of geographical zones or areas. It’s used to restrict available shipping options to a defined set of locations. -A service zone is represented by the [ServiceZone data model](undefined/references/fulfillment/models/ServiceZone). It’s associated with a fulfillment set, as each service zone is specific to a form of fulfillment. For example, if a customer chooses to pick up items, you can restrict the available shipping options based on their location. +A service zone is represented by the [ServiceZone data model](https://docs.medusajs.com/references/fulfillment/models/ServiceZone/index.html.md). It’s associated with a fulfillment set, as each service zone is specific to a form of fulfillment. For example, if a customer chooses to pick up items, you can restrict the available shipping options based on their location. ![A diagram showcasing the relation between fulfillment sets, service zones, and geo zones](https://res.cloudinary.com/dza7lstvk/image/upload/v1712329770/Medusa%20Resources/service-zone_awmvfs.jpg) -A service zone can have multiple geographical zones, each represented by the [GeoZone data model](undefined/references/fulfillment/models/GeoZone). It holds location-related details to narrow down supported areas, such as country, city, or province code. +A service zone can have multiple geographical zones, each represented by the [GeoZone data model](https://docs.medusajs.com/references/fulfillment/models/GeoZone/index.html.md). It holds location-related details to narrow down supported areas, such as country, city, or province code. *** @@ -19714,7 +19303,7 @@ A service zone can have multiple geographical zones, each represented by the [Ge A shipping profile defines a type of items that are shipped in a similar manner. For example, a `default` shipping profile is used for all item types, but the `digital` shipping profile is used for digital items that aren’t shipped and delivered conventionally. -A shipping profile is represented by the [ShippingProfile data model](undefined/references/fulfillment/models/ShippingProfile). It only defines the profile’s details, but it’s associated with the shipping options available for the item type. +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 @@ -19725,7 +19314,7 @@ In this document, you’ll learn what a fulfillment module provider is. A fulfillment module provider handles fulfilling items, typically using a third-party integration. -Fulfillment module providers registered in the Fulfillment Module's [options](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-options) are stored and represented by the [FulfillmentProvider data model](undefined/references/fulfillment/models/FulfillmentProvider). +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). *** @@ -19733,13 +19322,13 @@ Fulfillment module providers registered in the Fulfillment Module's [options](un 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/fulfillment/module-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). *** ## How to Create a Fulfillment Provider? -Refer to [this guide](undefined/references/fulfillment/provider) to learn how to create a fulfillment module 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 @@ -19748,7 +19337,7 @@ In this document, you’ll learn about the concepts of item fulfillment. ## Fulfillment Data Model -A fulfillment is the shipping and delivery of one or more items to the customer. It’s represented by the [Fulfillment data model](undefined/references/fulfillment/models/Fulfillment). +A fulfillment is the shipping and delivery of one or more items to the customer. It’s represented by the [Fulfillment data model](https://docs.medusajs.com/references/fulfillment/models/Fulfillment/index.html.md). *** @@ -19814,7 +19403,7 @@ The Fulfillment Module has the following links to other modules: ## Order Module -The [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order) provides order-management functionalities. +The [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md) provides order-management functionalities. Medusa defines a link between the `Fulfillment` and `Order` data models. A fulfillment is created for an orders' items. @@ -19826,7 +19415,7 @@ A fulfillment is also created for a return's items. So, Medusa defines a link be ### Retrieve with Query -To retrieve the order of a fulfillment with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `order.*` in `fields`: +To retrieve the order of a fulfillment with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order.*` in `fields`: To retrieve the return, pass `return.*` in `fields`. @@ -19862,7 +19451,7 @@ const { data: fulfillments } = useQueryGraphStep({ ### Manage with Link -To manage the order of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the order of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -19911,7 +19500,7 @@ Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A ### Retrieve with Query -To retrieve the price set of a shipping option with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `price_set.*` in `fields`: +To retrieve the price set of a shipping option with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `price_set.*` in `fields`: ### query.graph @@ -19945,7 +19534,7 @@ const { data: shippingOptions } = useQueryGraphStep({ ### Manage with Link -To manage the price set of a shipping option, use [Link](undefined/docs/learn/fundamentals/module-links/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 @@ -19992,7 +19581,7 @@ This link is introduced in [Medusa v2.5.0](https://github.com/medusajs/medusa/re ### Retrieve with Query -To retrieve the products of a shipping profile with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `products.*` in `fields`: +To retrieve the products of a shipping profile with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `products.*` in `fields`: ### query.graph @@ -20026,7 +19615,7 @@ const { data: shippingProfiles } = useQueryGraphStep({ ### Manage with Link -To manage the shipping profile of a product, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the shipping profile of a product, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -20079,7 +19668,7 @@ Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` ### Retrieve with Query -To retrieve the stock location of a fulfillment set with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `location.*` in `fields`: +To retrieve the stock location of a fulfillment set with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `location.*` in `fields`: To retrieve the stock location of a fulfillment provider, pass `locations.*` in `fields`. @@ -20115,7 +19704,7 @@ const { data: fulfillmentSets } = useQueryGraphStep({ ### Manage with Link -To manage the stock location of a fulfillment set, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the stock location of a fulfillment set, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -20208,7 +19797,7 @@ A shipping option is a way of shipping an item. Each fulfillment provider provid When the customer places their order, they choose a shipping option to be used to fulfill their items. -A shipping option is represented by the [ShippingOption data model](undefined/references/fulfillment/models/ShippingOption). +A shipping option is represented by the [ShippingOption data model](https://docs.medusajs.com/references/fulfillment/models/ShippingOption/index.html.md). *** @@ -20230,13 +19819,13 @@ Service zones can be more restrictive, such as restricting to certain cities or You can restrict shipping options by custom rules, such as the item’s weight or the customer’s group. -These rules are represented by the [ShippingOptionRule data model](undefined/references/fulfillment/models/ShippingOptionRule). Its properties define the custom rule: +These rules are represented by the [ShippingOptionRule data model](https://docs.medusajs.com/references/fulfillment/models/ShippingOptionRule/index.html.md). Its properties define the custom rule: - `attribute`: The name of a property or table that the rule applies to. For example, `customer_group`. - `operator`: The operator used in the condition. For example: - To allow multiple values, use the operator `in`, which validates that the provided values are in the rule’s values. - To create a negation condition that considers `value` against the rule, use `nin`, which validates that the provided values aren’t in the rule’s values. - - Check out more operators in [this reference](undefined/references/fulfillment/types/fulfillment.RuleOperatorType). + - Check out more operators in [this reference](https://docs.medusajs.com/references/fulfillment/types/fulfillment.RuleOperatorType/index.html.md). - `value`: One or more values. ![A diagram showcasing the relation between shipping option and shipping option rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712331340/Medusa%20Resources/shipping-option-rule_oosopf.jpg) @@ -20249,7 +19838,7 @@ A shipping option can have multiple rules. For example, you can add rules to a s ## Shipping Profile and Types -A shipping option belongs to a type. For example, a shipping option’s type may be `express`, while another `standard`. The type is represented by the [ShippingOptionType data model](undefined/references/fulfillment/models/ShippingOptionType). +A shipping option belongs to a type. For example, a shipping option’s type may be `express`, while another `standard`. The type is represented by the [ShippingOptionType data model](https://docs.medusajs.com/references/fulfillment/models/ShippingOptionType/index.html.md). A shipping option also belongs to a shipping profile, as each shipping profile defines the type of items to be shipped in a similar manner. @@ -20268,7 +19857,7 @@ In this document, you’ll learn about the main concepts in the Inventory Module ## InventoryItem -An inventory item, represented by the [InventoryItem data model](undefined/references/inventory-next/models/InventoryItem), is a stock-kept item, such as a product, whose inventory can be managed. +An inventory item, represented by the [InventoryItem data model](https://docs.medusajs.com/references/inventory-next/models/InventoryItem/index.html.md), is a stock-kept item, such as a product, whose inventory can be managed. The `InventoryItem` data model mainly holds details related to the underlying stock item, but has relations to other data models that include its inventory details. @@ -20278,7 +19867,7 @@ The `InventoryItem` data model mainly holds details related to the underlying st ## InventoryLevel -An inventory level, represented by the [InventoryLevel data model](undefined/references/inventory-next/models/InventoryLevel), holds the inventory and quantity details of an inventory item in a specific location. +An inventory level, represented by the [InventoryLevel data model](https://docs.medusajs.com/references/inventory-next/models/InventoryLevel/index.html.md), holds the inventory and quantity details of an inventory item in a specific location. It has three quantity-related properties: @@ -20294,7 +19883,7 @@ The inventory level's location is determined by the `location_id` property. Medu ## ReservationItem -A reservation item, represented by the [ReservationItem](undefined/references/inventory-next/models/ReservationItem) data model, represents unavailable quantity of an inventory item in a location. It's used when an order is placed but not fulfilled yet. +A reservation item, represented by the [ReservationItem](https://docs.medusajs.com/references/inventory-next/models/ReservationItem/index.html.md) data model, represents unavailable quantity of an inventory item in a location. It's used when an order is placed but not fulfilled yet. The reserved quantity is associated with a location, so it has a similar relation to that of the `InventoryLevel` with the Stock Location Module. @@ -20307,7 +19896,7 @@ This document explains how the Inventory Module is used within the Medusa applic When a product variant is created and its `manage_inventory` property's value is `true`, the Medusa application creates an inventory item associated with that product variant. -This flow is implemented within the [createProductVariantsWorkflow](undefined/references/medusa-workflows/createProductVariantsWorkflow) +This flow is implemented within the [createProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductVariantsWorkflow/index.html.md) ![A diagram showcasing how the Inventory Module is used in the product variant creation form](https://res.cloudinary.com/dza7lstvk/image/upload/v1709661511/Medusa%20Resources/inventory-product-create_khz2hk.jpg) @@ -20317,7 +19906,7 @@ This flow is implemented within the [createProductVariantsWorkflow](undefined/re When a product variant with `manage_inventory` set to `true` is added to cart, the Medusa application checks whether there's sufficient stocked quantity. If not, an error is thrown and the product variant won't be added to the cart. -This flow is implemented within the [addToCartWorkflow](undefined/references/medusa-workflows/addToCartWorkflow) +This flow is implemented within the [addToCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/addToCartWorkflow/index.html.md) ![A diagram showcasing how the Inventory Module is used in the add to cart flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1709711645/Medusa%20Resources/inventory-cart-flow_achwq9.jpg) @@ -20327,7 +19916,7 @@ This flow is implemented within the [addToCartWorkflow](undefined/references/med When an order is placed, the Medusa application creates a reservation item for each product variant with `manage_inventory` set to `true`. -This flow is implemented within the [completeCartWorkflow](undefined/references/medusa-workflows/completeCartWorkflow) +This flow is implemented within the [completeCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/completeCartWorkflow/index.html.md) ![A diagram showcasing how the Inventory Module is used in the order placed flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1709712005/Medusa%20Resources/inventory-order-placed_qdxqdn.jpg) @@ -20341,7 +19930,7 @@ When an item in an order is fulfilled and the associated variant has its `manage - Resets the `reserved_quantity` to `0`. - Deletes the associated reservation item. -This flow is implemented within the [createOrderFulfillmentWorkflow](undefined/references/medusa-workflows/createOrderFulfillmentWorkflow) +This flow is implemented within the [createOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderFulfillmentWorkflow/index.html.md) ![A diagram showcasing how the Inventory Module is used in the order fulfillment flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1709712390/Medusa%20Resources/inventory-order-fulfillment_o9wdxh.jpg) @@ -20351,7 +19940,7 @@ This flow is implemented within the [createOrderFulfillmentWorkflow](undefined/r When an item in an order is returned and the associated variant has its `manage_inventory` property set to `true`, the Medusa application increments the `stocked_quantity` of the inventory item's level with the returned quantity. -This flow is implemented within the [confirmReturnReceiveWorkflow](undefined/references/medusa-workflows/confirmReturnReceiveWorkflow) +This flow is implemented within the [confirmReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmReturnReceiveWorkflow/index.html.md) ![A diagram showcasing how the Inventory Module is used in the order return flow](https://res.cloudinary.com/dza7lstvk/image/upload/v1709712457/Medusa%20Resources/inventory-order-return_ihftyk.jpg) @@ -20368,7 +19957,7 @@ In this guide, you'll learn how inventory kits can be used in the Medusa applica An inventory kit is a collection of inventory items that are linked to a single product variant. These inventory items can be used to represent different parts of a product, or to represent a bundle of products. -The Medusa application links inventory items from the [Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory) to product variants in the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product). Each variant can have multiple inventory items, and these inventory items can be re-used or shared across variants. +The Medusa application links inventory items from the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md) to product variants in the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md). Each variant can have multiple inventory items, and these inventory items can be re-used or shared across variants. Using inventory kits, you can implement use cases like: @@ -20394,7 +19983,7 @@ Then, whenever a customer purchases a bicycle, the inventory of each part is upd Using the Medusa Admin, you can create a multi-part product by creating its inventory items first, then assigning these inventory items to the product's variant(s). -Using [workflows](undefined/docs/learn/fundamentals/workflows), you can implement this by first creating the inventory items: +Using [workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), you can implement this by first creating the inventory items: ```ts highlights={multiPartsHighlights1} import { @@ -20457,7 +20046,7 @@ export const createMultiPartProductsWorkflow = createWorkflow( ) ``` -You start by retrieving the stock location to create the inventory items in. Alternatively, you can [create a stock location](undefined/references/medusa-workflows/createStockLocationsWorkflow). +You start by retrieving the stock location to create the inventory items in. Alternatively, you can [create a stock location](https://docs.medusajs.com/references/medusa-workflows/createStockLocationsWorkflow/index.html.md). Then, you create the inventory items that the product variant consists of. @@ -20524,9 +20113,9 @@ export const createMultiPartProductsWorkflow = createWorkflow( ) ``` -You prepare the inventory item IDs to pass to the variant using [transform](undefined/docs/learn/fundamentals/workflows/variable-manipulation) from the Workflows SDK, then pass these IDs to the created product's variant. +You prepare the inventory item IDs to pass to the variant using [transform](https://docs.medusajs.com/docs/learn/fundamentals/workflows/variable-manipulation/index.html.md) from the Workflows SDK, then pass these IDs to the created product's variant. -You can now [execute the workflow](undefined/docs/learn/fundamentals/workflows#3-execute-the-workflow) in [API routes](undefined/docs/learn/fundamentals/api-routes), [scheduled jobs](undefined/docs/learn/fundamentals/scheduled-jobs), or [subscribers](undefined/docs/learn/fundamentals/events-and-subscribers). +You can now [execute the workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows#3-execute-the-workflow/index.html.md) in [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md), [scheduled jobs](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md), or [subscribers](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). *** @@ -20546,7 +20135,7 @@ Then, when the bundled product's variant is purchased, the inventory quantity of You can create a bundled product in the Medusa Admin by creating the products part of the bundle first, each having its own inventory items. Then, you create the bundled product whose variant(s) have inventory kits composed of inventory items from each of the products part of the bundle. -Using [workflows](undefined/docs/learn/fundamentals/workflows), you can implement this by first creating the products part of the bundle: +Using [workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md), you can implement this by first creating the products part of the bundle: ```ts highlights={bundledHighlights1} import { @@ -20694,7 +20283,7 @@ export const createBundledProducts = createWorkflow( ) ``` -Using [Query](undefined/docs/learn/fundamentals/module-links/query), you retrieve the product again with the inventory items of each variant. Then, you prepare the inventory items to pass to the bundled product's variant. +Using [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), you retrieve the product again with the inventory items of each variant. Then, you prepare the inventory items to pass to the bundled product's variant. Finally, create the bundled product: @@ -20740,7 +20329,7 @@ export const createBundledProducts = createWorkflow( The bundled product has the same inventory items as those of the products part of the bundle. -You can now [execute the workflow](undefined/docs/learn/fundamentals/workflows#3-execute-the-workflow) in [API routes](undefined/docs/learn/fundamentals/api-routes), [scheduled jobs](undefined/docs/learn/fundamentals/scheduled-jobs), or [subscribers](undefined/docs/learn/fundamentals/events-and-subscribers). +You can now [execute the workflow](https://docs.medusajs.com/docs/learn/fundamentals/workflows#3-execute-the-workflow/index.html.md) in [API routes](https://docs.medusajs.com/docs/learn/fundamentals/api-routes/index.html.md), [scheduled jobs](https://docs.medusajs.com/docs/learn/fundamentals/scheduled-jobs/index.html.md), or [subscribers](https://docs.medusajs.com/docs/learn/fundamentals/events-and-subscribers/index.html.md). # Links between Inventory Module and Other Modules @@ -20766,11 +20355,11 @@ Each product variant has different inventory details. Medusa defines a link betw A product variant whose `manage_inventory` property is enabled has an associated inventory item. Through that inventory's items relations in the Inventory Module, you can manage and check the variant's inventory quantity. -Learn more about product variant's inventory management in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory). +Learn more about product variant's inventory management in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory/index.html.md). ### Retrieve with Query -To retrieve the product variants of an inventory item with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `variants.*` in `fields`: +To retrieve the product variants of an inventory item with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variants.*` in `fields`: ### query.graph @@ -20804,7 +20393,7 @@ const { data: inventoryItems } = useQueryGraphStep({ ### Manage with Link -To manage the variants of an inventory item, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the variants of an inventory item, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -20845,11 +20434,11 @@ createRemoteLinkStep({ ## Stock Location Module -Medusa defines a read-only link between the `InventoryLevel` data model and the [Stock Location Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/stock-location)'s `StockLocation` data model. This means you can retrieve the details of an inventory level's stock locations, but you don't manage the links in a pivot table in the database. The stock location of an inventory level is determined by the `location_id` property of the `InventoryLevel` data model. +Medusa defines a read-only link between the `InventoryLevel` data model and the [Stock Location Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/index.html.md)'s `StockLocation` data model. This means you can retrieve the details of an inventory level's stock locations, but you don't manage the links in a pivot table in the database. The stock location of an inventory level is determined by the `location_id` property of the `InventoryLevel` data model. ### Retrieve with Query -To retrieve the stock locations of an inventory level with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `stock_locations.*` in `fields`: +To retrieve the stock locations of an inventory level with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `stock_locations.*` in `fields`: ### query.graph @@ -20882,105 +20471,105 @@ const { data: inventoryLevels } = useQueryGraphStep({ ``` -# Order Claim - -In this document, you’ll learn about order claims. - -## What is a Claim? +# Order Concepts -When a customer receives a defective or incorrect item, the merchant can create a claim to refund or replace the item. +In this document, you’ll learn about orders and related concepts -The [OrderClaim data model](undefined/references/order/models/OrderClaim) represents a claim. +## Order Items -*** +The items purchased in the order are represented by the [OrderItem data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). An order can have multiple items. -## Claim Type +![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712304722/Medusa%20Resources/order-order-items_uvckxd.jpg) -The `Claim` data model has a `type` property whose value indicates the type of the claim: +### Item’s Product Details -- `refund`: the items are returned, and the customer is refunded. -- `replace`: the items are returned, and the customer receives new items. +The details of the purchased products are represented by the [LineItem data model](https://docs.medusajs.com/references/order/models/OrderLineItem/index.html.md). Not only does a line item hold the details of the product, but also details related to its price, adjustments due to promotions, and taxes. *** -## Old and Replacement Items +## Order’s Shipping Method -When the claim is created, a return, represented by the [Return data model](undefined/references/order/models/Return), is also created to handle receiving the old items from the customer. +An order has one or more shipping methods used to handle item shipment. -Learn more about returns in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return). +Each shipping method is represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md) that holds its details. The shipping method is linked to the order through the [OrderShipping data model](https://docs.medusajs.com/references/order/models/OrderShipping/index.html.md). -If the claim’s type is `replace`, replacement items are represented by the [ClaimItem data model](undefined/references/order/models/OrderClaimItem). +![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719570409/Medusa%20Resources/order-shipping-method_tkggvd.jpg) -*** +### data Property -## Claim Shipping Methods +When fulfilling the order, you can use a third-party fulfillment provider that requires additional custom data to be passed along from the order creation process. -A claim uses shipping methods to send the replacement items to the customer. These methods are represented by the [OrderShippingMethod data model](undefined/references/order/models/OrderShippingMethod). +The `OrderShippingMethod` data model has a `data` property. It’s an object used to store custom data relevant later for fulfillment. -The shipping methods for the returned items are associated with the claim's return, as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods). +The Medusa application passes the `data` property to the Fulfillment Module when fulfilling items. *** -## Claim Refund - -If the claim’s type is `refund`, the amount to be refunded is stored in the `refund_amount` property. +## Order Totals -The [Transaction data model](undefined/references/order/models/OrderTransaction) represents the refunds made for the claim. +The order’s total amounts (including tax total, total after an item is returned, etc…) are represented by the [OrderSummary data model](https://docs.medusajs.com/references/order/models/OrderSummary/index.html.md). *** -## How Claims Impact an Order’s Version +## Order Payments -When a claim is confirmed, the order’s version is incremented. +Payments made on an order, whether they’re capture or refund payments, are recorded as transactions represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). +An order can have multiple transactions. The sum of these transactions must be equal to the order summary’s total. Otherwise, there’s an outstanding amount. -# Order Concepts +Learn more about transactions in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions/index.html.md). -In this document, you’ll learn about orders and related concepts -## Order Items +# Order Claim -The items purchased in the order are represented by the [OrderItem data model](undefined/references/order/models/OrderItem). An order can have multiple items. +In this document, you’ll learn about order claims. -![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1712304722/Medusa%20Resources/order-order-items_uvckxd.jpg) +## What is a Claim? -### Item’s Product Details +When a customer receives a defective or incorrect item, the merchant can create a claim to refund or replace the item. -The details of the purchased products are represented by the [LineItem data model](undefined/references/order/models/OrderLineItem). Not only does a line item hold the details of the product, but also details related to its price, adjustments due to promotions, and taxes. +The [OrderClaim data model](https://docs.medusajs.com/references/order/models/OrderClaim/index.html.md) represents a claim. *** -## Order’s Shipping Method +## Claim Type -An order has one or more shipping methods used to handle item shipment. +The `Claim` data model has a `type` property whose value indicates the type of the claim: -Each shipping method is represented by the [OrderShippingMethod data model](undefined/references/order/models/OrderShippingMethod) that holds its details. The shipping method is linked to the order through the [OrderShipping data model](undefined/references/order/models/OrderShipping). +- `refund`: the items are returned, and the customer is refunded. +- `replace`: the items are returned, and the customer receives new items. -![A diagram showcasing the relation between an order and its items.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719570409/Medusa%20Resources/order-shipping-method_tkggvd.jpg) +*** -### data Property +## Old and Replacement Items -When fulfilling the order, you can use a third-party fulfillment provider that requires additional custom data to be passed along from the order creation process. +When the claim is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is also created to handle receiving the old items from the customer. -The `OrderShippingMethod` data model has a `data` property. It’s an object used to store custom data relevant later for fulfillment. +Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). -The Medusa application passes the `data` property to the Fulfillment Module when fulfilling items. +If the claim’s type is `replace`, replacement items are represented by the [ClaimItem data model](https://docs.medusajs.com/references/order/models/OrderClaimItem/index.html.md). *** -## Order Totals +## Claim Shipping Methods -The order’s total amounts (including tax total, total after an item is returned, etc…) are represented by the [OrderSummary data model](undefined/references/order/models/OrderSummary). +A claim uses shipping methods to send the replacement items to the customer. These methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). + +The shipping methods for the returned items are associated with the claim's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). *** -## Order Payments +## Claim Refund -Payments made on an order, whether they’re capture or refund payments, are recorded as transactions represented by the [OrderTransaction data model](undefined/references/order/models/OrderTransaction). +If the claim’s type is `refund`, the amount to be refunded is stored in the `refund_amount` property. -An order can have multiple transactions. The sum of these transactions must be equal to the order summary’s total. Otherwise, there’s an outstanding amount. +The [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md) represents the refunds made for the claim. -Learn more about transactions in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/transactions). +*** + +## How Claims Impact an Order’s Version + +When a claim is confirmed, the order’s version is incremented. # Order Edit @@ -20991,7 +20580,7 @@ In this document, you'll learn about order edits. A merchant can edit an order to add new items or change the quantity of existing items in the order. -An order edit is represented by the [OrderChange data model](undefined/references/order/models/OrderChange). +An order edit is represented by the [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md). The `OrderChange` data model is associated with any type of change, including a return or exchange. However, its `change_type` property distinguishes the type of change it's making. @@ -21001,9 +20590,9 @@ In the case of an order edit, the `OrderChange`'s type is `edit`. ## Add Items in an Order Edit -When the merchant adds new items to the order in the order edit, the item is added as an [OrderItem](undefined/references/order/models/OrderItem). +When the merchant adds new items to the order in the order edit, the item is added as an [OrderItem](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). -Also, an `OrderChangeAction` is created. The [OrderChangeAction data model](undefined/references/order/models/OrderChangeAction) represents a change made by an `OrderChange`, such as an item added. +Also, an `OrderChangeAction` is created. The [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md) represents a change made by an `OrderChange`, such as an item added. So, when an item is added, an `OrderChangeAction` is created with the type `ITEM_ADD`. In its `details` property, the item's ID, price, and quantity are stored. @@ -21021,7 +20610,7 @@ This change is added as an `OrderChangeAction` with the type `ITEM_UPDATE`. In i Adding new items to the order requires adding shipping methods for those items. -These shipping methods are represented by the [OrderShippingMethod data model](undefined/references/order/models/OrderItem). Also, an `OrderChangeAction` is created with the type `SHIPPING_ADD` +These shipping methods are represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderItem/index.html.md). Also, an `OrderChangeAction` is created with the type `SHIPPING_ADD` *** @@ -21035,7 +20624,7 @@ When an order edit is confirmed, the order’s version is incremented. Once the Order Edit is confirmed, any additional payment or refund required can be made on the original order. -This is determined by the comparison between the `OrderSummary` and the order's transactions, as mentioned in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/transactions#checking-outstanding-amount). +This is determined by the comparison between the `OrderSummary` and the order's transactions, as mentioned in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/transactions#checking-outstanding-amount/index.html.md). # Order Exchange @@ -21048,25 +20637,25 @@ An exchange is the replacement of an item that the customer ordered with another A merchant creates the exchange, specifying the items to be replaced and the new items to be sent. -The [OrderExchange data model](undefined/references/order/models/OrderExchange) represents an exchange. +The [OrderExchange data model](https://docs.medusajs.com/references/order/models/OrderExchange/index.html.md) represents an exchange. *** ## Returned and New Items -When the exchange is created, a return, represented by the [Return data model](undefined/references/order/models/Return), is created to handle receiving the items back from the customer. +When the exchange is created, a return, represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md), is created to handle receiving the items back from the customer. -Learn more about returns in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return). +Learn more about returns in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). -The [OrderExchangeItem data model](undefined/references/order/models/OrderExchangeItem) represents the new items to be sent to the customer. +The [OrderExchangeItem data model](https://docs.medusajs.com/references/order/models/OrderExchangeItem/index.html.md) represents the new items to be sent to the customer. *** ## Exchange Shipping Methods -An exchange has shipping methods used to send the new items to the customer. They’re represented by the [OrderShippingMethod data model](undefined/references/order/models/OrderShippingMethod). +An exchange has shipping methods used to send the new items to the customer. They’re represented by the [OrderShippingMethod data model](https://docs.medusajs.com/references/order/models/OrderShippingMethod/index.html.md). -The shipping methods for the returned items are associated with the exchange's return, as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods). +The shipping methods for the returned items are associated with the exchange's return, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return#return-shipping-methods/index.html.md). *** @@ -21080,7 +20669,7 @@ The `Exchange` data model has a `difference_due` property that stores the outsta |\`difference\_due > 0\`|Merchant requires additional payment from the customer of the | |\`difference\_due = 0\`|No payment processing is required.| -Any payment or refund made is stored in the [Transaction data model](undefined/references/order/models/OrderTransaction). +Any payment or refund made is stored in the [Transaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). *** @@ -21089,45 +20678,6 @@ Any payment or refund made is stored in the [Transaction data model](undefined/r When an exchange is confirmed, the order’s version is incremented. -# Order Change - -In this document, you'll learn about the Order Change data model and possible actions in it. - -## OrderChange Data Model - -The [OrderChange data model](undefined/references/order/models/OrderChange) represents any kind of change to an order, such as a return, exchange, or edit. - -Its `change_type` property indicates what the order change is created for: - -1. `edit`: The order change is making edits to the order, as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/edit). -2. `exchange`: The order change is associated with an exchange, which you can learn about in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/exchange). -3. `claim`: The order change is associated with a claim, which you can learn about in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/claim). -4. `return_request` or `return_receive`: The order change is associated with a return, which you can learn about in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order/return). - -Once the order change is confirmed, its changes are applied on the order. - -*** - -## Order Change Actions - -The actions to perform on the original order by a change, such as adding an item, are represented by the [OrderChangeAction data model](undefined/references/order/models/OrderChangeAction). - -The `OrderChangeAction` has an `action` property that indicates the type of action to perform on the order, and a `details` property that holds more details related to the action. - -The following table lists the possible `action` values that Medusa uses and what `details` they carry. - -|Action|Description|Details| -|---|---|---|---|---| -|\`ITEM\_ADD\`|Add an item to the order.|\`details\`| -|\`ITEM\_UPDATE\`|Update an item in the order.|\`details\`| -|\`RETURN\_ITEM\`|Set an item to be returned.|\`details\`| -|\`RECEIVE\_RETURN\_ITEM\`|Mark a return item as received.|\`details\`| -|\`RECEIVE\_DAMAGED\_RETURN\_ITEM\`|Mark a return item that's damaged as received.|\`details\`| -|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | -|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | -|\`WRITE\_OFF\_ITEM\`|Remove an item's quantity as part of the claim, without adding the quantity back to the item variant's inventory.|\`details\`| - - # Links between Order Module and Other Modules This document showcases the module links defined between the Order Module and other commerce modules. @@ -21154,11 +20704,11 @@ Read-only links are used to query data across modules, but the relations aren't ## Customer Module -Medusa defines a read-only link between the `Order` data model and the [Customer Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/customer)'s `Customer` data model. This means you can retrieve the details of an order's customer, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. +Medusa defines a read-only link between the `Order` data model and the [Customer Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/customer/index.html.md)'s `Customer` data model. This means you can retrieve the details of an order's customer, but you don't manage the links in a pivot table in the database. The customer of an order is determined by the `customer_id` property of the `Order` data model. ### Retrieve with Query -To retrieve the customer of an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: +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 @@ -21194,7 +20744,7 @@ const { data: orders } = useQueryGraphStep({ ## Cart Module -The [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart) provides cart-management features. +The [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md) provides cart-management features. Medusa defines a link between the `Order` and `Cart` data models. The order is linked to the cart used for the purchased. @@ -21202,7 +20752,7 @@ Medusa defines a link between the `Order` and `Cart` data models. The order is l ### Retrieve with Query -To retrieve the cart of an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `cart.*` in `fields`: +To retrieve the cart of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: ### query.graph @@ -21236,7 +20786,7 @@ const { data: orders } = useQueryGraphStep({ ### Manage with Link -To manage the cart of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the cart of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -21287,7 +20837,7 @@ A fulfillment is also created for a return's items. So, Medusa defines a link be ### Retrieve with Query -To retrieve the fulfillments of an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `fulfillments.*` in `fields`: +To retrieve the fulfillments of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillments.*` in `fields`: To retrieve the fulfillments of a return, pass `fulfillments.*` in `fields`. @@ -21323,7 +20873,7 @@ const { data: orders } = useQueryGraphStep({ ### Manage with Link -To manage the fulfillments of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the fulfillments of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -21372,7 +20922,7 @@ So, Medusa defines links between the `PaymentCollection` data model and the `Ord ### Retrieve with Query -To retrieve the payment collections of an order, order exchange, or order claim with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `payment_collections.*` in `fields`: +To retrieve the payment collections of an order, order exchange, or order claim with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `payment_collections.*` in `fields`: ### query.graph @@ -21406,7 +20956,7 @@ const { data: orders } = useQueryGraphStep({ ### Manage with Link -To manage the payment collections of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -21449,12 +20999,12 @@ createRemoteLinkStep({ Medusa defines read-only links between: -- the `OrderLineItem` data model and the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. -- the `OrderLineItem` data model and the [Product Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. +- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `Product` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. +- the `OrderLineItem` data model and the [Product Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/index.html.md)'s `ProductVariant` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. ### Retrieve with Query -To retrieve the variant of a line item with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `variant.*` in `fields`: +To retrieve the variant of a line item with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `variant.*` in `fields`: To retrieve the product, pass `product.*` in `fields`. @@ -21498,7 +21048,7 @@ An order is associated with the promotion applied on it. Medusa defines a link b ### Retrieve with Query -To retrieve the promotion applied on an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `promotion.*` in `fields`: +To retrieve the promotion applied on an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `promotion.*` in `fields`: ### query.graph @@ -21532,7 +21082,7 @@ const { data: orders } = useQueryGraphStep({ ### Manage with Link -To manage the promotion of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the promotion of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -21573,410 +21123,1313 @@ createRemoteLinkStep({ ## Region Module -Medusa defines a read-only link between the `Order` data model and the [Region Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/region)'s `Region` data model. This means you can retrieve the details of an order's region, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. +Medusa defines a read-only link between the `Order` data model and the [Region Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/region/index.html.md)'s `Region` data model. This means you can retrieve the details of an order's region, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. ### Retrieve with Query -To retrieve the region of an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `region.*` in `fields`: +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 +``` + +*** + +## Sales Channel Module + +Medusa defines a read-only link between the `Order` data model and the [Sales Channel Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md)'s `SalesChannel` data model. This means you can retrieve the details of an order's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. + +### Retrieve with Query + +To retrieve the sales channel of an order with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channel.*` in `fields`: + +### query.graph + +```ts +const { data: orders } = await query.graph({ + entity: "order", + fields: [ + "sales_channel.*", + ], +}) + +// orders.sales_channel +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: orders } = useQueryGraphStep({ + entity: "order", + fields: [ + "sales_channel.*", + ], +}) + +// orders.sales_channel +``` + + +# Order Change + +In this document, you'll learn about the Order Change data model and possible actions in it. + +## OrderChange Data Model + +The [OrderChange data model](https://docs.medusajs.com/references/order/models/OrderChange/index.html.md) represents any kind of change to an order, such as a return, exchange, or edit. + +Its `change_type` property indicates what the order change is created for: + +1. `edit`: The order change is making edits to the order, as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/edit/index.html.md). +2. `exchange`: The order change is associated with an exchange, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/exchange/index.html.md). +3. `claim`: The order change is associated with a claim, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/claim/index.html.md). +4. `return_request` or `return_receive`: The order change is associated with a return, which you can learn about in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/return/index.html.md). + +Once the order change is confirmed, its changes are applied on the order. + +*** + +## Order Change Actions + +The actions to perform on the original order by a change, such as adding an item, are represented by the [OrderChangeAction data model](https://docs.medusajs.com/references/order/models/OrderChangeAction/index.html.md). + +The `OrderChangeAction` has an `action` property that indicates the type of action to perform on the order, and a `details` property that holds more details related to the action. + +The following table lists the possible `action` values that Medusa uses and what `details` they carry. + +|Action|Description|Details| +|---|---|---|---|---| +|\`ITEM\_ADD\`|Add an item to the order.|\`details\`| +|\`ITEM\_UPDATE\`|Update an item in the order.|\`details\`| +|\`RETURN\_ITEM\`|Set an item to be returned.|\`details\`| +|\`RECEIVE\_RETURN\_ITEM\`|Mark a return item as received.|\`details\`| +|\`RECEIVE\_DAMAGED\_RETURN\_ITEM\`|Mark a return item that's damaged as received.|\`details\`| +|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | +|\`SHIPPING\_ADD\`|Add a shipping method for new or returned items.|No details added. The ID to the shipping method is added in the | +|\`WRITE\_OFF\_ITEM\`|Remove an item's quantity as part of the claim, without adding the quantity back to the item variant's inventory.|\`details\`| + + +# Order Versioning + +In this document, you’ll learn how an order and its details are versioned. + +## What's Versioning? + +Versioning means assigning a version number to a record, such as an order and its items. This is useful to view the different versions of the order following changes in its lifetime. + +When changes are made on an order, such as an item is added or returned, the order's version changes. + +*** + +## version Property + +The `Order` and `OrderSummary` data models have a `version` property that indicates the current version. By default, its value is `1`. + +Other order-related data models, such as `OrderItem`, also has a `version` property, but it indicates the version it belongs to. + +*** + +## How the Version Changes + +When the order is changed, such as an item is exchanged, this changes the version of the order and its related data: + +1. The version of the order and its summary is incremented. +2. Related order data that have a `version` property, such as the `OrderItem`, are duplicated. The duplicated item has the new version, whereas the original item has the previous version. + +When the order is retrieved, only the related data having the same version is retrieved. + + +# Order Return + +In this document, you’ll learn about order returns. + +## What is a Return? + +A return is the return of items delivered from the customer back to the merchant. It is represented by the [Return data model](https://docs.medusajs.com/references/order/models/Return/index.html.md). + +A return is requested either by the customer from the storefront, or the merchant from the admin. Medusa supports an automated Return Merchandise Authorization (RMA) flow. + +![Diagram showcasing the automated RMA flow.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719578128/Medusa%20Resources/return-rma_pzprwq.jpg) + +Once the merchant receives the returned items, they mark the return as received. + +*** + +## Returned Items + +The items to be returned are represented by the [ReturnItem data model](references/order/models/ReturnItem). + +The `ReturnItem` model has two properties storing the item's quantity: + +1. `received_quantity`: The quantity of the item that's received and can be added to the item's inventory quantity. +2. `damaged_quantity`: The quantity of the item that's damaged, meaning it can't be sold again or added to the item's inventory quantity. + +*** + +## Return Shipping Methods + +A return has shipping methods used to return the items to the merchant. The shipping methods are represented by the [OrderShippingMethod data model](references/order/models/OrderShippingMethod). + +In the Medusa application, the shipping method for a return is created only from a shipping option, provided by the Fulfillment Module, that has the rule `is_return` enabled. + +*** + +## Refund Payment + +The `refund_amount` property of the `Return` data model holds the amount a merchant must refund the customer. + +The [OrderTransaction data model](references/order/models/OrderTransaction) represents the refunds made for the return. + +*** + +## Returns in Exchanges and Claims + +When a merchant creates an exchange or a claim, it includes returning items from the customer. + +The `Return` data model also represents the return of these items. In this case, the return is associated with the exchange or claim it was created for. + +*** + +## How Returns Impact an Order’s Version + +The order’s version is incremented when: + +1. A return is requested. +2. A return is marked as received. + + +# Promotions Adjustments in Orders + +In this document, you’ll learn how a promotion is applied to an order’s items and shipping methods using adjustment lines. + +## What are Adjustment Lines? + +An adjustment line indicates a change to a line item or a shipping method’s amount. It’s used to apply promotions or discounts on an order. + +The [OrderLineItemAdjustment data model](https://docs.medusajs.com/references/order/models/OrderLineItemAdjustment/index.html.md) represents changes on a line item, and the [OrderShippingMethodAdjustment data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodAdjustment/index.html.md) represents changes on a shipping method. + +![A diagram showcasing the relation between an order, its items and shipping methods, and their adjustment lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712306017/Medusa%20Resources/order-adjustments_myflir.jpg) + +The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. + +The ID of the applied promotion is stored in the `promotion_id` property of the adjustment line. + +*** + +## Discountable Option + +The `OrderLineItem` 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 Order and Promotion modules together, 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). + +```ts collapsibleLines="1-10" expandButtonLabel="Show Imports" +import { + ComputeActionAdjustmentLine, + ComputeActionItemLine, + ComputeActionShippingLine, + // ... +} from "@medusajs/framework/types" + +// ... + +// retrieve the order +const order = await orderModuleService.retrieveOrder("ord_123", { + relations: [ + "items.item.adjustments", + "shipping_methods.shipping_method.adjustments", + ], +}) +// retrieve the line item adjustments +const lineItemAdjustments: ComputeActionItemLine[] = [] +order.items.forEach((item) => { + const filteredAdjustments = item.adjustments?.filter( + (adjustment) => adjustment.code !== undefined + ) as unknown as ComputeActionAdjustmentLine[] + if (filteredAdjustments.length) { + lineItemAdjustments.push({ + ...item, + ...item.detail, + adjustments: filteredAdjustments, + }) + } +}) + +//retrieve shipping method adjustments +const shippingMethodAdjustments: ComputeActionShippingLine[] = + [] +order.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, + // TODO infer from cart or region + currency_code: "usd", + } +) +``` + +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 order’s line items and the shipping method’s adjustments. + +```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" +import { + AddItemAdjustmentAction, + AddShippingMethodAdjustment, + // ... +} from "@medusajs/framework/types" + +// ... + +await orderModuleService.setOrderLineItemAdjustments( + order.id, + actions.filter( + (action) => action.action === "addItemAdjustment" + ) as AddItemAdjustmentAction[] +) + +await orderModuleService.setOrderShippingMethodAdjustments( + order.id, + actions.filter( + (action) => + action.action === "addShippingMethodAdjustment" + ) as AddShippingMethodAdjustment[] +) +``` + + +# Tax Lines in Order Module + +In this document, you’ll learn about tax lines in an order. + +## What are Tax Lines? + +A tax line indicates the tax rate of a line item or a shipping method. + +The [OrderLineItemTaxLine data model](https://docs.medusajs.com/references/order/models/OrderLineItemTaxLine/index.html.md) represents a line item’s tax line, and the [OrderShippingMethodTaxLine data model](https://docs.medusajs.com/references/order/models/OrderShippingMethodTaxLine/index.html.md) represents a shipping method’s tax line. + +![A diagram showcasing the relation between orders, items and shipping methods, and tax lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307225/Medusa%20Resources/order-tax-lines_sixujd.jpg) + +*** + +## 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 it 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 tax perspective. + +![A diagram showcasing how a subtotal is calculated from the tax perspective](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307395/Medusa%20Resources/order-tax-inclusive_oebdnm.jpg) + +For example, if a line item's amount is `5000`, the tax rate is `10`, and `is_tax_inclusive` is enabled, the tax amount is 10% of `5000`, which is `500`. The item's unit price becomes `4500`. + + +# Transactions + +In this document, you’ll learn about an order’s transactions and its use. + +## What is a Transaction? + +A transaction represents any order payment process, such as capturing or refunding an amount. It’s represented by the [OrderTransaction data model](https://docs.medusajs.com/references/order/models/OrderTransaction/index.html.md). + +The transaction’s main purpose is to ensure a correct balance between paid and outstanding amounts. + +Transactions are also associated with returns, claims, and exchanges if additional payment or refund is required. + +*** + +## Checking Outstanding Amount + +The order’s total amounts are stored in the `OrderSummary`'s `totals` property, which is a JSON object holding the total details of the order. + +```json +{ + "totals": { + "total": 30, + "subtotal": 30, + // ... + } +} +``` + +To check the outstanding amount of the order, its transaction amounts are summed. Then, the following conditions are checked: + +|Condition|Result| +|---|---|---| +|summary’s total - transaction amounts total = 0|There’s no outstanding amount.| +|summary’s total - transaction amounts total > 0|The customer owes additional payment to the merchant.| +|summary’s total - transaction amounts total \< 0|The merchant owes the customer a refund.| + +*** + +## Transaction Reference + +The Order Module doesn’t provide payment processing functionalities, so it doesn’t store payments that can be processed. Payment functionalities are provided by the [Payment Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/index.html.md). + +The `OrderTransaction` data model has two properties that determine which data model and record holds the actual payment’s details: + +- `reference`: indicates the table’s name in the database. For example, `payment` from the Payment Module. +- `reference_id`: indicates the ID of the record in the table. For example, `pay_123`. + + +# Pricing Concepts + +In this document, you’ll learn about the main concepts in the Pricing Module. + +## Price Set + +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). + +Each of these prices are represented by the [Price data module](https://docs.medusajs.com/references/pricing/models/Price/index.html.md). + +![A diagram showcasing the relation between the price set and price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648650/Medusa%20Resources/price-set-money-amount_xeees0.jpg) + +*** + +## Price List + +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. + +A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied. + +Its associated prices are represented by the `Price` data model. + + +# Links between Pricing Module and Other Modules + +This document showcases the module links defined between the Pricing Module and other commerce modules. + +## Summary + +The Pricing Module has the following links to other modules: + +- [`ShippingOption` data model of Fulfillment Module \<> `PriceSet` data model](#fulfillment-module). +- [`ProductVariant` data model of Product Module \<> `PriceSet` data model](#product-module). + +*** + +## Fulfillment Module + +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. + +Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set. + +![A diagram showcasing an example of how data models from the Pricing and Fulfillment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716561747/Medusa%20Resources/pricing-fulfillment_spywwa.jpg) + +### 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. + +![A diagram showcasing an example of how data models from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg) + +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 diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg) + +A price can have multiple price rules. + +For example, a price can be restricted by a region and a zip code. + +![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg) + +*** + +## 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. + +![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg) + + +# Promotion Actions + +In this document, you’ll learn about promotion actions and how they’re computed using the [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md). + +## computeActions Method + +The Promotion Module's main service has a [computeActions method](https://docs.medusajs.com/references/promotion/computeActions/index.html.md) that returns an array of actions to perform on a cart when one or more promotions are applied. + +Actions inform you what adjustment must be made to a cart item or shipping method. Each action is an object having the `action` property indicating the type of action. + +*** + +## Action Types + +### `addItemAdjustment` Action + +The `addItemAdjustment` action indicates that an adjustment must be made to an item. For example, removing $5 off its amount. + +This action has the following format: + +```ts +export interface AddItemAdjustmentAction { + action: "addItemAdjustment" + item_id: string + amount: number + code: string + description?: string +} +``` + +This action means that a new record should be created of the `LineItemAdjustment` data model in the Cart Module, or `OrderLineItemAdjustment` data model in the Order Module. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddItemAdjustmentAction/index.html.md) for details on the object’s properties. + +### `removeItemAdjustment` Action + +The `removeItemAdjustment` action indicates that an adjustment must be removed from a line item. For example, remove the $5 discount. + +The `computeActions` method accepts any previous item adjustments in the `items` property of the second parameter. + +This action has the following format: + +```ts +export interface RemoveItemAdjustmentAction { + action: "removeItemAdjustment" + adjustment_id: string + description?: string + code: string +} +``` + +This action means that a new record should be removed of the `LineItemAdjustment` (or `OrderLineItemAdjustment`) with the specified ID in the `adjustment_id` property. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveItemAdjustmentAction/index.html.md) for details on the object’s properties. + +### `addShippingMethodAdjustment` Action + +The `addShippingMethodAdjustment` action indicates that an adjustment must be made on a shipping method. For example, make the shipping method free. + +This action has the following format: + +```ts +export interface AddShippingMethodAdjustment { + action: "addShippingMethodAdjustment" + shipping_method_id: string + amount: number + code: string + description?: string +} +``` + +This action means that a new record should be created of the `ShippingMethodAdjustment` data model in the Cart Module, or `OrderShippingMethodAdjustment` data model in the Order Module. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.AddShippingMethodAdjustment/index.html.md) for details on the object’s properties. + +### `removeShippingMethodAdjustment` Action + +The `removeShippingMethodAdjustment` action indicates that an adjustment must be removed from a shipping method. For example, remove the free shipping discount. + +The `computeActions` method accepts any previous shipping method adjustments in the `shipping_methods` property of the second parameter. + +This action has the following format: + +```ts +export interface RemoveShippingMethodAdjustment { + action: "removeShippingMethodAdjustment" + adjustment_id: string + code: string +} +``` + +When the Medusa application receives this action type, it removes the `ShippingMethodAdjustment` (or `OrderShippingMethodAdjustment`) with the specified ID in the `adjustment_id` property. + +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.RemoveShippingMethodAdjustment/index.html.md) for details on the object’s properties. + +### `campaignBudgetExceeded` Action + +When the `campaignBudgetExceeded` action is returned, the promotions within a campaign can no longer be used as the campaign budget has been exceeded. -### query.graph +This action has the following format: ```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "region.*", - ], -}) - -// orders.region +export interface CampaignBudgetExceededAction { + action: "campaignBudgetExceeded" + code: string +} ``` -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +Refer to [this reference](https://docs.medusajs.com/references/promotion/interfaces/promotion.CampaignBudgetExceededAction/index.html.md) for details on the object’s properties. -// ... -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "region.*", - ], -}) +# Tax-Inclusive Pricing -// orders.region -``` +In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices. -*** +## What is Tax-Inclusive Pricing? -## Sales Channel Module +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. -Medusa defines a read-only link between the `Order` data model and the [Sales Channel Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel)'s `SalesChannel` data model. This means you can retrieve the details of an order's sales channel, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. +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. -### Retrieve with Query +*** -To retrieve the sales channel of an order with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `sales_channel.*` in `fields`: +## How is Tax-Inclusive Pricing Set? -### query.graph +The [PricePreference data model](https://docs.medusajs.com/references/pricing/PricePreference/index.html.md) holds the tax-inclusive setting for a context. It has two properties that indicate the context: -```ts -const { data: orders } = await query.graph({ - entity: "order", - fields: [ - "sales_channel.*", - ], -}) +- `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`. -// orders.sales_channel -``` +Only `region_id` and `currency_code` are supported as an `attribute` at the moment. -### useQueryGraphStep +The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context. -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +For example: -// ... +```json +{ + "attribute": "currency_code", + "value": "USD", + "is_tax_inclusive": true, +} +``` -const { data: orders } = useQueryGraphStep({ - entity: "order", - fields: [ - "sales_channel.*", - ], -}) +In this example, tax-inclusivity is enabled for the `USD` currency code. -// orders.sales_channel -``` +*** +## Tax-Inclusive Pricing in Price Calculation -# Order Versioning +### Tax Context -In this document, you’ll learn how an order and its details are versioned. +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. -## What's Versioning? +To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context. -Versioning means assigning a version number to a record, such as an order and its items. This is useful to view the different versions of the order following changes in its lifetime. +### Returned Tax Properties -When changes are made on an order, such as an item is added or returned, the order's version changes. +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). -## version Property +- `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. -The `Order` and `OrderSummary` data models have a `version` property that indicates the current version. By default, its value is `1`. +A price is considered tax-inclusive if: -Other order-related data models, such as `OrderItem`, also has a `version` property, but it indicates the version it belongs to. +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 -## How the Version Changes +A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if: -When the order is changed, such as an item is exchanged, this changes the version of the order and its related data: +- 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 -1. The version of the order and its summary is incremented. -2. Related order data that have a `version` property, such as the `OrderItem`, are duplicated. The duplicated item has the new version, whereas the original item has the previous version. -When the order is retrieved, only the related data having the same version is retrieved. +# Application Method +In this document, you'll learn what an application method is. -# Promotions Adjustments in Orders +## What is an Application Method? -In this document, you’ll learn how a promotion is applied to an order’s items and shipping methods using adjustment lines. +The [ApplicationMethod data model](https://docs.medusajs.com/references/promotion/models/ApplicationMethod/index.html.md) defines how a promotion is applied: -## What are Adjustment Lines? +|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?| -An adjustment line indicates a change to a line item or a shipping method’s amount. It’s used to apply promotions or discounts on an order. +## Target Promotion Rules -The [OrderLineItemAdjustment data model](undefined/references/order/models/OrderLineItemAdjustment) represents changes on a line item, and the [OrderShippingMethodAdjustment data model](undefined/references/order/models/OrderShippingMethodAdjustment) represents changes on a shipping method. +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. -![A diagram showcasing the relation between an order, its items and shipping methods, and their adjustment lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712306017/Medusa%20Resources/order-adjustments_myflir.jpg) +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. -The `amount` property of the adjustment line indicates the amount to be discounted from the original amount. +![A diagram showcasing the target\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898273/Medusa%20Resources/application-method-target-rules_hqaymz.jpg) -The ID of the applied promotion is stored in the `promotion_id` property of the adjustment line. +In this example, the promotion is only applied on products in the cart having the SKU `SHIRT`. *** -## Discountable Option +## Buy Promotion Rules -The `OrderLineItem` data model has an `is_discountable` property that indicates whether promotions can be applied to the line item. It’s enabled by default. +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. -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. +The application method has a collection of `PromotionRule` items to define the “buy X” rule. The `buy_rules` property represents this relation. -*** +![A diagram showcasing the buy\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898453/Medusa%20Resources/application-method-buy-rules_djjuhw.jpg) -## Promotion Actions +In this example, the cart must have two products with the SKU `SHIRT` for the promotion to be applied. -When using the Order and Promotion modules together, use the [computeActions method of the Promotion Module’s main service](undefined/references/promotion/computeActions). It retrieves the actions of line items and shipping methods. -Learn more about actions in the [Promotion Module’s documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/promotion/actions). +# Campaign -```ts collapsibleLines="1-10" expandButtonLabel="Show Imports" -import { - ComputeActionAdjustmentLine, - ComputeActionItemLine, - ComputeActionShippingLine, - // ... -} from "@medusajs/framework/types" +In this document, you'll learn about campaigns. -// ... +## What is a Campaign? -// retrieve the order -const order = await orderModuleService.retrieveOrder("ord_123", { - relations: [ - "items.item.adjustments", - "shipping_methods.shipping_method.adjustments", - ], -}) -// retrieve the line item adjustments -const lineItemAdjustments: ComputeActionItemLine[] = [] -order.items.forEach((item) => { - const filteredAdjustments = item.adjustments?.filter( - (adjustment) => adjustment.code !== undefined - ) as unknown as ComputeActionAdjustmentLine[] - if (filteredAdjustments.length) { - lineItemAdjustments.push({ - ...item, - ...item.detail, - adjustments: filteredAdjustments, - }) - } -}) +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. -//retrieve shipping method adjustments -const shippingMethodAdjustments: ComputeActionShippingLine[] = - [] -order.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, - }) - } -}) +![A diagram showcasing the relation between the Campaign and Promotion data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899225/Medusa%20Resources/campagin-promotion_hh3qsi.jpg) -// compute actions -const actions = await promotionModuleService.computeActions( - ["promo_123"], - { - items: lineItemAdjustments, - shipping_methods: shippingMethodAdjustments, - // TODO infer from cart or region - currency_code: "usd", - } -) -``` +*** -The `computeActions` method accepts the existing adjustments of line items and shipping methods to compute the actions accurately. +## Campaign Limits -Then, use the returned `addItemAdjustment` and `addShippingMethodAdjustment` actions to set the order’s line items and the shipping method’s adjustments. +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. -```ts collapsibleLines="1-9" expandButtonLabel="Show Imports" -import { - AddItemAdjustmentAction, - AddShippingMethodAdjustment, - // ... -} from "@medusajs/framework/types" +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. -await orderModuleService.setOrderLineItemAdjustments( - order.id, - actions.filter( - (action) => action.action === "addItemAdjustment" - ) as AddItemAdjustmentAction[] -) +![A diagram showcasing the relation between the Campaign and CampaignBudget data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899463/Medusa%20Resources/campagin-budget_rvqlmi.jpg) -await orderModuleService.setOrderShippingMethodAdjustments( - order.id, - actions.filter( - (action) => - action.action === "addShippingMethodAdjustment" - ) as AddShippingMethodAdjustment[] -) -``` +# Promotion Concepts -# Order Return +In this document, you’ll learn about the main promotion and rule concepts in the Promotion Module. -In this document, you’ll learn about order returns. +## What is a Promotion? -## What is a Return? +A promotion, represented by the [Promotion data model](https://docs.medusajs.com/references/promotion/models/Promotion/index.html.md), is a discount that can be applied on cart items, shipping methods, or entire orders. -A return is the return of items delivered from the customer back to the merchant. It is represented by the [Return data model](undefined/references/order/models/Return). +A promotion has two types: -A return is requested either by the customer from the storefront, or the merchant from the admin. Medusa supports an automated Return Merchandise Authorization (RMA) flow. +- `standard`: A standard promotion with rules. +- `buyget`: “A buy X get Y” promotion with rules. -![Diagram showcasing the automated RMA flow.](https://res.cloudinary.com/dza7lstvk/image/upload/v1719578128/Medusa%20Resources/return-rma_pzprwq.jpg) +|\`standard\`|\`buyget\`| +|---|---| +|A coupon code that gives customers 10% off their entire order.|Buy two shirts and get another for free.| +|A coupon code that gives customers $15 off any shirt in their order.|Buy two shirts and get 10% off the entire order.| +|A discount applied automatically for VIP customers that removes 10% off their shipping method’s amount.|Spend $100 and get free shipping.| -Once the merchant receives the returned items, they mark the return as received. +The Medusa Admin UI may not provide a way to create each of these promotion examples. However, they are supported by the Promotion Module and Medusa's workflows and API routes. *** -## Returned Items +## PromotionRule -The items to be returned are represented by the [ReturnItem data model](references/order/models/ReturnItem). +A promotion can be restricted by a set of rules, each rule is represented by the [PromotionRule data model](https://docs.medusajs.com/references/promotion/models/PromotionRule/index.html.md). -The `ReturnItem` model has two properties storing the item's quantity: +For example, you can create a promotion that only customers of the `VIP` customer group can use. -1. `received_quantity`: The quantity of the item that's received and can be added to the item's inventory quantity. -2. `damaged_quantity`: The quantity of the item that's damaged, meaning it can't be sold again or added to the item's inventory quantity. +![A diagram showcasing the relation between Promotion and PromotionRule](https://res.cloudinary.com/dza7lstvk/image/upload/v1709833196/Medusa%20Resources/promotion-promotion-rule_msbx0w.jpg) -*** +A `PromotionRule`'s `attribute` property indicates the property's name to which this rule is applied. -## Return Shipping Methods +For example, `customer_group_id`. Its value is stored in the `PromotionRuleValue` data model. So, a rule can have multiple values. -A return has shipping methods used to return the items to the merchant. The shipping methods are represented by the [OrderShippingMethod data model](references/order/models/OrderShippingMethod). +When testing whether a promotion can be applied to a cart, the rule's `attribute` property and its values are tested on the cart itself. -In the Medusa application, the shipping method for a return is created only from a shipping option, provided by the Fulfillment Module, that has the rule `is_return` enabled. +For example, the cart's customer must be part of the customer group(s) indicated in the promotion rule's value. *** -## Refund Payment +## Flexible Rules -The `refund_amount` property of the `Return` data model holds the amount a merchant must refund the customer. +The `PromotionRule`'s `operator` property adds more flexibility to the rule’s condition rather than simple equality (`eq`). -The [OrderTransaction data model](references/order/models/OrderTransaction) represents the refunds made for the return. +For example, to restrict the promotion to only `VIP` and `B2B` customer groups: -*** +- Add a `PromotionRule` record with its `attribute` property set to `customer_group_id` and `operator` property to `in`. +- Add two `PromotionRuleValue` records associated with the rule: one with the value `VIP` and the other `B2B`. -## Returns in Exchanges and Claims +![A diagram showcasing the relation between PromotionRule and PromotionRuleValue when a rule has multiple values](https://res.cloudinary.com/dza7lstvk/image/upload/v1709897383/Medusa%20Resources/promotion-promotion-rule-multiple_hctpmt.jpg) -When a merchant creates an exchange or a claim, it includes returning items from the customer. +In this case, a customer’s group must be in the `VIP` and `B2B` set of values to use the promotion. -The `Return` data model also represents the return of these items. In this case, the return is associated with the exchange or claim it was created for. -*** +# Links between Promotion Module and Other Modules -## How Returns Impact an Order’s Version +This document showcases the module links defined between the Promotion Module and other commerce modules. -The order’s version is incremented when: +## Summary -1. A return is requested. -2. A return is marked as received. +The Promotion 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. -# Tax Lines in Order Module +- [`Cart` data model of the Cart Module \<> `Promotion` data model](#cart-module). +- [`LineItemAdjustment` data model of the Cart Module \<> `Promotion` data model](#cart-module). (Read-only). +- [`Order` data model of the Order Module \<> `Promotion` data model](#order-module). -In this document, you’ll learn about tax lines in an order. +*** -## What are Tax Lines? +## Cart Module -A tax line indicates the tax rate of a line item or a shipping method. +A promotion can be applied on line items and shipping methods of a cart. Medusa defines a link between the `Cart` and `Promotion` data models. -The [OrderLineItemTaxLine data model](undefined/references/order/models/OrderLineItemTaxLine) represents a line item’s tax line, and the [OrderShippingMethodTaxLine data model](undefined/references/order/models/OrderShippingMethodTaxLine) represents a shipping method’s tax line. +![A diagram showcasing an example of how data models from the Cart and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711538015/Medusa%20Resources/cart-promotion_kuh9vm.jpg) -![A diagram showcasing the relation between orders, items and shipping methods, and tax lines](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307225/Medusa%20Resources/order-tax-lines_sixujd.jpg) +Medusa also defines a read-only link between the `Promotion` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `LineItemAdjustment` data model. This means you can retrieve the details of the promotion applied on a line item, but you don't manage the links in a pivot table in the database. The promotion of a line item is determined by the `promotion_id` property of the `LineItemAdjustment` data model. -*** +### Retrieve with Query -## Tax Inclusivity +To retrieve the carts that a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: -By default, the tax amount is calculated by taking the tax rate from the line item or shipping method’s amount and then adding it to the item/method’s subtotal. +To retrieve the line item adjustments of a promotion, pass `line_item_adjustments.*` in `fields`. -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. +### query.graph -So, instead of calculating the tax rate and adding it to the item/method’s subtotal, it’s calculated as part of the subtotal. +```ts +const { data: promotions } = await query.graph({ + entity: "promotion", + fields: [ + "carts.*", + ], +}) -The following diagram is a simplified showcase of how a subtotal is calculated from the tax perspective. +// promotions.carts +``` -![A diagram showcasing how a subtotal is calculated from the tax perspective](https://res.cloudinary.com/dza7lstvk/image/upload/v1712307395/Medusa%20Resources/order-tax-inclusive_oebdnm.jpg) +### useQueryGraphStep -For example, if a line item's amount is `5000`, the tax rate is `10`, and `is_tax_inclusive` is enabled, the tax amount is 10% of `5000`, which is `500`. The item's unit price becomes `4500`. +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +// ... -# Transactions +const { data: promotions } = useQueryGraphStep({ + entity: "promotion", + fields: [ + "carts.*", + ], +}) -In this document, you’ll learn about an order’s transactions and its use. +// promotions.carts +``` -## What is a Transaction? +### Manage with Link -A transaction represents any order payment process, such as capturing or refunding an amount. It’s represented by the [OrderTransaction data model](undefined/references/order/models/OrderTransaction). +To manage the promotions of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): -The transaction’s main purpose is to ensure a correct balance between paid and outstanding amounts. +### link.create -Transactions are also associated with returns, claims, and exchanges if additional payment or refund is required. +```ts +import { Modules } from "@medusajs/framework/utils" -*** +// ... -## Checking Outstanding Amount +await link.create({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` -The order’s total amounts are stored in the `OrderSummary`'s `totals` property, which is a JSON object holding the total details of the order. +### createRemoteLinkStep -```json -{ - "totals": { - "total": 30, - "subtotal": 30, - // ... - } -} -``` +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" -To check the outstanding amount of the order, its transaction amounts are summed. Then, the following conditions are checked: +// ... -|Condition|Result| -|---|---|---| -|summary’s total - transaction amounts total = 0|There’s no outstanding amount.| -|summary’s total - transaction amounts total > 0|The customer owes additional payment to the merchant.| -|summary’s total - transaction amounts total \< 0|The merchant owes the customer a refund.| +createRemoteLinkStep({ + [Modules.CART]: { + cart_id: "cart_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` *** -## Transaction Reference +## Order Module -The Order Module doesn’t provide payment processing functionalities, so it doesn’t store payments that can be processed. Payment functionalities are provided by the [Payment Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment). +An order is associated with the promotion applied on it. Medusa defines a link between the `Order` and `Promotion` data models. -The `OrderTransaction` data model has two properties that determine which data model and record holds the actual payment’s details: +![A diagram showcasing an example of how data models from the Order and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716555015/Medusa%20Resources/order-promotion_dgjzzd.jpg) -- `reference`: indicates the table’s name in the database. For example, `payment` from the Payment Module. -- `reference_id`: indicates the ID of the record in the table. For example, `pay_123`. +### Retrieve with Query +To retrieve the orders a promotion is applied on with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: -# Account Holders and Saved Payment Methods +### query.graph -In this documentation, you'll learn about account holders, and how they're used to save payment methods in third-party payment providers. +```ts +const { data: promotions } = await query.graph({ + entity: "promotion", + fields: [ + "orders.*", + ], +}) -Account holders are available starting from Medusa `v2.5.0`. +// promotions.orders +``` -## What's an Account Holder? +### useQueryGraphStep -An account holder represents a customer that can have saved payment methods in a third-party service. It's represented by the `AccountHolder` data model. +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" -It holds fields retrieved from the third-party provider, such as: +// ... -- `external_id`: The ID of the equivalent customer or account holder in the third-party provider. -- `data`: Data returned by the payment provider when the account holder is created. +const { data: promotions } = useQueryGraphStep({ + entity: "promotion", + fields: [ + "orders.*", + ], +}) -A payment provider that supports saving payment methods for customers would create the equivalent of an account holder in the third-party provider. Then, whenever a payment method is saved, it would be saved under the account holder in the third-party provider. +// promotions.orders +``` -*** +### Manage with Link -## Save Payment Methods +To manage the promotion of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): -If a payment provider supports saving payment methods for a customer, they must implement the following methods: +### link.create -- `createAccountHolder`: Creates an account holder in the payment provider. The Payment Module uses this method before creating the account holder in Medusa, and uses the returned data to set fields like `external_id` and `data` in the created `AccountHolder` record. -- `deleteAccountHolder`: Deletes an account holder in the payment provider. The Payment Module uses this method when an account holder is deleted in Medusa. -- `savePaymentMethod`: Saves a payment method for an account holder in the payment provider. -- `listPaymentMethods`: Lists saved payment methods in the third-party service for an account holder. This is useful when displaying the customer's saved payment methods in the storefront. +```ts +import { Modules } from "@medusajs/framework/utils" -Learn more about implementing these methods in the [Create Payment Provider guide](undefined/references/payment/provider). +// ... -*** +await link.create({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` -## Account Holder in Medusa Payment Flows +### createRemoteLinkStep -In the Medusa application, when a payment session is created for a registered customer, the Medusa application uses the Payment Module to create an account holder for the customer. +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" -Consequently, the Payment Module uses the payment provider to create an account holder in the third-party service, then creates the account holder in Medusa. +// ... -This flow is only supported if the chosen payment provider has implemented the necessary [save payment methods](#save-payment-methods). +createRemoteLinkStep({ + [Modules.ORDER]: { + order_id: "order_123", + }, + [Modules.PROMOTION]: { + promotion_id: "promo_123", + }, +}) +``` # Links between Payment Module and Other Modules @@ -22002,11 +22455,11 @@ The Cart Module provides cart-related features, but not payment processing. Medusa defines a link between the `Cart` and `PaymentCollection` data models. A cart has a payment collection which holds all the authorized payment sessions and payments made related to the cart. -Learn more about this relation in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection#usage-with-the-cart-module). +Learn more about this relation in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-collection#usage-with-the-cart-module/index.html.md). ### Retrieve with Query -To retrieve the cart associated with the payment collection with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `cart.*` in `fields`: +To retrieve the cart associated with the payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `cart.*` in `fields`: ### query.graph @@ -22040,7 +22493,7 @@ const { data: paymentCollections } = useQueryGraphStep({ ### Manage with Link -To manage the payment collection of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the payment collection of a cart, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -22086,7 +22539,7 @@ This link is available starting from Medusa `v2.5.0`. ### Retrieve with Query -To retrieve the customer associated with an account holder with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `customer.*` in `fields`: +To retrieve the customer associated with an account holder with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `customer.*` in `fields`: ### query.graph @@ -22120,7 +22573,7 @@ const { data: accountHolders } = useQueryGraphStep({ ### Manage with Link -To manage the account holders of a customer, use [Link](undefined/docs/learn/fundamentals/module-links/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 @@ -22168,7 +22621,7 @@ So, Medusa defines links between the `PaymentCollection` data model and the `Ord ### Retrieve with Query -To retrieve the order of a payment collection with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `order.*` in `fields`: +To retrieve the order of a payment collection with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order.*` in `fields`: ### query.graph @@ -22202,7 +22655,7 @@ const { data: paymentCollections } = useQueryGraphStep({ ### Manage with Link -To manage the payment collections of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the payment collections of an order, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -22251,7 +22704,7 @@ This increases the flexibility of your store. For example, you only show during ### Retrieve with Query -To retrieve the regions of a payment provider with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `regions.*` in `fields`: +To retrieve the regions of a payment provider with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `regions.*` in `fields`: ### query.graph @@ -22285,42 +22738,83 @@ const { data: paymentProviders } = useQueryGraphStep({ ### Manage with Link -To manage the payment providers in a region, use [Link](undefined/docs/learn/fundamentals/module-links/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", + }, +}) +``` + + +# Account Holders and Saved Payment Methods + +In this documentation, you'll learn about account holders, and how they're used to save payment methods in third-party payment providers. + +Account holders are available starting from Medusa `v2.5.0`. + +## What's an Account Holder? + +An account holder represents a customer that can have saved payment methods in a third-party service. It's represented by the `AccountHolder` data model. + +It holds fields retrieved from the third-party provider, such as: + +- `external_id`: The ID of the equivalent customer or account holder in the third-party provider. +- `data`: Data returned by the payment provider when the account holder is created. + +A payment provider that supports saving payment methods for customers would create the equivalent of an account holder in the third-party provider. Then, whenever a payment method is saved, it would be saved under the account holder in the third-party provider. + +*** + +## Save Payment Methods -### link.create +If a payment provider supports saving payment methods for a customer, they must implement the following methods: -```ts -import { Modules } from "@medusajs/framework/utils" +- `createAccountHolder`: Creates an account holder in the payment provider. The Payment Module uses this method before creating the account holder in Medusa, and uses the returned data to set fields like `external_id` and `data` in the created `AccountHolder` record. +- `deleteAccountHolder`: Deletes an account holder in the payment provider. The Payment Module uses this method when an account holder is deleted in Medusa. +- `savePaymentMethod`: Saves a payment method for an account holder in the payment provider. +- `listPaymentMethods`: Lists saved payment methods in the third-party service for an account holder. This is useful when displaying the customer's saved payment methods in the storefront. -// ... +Learn more about implementing these methods in the [Create Payment Provider guide](https://docs.medusajs.com/references/payment/provider/index.html.md). -await link.create({ - [Modules.REGION]: { - region_id: "reg_123", - }, - [Modules.PAYMENT]: { - payment_provider_id: "pp_stripe_stripe", - }, -}) -``` +*** -### createRemoteLinkStep +## Account Holder in Medusa Payment Flows -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" +In the Medusa application, when a payment session is created for a registered customer, the Medusa application uses the Payment Module to create an account holder for the customer. -// ... +Consequently, the Payment Module uses the payment provider to create an account holder in the third-party service, then creates the account holder in Medusa. -createRemoteLinkStep({ - [Modules.REGION]: { - region_id: "reg_123", - }, - [Modules.PAYMENT]: { - payment_provider_id: "pp_stripe_stripe", - }, -}) -``` +This flow is only supported if the chosen payment provider has implemented the necessary [save payment methods](#save-payment-methods). # Payment Module Options @@ -22384,7 +22878,7 @@ In this document, you’ll learn what a payment is and how it's created, capture ## What's a Payment? -When a payment session is authorized, a payment, represented by the [Payment data model](undefined/references/payment/models/Payment), is created. This payment can later be captured or refunded. +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: @@ -22396,7 +22890,7 @@ A payment carries many of the data and relations of a payment session: ## Capture Payments -When a payment is captured, a capture, represented by the [Capture data model](undefined/references/payment/models/Capture), is created. It holds details related to the capture, such as the amount, the capture date, and more. +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. @@ -22406,7 +22900,7 @@ The payment can also be captured incrementally, each time a capture record is cr ## Refund Payments -When a payment is refunded, a refund, represented by the [Refund data model](undefined/references/payment/models/Refund), is created. It holds details related to the refund, such as the amount, refund date, and more. +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. @@ -22419,13 +22913,13 @@ In this document, you’ll learn what a payment collection is and how the Medusa ## 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](undefined/references/payment/models/PaymentCollection). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-session) that represents the payment amount to authorize. -- The [payments](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment) that are created when a payment session is authorized. They can be captured and refunded. -- The [payment providers](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider) that handle the processing of each payment session, including the authorization, capture, and refund. +- 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. *** @@ -22445,7 +22939,7 @@ The Cart Module provides cart management features. However, it doesn’t provide 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-flow). +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). ![Diagram showcasing the relation between the Payment and Cart modules](https://res.cloudinary.com/dza7lstvk/image/upload/v1711537849/Medusa%20Resources/cart-payment_ixziqm.jpg) @@ -22456,7 +22950,7 @@ In this document, you’ll learn how to implement an accept-payment flow using w It's highly recommended to use Medusa's workflows to implement this flow. Use the Payment Module's main service for more complex cases. -For a guide on how to implement this flow in the storefront, check out [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/checkout/payment). +For a guide on how to implement this flow in the storefront, check out [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/index.html.md). ## Flow Overview @@ -22611,8 +23105,8 @@ The payment flow is complete once the payment session is authorized and the paym You can then: -- Capture the payment either using the [capturePaymentWorkflow](undefined/references/medusa-workflows/capturePaymentWorkflow) or [capturePayment method](undefined/references/payment/capturePayment). -- Refund captured amounts using the [refundPaymentWorkflow](undefined/references/medusa-workflows/refundPaymentWorkflow) or [refundPayment method](undefined/references/payment/refundPayment). +- Capture the payment either using the [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/index.html.md) or [capturePayment method](https://docs.medusajs.com/references/payment/capturePayment/index.html.md). +- Refund captured amounts using the [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/index.html.md) or [refundPayment method](https://docs.medusajs.com/references/payment/refundPayment/index.html.md). Some payment providers allow capturing the payment automatically once it’s authorized. In that case, you don’t need to do it manually. @@ -22631,7 +23125,7 @@ After the payment session is authorized, the payment provider is associated with ### List of Payment Module Providers -- [Stripe](undefined/commerce-modules/payment/payment-provider/stripe) +- [Stripe](https://docs.medusajs.com/commerce-modules/payment/payment-provider/stripe/index.html.md) *** @@ -22647,7 +23141,7 @@ It doesn’t handle payment processing and delegates that to the merchant. It ac A payment provider is a module whose main service extends the `AbstractPaymentProvider` imported from `@medusajs/framework/utils`. -Refer to [this guide](undefined/references/payment/provider) on how to create a payment provider for the Payment Module. +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. *** @@ -22655,7 +23149,7 @@ Refer to [this guide](undefined/references/payment/provider) on how to create a The Payment Module accepts a `providers` option that allows you to register providers in your application. -Learn more about this option in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/module-options#providers). +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). *** @@ -22670,566 +23164,69 @@ This data model is used to reference a payment provider and determine whether it In this document, you’ll learn how the Payment Module supports listening to webhook events. -## What's a Webhook Event? - -A webhook event is sent from a third-party payment provider to your application. It indicates a change in a payment’s status. - -This is useful in many cases such as when a payment is being processed asynchronously or when a request is interrupted and the payment provider is sending details on the process later. - -*** - -## getWebhookActionAndData Method - -The Payment Module’s main service has a [getWebhookActionAndData method](undefined/references/payment/getWebhookActionAndData) used to handle incoming webhook events from third-party payment services. The method delegates the handling to the associated payment provider, which returns the event's details. - -Medusa implements a webhook listener route at the `/hooks/payment/[identifier]_[provider]` API route, where: - -- `[identifier]` is the `identifier` static property defined in the payment provider. For example, `stripe`. -- `[provider]` is the ID of the provider. For example, `stripe`. - -For example, when integrating basic Stripe payments with the [Stripe Module Provider](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe), the webhook listener route is `/hooks/payment/stripe_stripe`. If you're integrating Stripe's Bancontact payments, the webhook listener route is `/hooks/payment/stripe-bancontact_stripe`. - -Use that webhook listener in your third-party payment provider's configurations. - -![A diagram showcasing the steps of how the getWebhookActionAndData method words](https://res.cloudinary.com/dza7lstvk/image/upload/v1711567415/Medusa%20Resources/payment-webhook_seaocg.jpg) - -If the event's details indicate that the payment should be authorized, then the [authorizePaymentSession method of the main service](undefined/references/payment/authorizePaymentSession) is executed on the specified payment session. - -If the event's details indicate that the payment should be captured, then the [capturePayment method of the main service](undefined/references/payment/capturePayment) is executed on the payment of the specified payment session. - -### Actions After Webhook Payment Processing - -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. - - -# 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](undefined/references/payment/models/PaymentSession), 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. - -![Diagram showcasing how every payment session has a different payment provider](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565056/Medusa%20Resources/payment-session-provider_guxzqt.jpg) - -*** - -## 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. - - -# Pricing Concepts - -In this document, you’ll learn about the main concepts in the Pricing Module. - -## Price Set - -A [PriceSet](undefined/references/pricing/models/PriceSet) represents a collection of prices that are linked to a resource (for example, a product or a shipping option). - -Each of these prices are represented by the [Price data module](undefined/references/pricing/models/Price). - -![A diagram showcasing the relation between the price set and price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648650/Medusa%20Resources/price-set-money-amount_xeees0.jpg) - -*** - -## Price List - -A [PriceList](undefined/references/pricing/models/PriceList) is a group of prices only enabled if their conditions and rules are satisfied. - -A price list has optional `start_date` and `end_date` properties that indicate the date range in which a price list can be applied. - -Its associated prices are represented by the `Price` data model. - - -# Links between Pricing Module and Other Modules - -This document showcases the module links defined between the Pricing Module and other commerce modules. - -## Summary - -The Pricing Module has the following links to other modules: - -- [`ShippingOption` data model of Fulfillment Module \<> `PriceSet` data model](#fulfillment-module). -- [`ProductVariant` data model of Product Module \<> `PriceSet` data model](#product-module). - -*** - -## Fulfillment Module - -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. - -Medusa defines a link between the `PriceSet` and `ShippingOption` data models. A shipping option's price is stored as a price set. - -![A diagram showcasing an example of how data models from the Pricing and Fulfillment modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716561747/Medusa%20Resources/pricing-fulfillment_spywwa.jpg) - -### Retrieve with Query - -To retrieve the shipping option of a price set with [Query](undefined/docs/learn/fundamentals/module-links/query), 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](undefined/docs/learn/fundamentals/module-links/link): - -### 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. - -![A diagram showcasing an example of how data models from the Pricing and Product Module are linked. The PriceSet is linked to the ProductVariant of the Product Module.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651039/Medusa%20Resources/pricing-product_m4xaut.jpg) - -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](undefined/docs/learn/fundamentals/module-links/query), 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](undefined/docs/learn/fundamentals/module-links/link): - -### 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](undefined/references/pricing/calculatePrices) of the Pricing Module's main service. - -## calculatePrices Method - -The [calculatePrices method](undefined/references/pricing/calculatePrices) 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](undefined/references/pricing/models/PriceRule). - -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 diagram showcasing the relation between the PriceRule and Price](https://res.cloudinary.com/dza7lstvk/image/upload/v1709648772/Medusa%20Resources/price-rule-1_vy8bn9.jpg) - -A price can have multiple price rules. - -For example, a price can be restricted by a region and a zip code. - -![A diagram showcasing the relation between the PriceRule and Price with multiple rules.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709649296/Medusa%20Resources/price-rule-3_pwpocz.jpg) - -*** - -## Price List Rules - -Rules applied to a price list are represented by the [PriceListRule data model](undefined/references/pricing/models/PriceListRule). - -The `rules_count` property of a `PriceList` indicates how many rules are applied to it. - -![A diagram showcasing the relation between the PriceSet, PriceList, Price, RuleType, and PriceListRuleValue](https://res.cloudinary.com/dza7lstvk/image/upload/v1709641999/Medusa%20Resources/price-list_zd10yd.jpg) +## What's a Webhook Event? +A webhook event is sent from a third-party payment provider to your application. It indicates a change in a payment’s status. -# Tax-Inclusive Pricing +This is useful in many cases such as when a payment is being processed asynchronously or when a request is interrupted and the payment provider is sending details on the process later. -In this document, you’ll learn about tax-inclusive pricing and how it's used when calculating prices. +*** -## What is Tax-Inclusive Pricing? +## getWebhookActionAndData Method -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. +The Payment Module’s main service has a [getWebhookActionAndData method](https://docs.medusajs.com/references/payment/getWebhookActionAndData/index.html.md) used to handle incoming webhook events from third-party payment services. The method delegates the handling to the associated payment provider, which returns the event's details. -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. +Medusa implements a webhook listener route at the `/hooks/payment/[identifier]_[provider]` API route, where: -*** +- `[identifier]` is the `identifier` static property defined in the payment provider. For example, `stripe`. +- `[provider]` is the ID of the provider. For example, `stripe`. -## How is Tax-Inclusive Pricing Set? +For example, when integrating basic Stripe payments with the [Stripe Module Provider](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/payment/payment-provider/stripe/index.html.md), the webhook listener route is `/hooks/payment/stripe_stripe`. If you're integrating Stripe's Bancontact payments, the webhook listener route is `/hooks/payment/stripe-bancontact_stripe`. -The [PricePreference data model](undefined/references/pricing/PricePreference) holds the tax-inclusive setting for a context. It has two properties that indicate the context: +Use that webhook listener in your third-party payment provider's configurations. -- `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`. +![A diagram showcasing the steps of how the getWebhookActionAndData method words](https://res.cloudinary.com/dza7lstvk/image/upload/v1711567415/Medusa%20Resources/payment-webhook_seaocg.jpg) -Only `region_id` and `currency_code` are supported as an `attribute` at the moment. +If the event's details indicate that the payment should be authorized, then the [authorizePaymentSession method of the main service](https://docs.medusajs.com/references/payment/authorizePaymentSession/index.html.md) is executed on the specified payment session. -The `is_tax_inclusive` property indicates whether tax-inclusivity is enabled in the specified context. +If the event's details indicate that the payment should be captured, then the [capturePayment method of the main service](https://docs.medusajs.com/references/payment/capturePayment/index.html.md) is executed on the payment of the specified payment session. -For example: +### Actions After Webhook Payment Processing -```json -{ - "attribute": "currency_code", - "value": "USD", - "is_tax_inclusive": true, -} -``` +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. -In this example, tax-inclusivity is enabled for the `USD` currency code. -*** +# Payment Session -## Tax-Inclusive Pricing in Price Calculation +In this document, you’ll learn what a payment session is. -### Tax Context +## What's a Payment Session? -As mentioned in the [Price Calculation documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context), The `calculatePrices` method accepts as a parameter a calculation context. +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. -To get accurate tax results, pass the `region_id` and / or `currency_code` in the calculation context. +A payment collection can have multiple payment sessions. Using this feature, you can implement payment in installments or payments using multiple providers. -### Returned Tax Properties +![Diagram showcasing how every payment session has a different payment provider](https://res.cloudinary.com/dza7lstvk/image/upload/v1711565056/Medusa%20Resources/payment-session-provider_guxzqt.jpg) -The `calculatePrices` method returns two properties related to tax-inclusivity: +*** -Learn more about the returned properties in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object). +## data Property -- `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. +Payment providers may need additional data to process the payment later. The `PaymentSession` data model has a `data` property used to store that data. -A price is considered tax-inclusive if: +For example, the customer's ID in Stripe is stored in the `data` property. -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 +## Payment Session Status -A region’s price preference’s `is_tax_inclusive`'s value takes higher precedence in determining whether a price is tax-inclusive if: +The `status` property of a payment session indicates its current status. Its value can be: -- 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 +- `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. # Links between Product Module and Other Modules @@ -23255,12 +23252,12 @@ Read-only links are used to query data across modules, but the relations aren't Medusa defines read-only links between: -- The `Product` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `LineItem` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `LineItem` data model. -- The `ProductVariant` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `LineItem` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `LineItem` data model. +- The `Product` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `LineItem` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `LineItem` data model. +- The `ProductVariant` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `LineItem` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `LineItem` data model. ### Retrieve with Query -To retrieve the line items of a variant with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `line_items.*` in `fields`: +To retrieve the line items of a variant with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `line_items.*` in `fields`: To retrieve the line items of a product, pass `line_items.*` in `fields`. @@ -23304,7 +23301,7 @@ This link is introduced in [Medusa v2.5.0](https://github.com/medusajs/medusa/re ### Retrieve with Query -To retrieve the shipping profile of a product with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `shipping_profile.*` in `fields`: +To retrieve the shipping profile of a product with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `shipping_profile.*` in `fields`: ### query.graph @@ -23338,7 +23335,7 @@ const { data: products } = useQueryGraphStep({ ### Manage with Link -To manage the shipping profile of a product, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the shipping profile of a product, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -23387,11 +23384,11 @@ Medusa defines a link between the `ProductVariant` and `InventoryItem` data mode When the `manage_inventory` property of a product variant is enabled, you can manage the variant's inventory in different locations through this relation. -Learn more about product variant's inventory management in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory). +Learn more about product variant's inventory management in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/variant-inventory/index.html.md). ### Retrieve with Query -To retrieve the inventory items of a product variant with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `inventory_items.*` in `fields`: +To retrieve the inventory items of a product variant with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `inventory_items.*` in `fields`: ### query.graph @@ -23425,7 +23422,7 @@ const { data: variants } = useQueryGraphStep({ ### Manage with Link -To manage the inventory items of a variant, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the inventory items of a variant, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -23468,12 +23465,12 @@ createRemoteLinkStep({ Medusa defines read-only links between: -- the `Product` data model and the [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order)'s `OrderLineItem` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. -- the `ProductVariant` data model and the [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order)'s `OrderLineItem` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. +- the `Product` data model and the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `OrderLineItem` data model. This means you can retrieve the details of a line item's product, but you don't manage the links in a pivot table in the database. The product of a line item is determined by the `product_id` property of the `OrderLineItem` data model. +- the `ProductVariant` data model and the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `OrderLineItem` data model. This means you can retrieve the details of a line item's variant, but you don't manage the links in a pivot table in the database. The variant of a line item is determined by the `variant_id` property of the `OrderLineItem` data model. ### Retrieve with Query -To retrieve the order line items of a variant with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `order_items.*` in `fields`: +To retrieve the order line items of a variant with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `order_items.*` in `fields`: To retrieve a product's order line items, pass `order_items.*` in `fields`. @@ -23521,492 +23518,19 @@ So, to add prices for a product variant, create a price set and add the prices t ### Retrieve with Query -To retrieve the price set of a variant with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `price_set.*` in `fields`: +To retrieve the price set of a variant with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `price_set.*` in `fields`: ### query.graph ```ts const { data: variants } = await query.graph({ - entity: "variant", - fields: [ - "price_set.*", - ], -}) - -// variants.price_set -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: variants } = useQueryGraphStep({ - entity: "variant", - fields: [ - "price_set.*", - ], -}) - -// variants.price_set -``` - -### Manage with Link - -To manage the price set of a variant, use [Link](undefined/docs/learn/fundamentals/module-links/link): - -### 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", - }, -}) -``` - -*** - -## Sales Channel Module - -The Sales Channel Module provides functionalities to manage multiple selling channels in your store. - -Medusa defines a link between the `Product` and `SalesChannel` data models. A product can have different availability in different sales channels. - -![A diagram showcasing an example of how data models from the Product and Sales Channel modules are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651840/Medusa%20Resources/product-sales-channel_t848ik.jpg) - -### Retrieve with Query - -To retrieve the sales channels of a product with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `sales_channels.*` in `fields`: - -### query.graph - -```ts -const { data: products } = await query.graph({ - entity: "product", - fields: [ - "sales_channels.*", - ], -}) - -// products.sales_channels -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: products } = useQueryGraphStep({ - entity: "product", - fields: [ - "sales_channels.*", - ], -}) - -// products.sales_channels -``` - -### Manage with Link - -To manage the sales channels of a product, use [Link](undefined/docs/learn/fundamentals/module-links/link): - -### link.create - -```ts -import { Modules } from "@medusajs/framework/utils" - -// ... - -await link.create({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", - }, -}) -``` - -### createRemoteLinkStep - -```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" - -// ... - -createRemoteLinkStep({ - [Modules.PRODUCT]: { - product_id: "prod_123", - }, - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", - }, -}) -``` - - -# Product Variant Inventory - -# Product Variant Inventory - -In this guide, you'll learn about the inventory management features related to product variants. - -## Configure Inventory Management of Product Variants - -A product variant, represented by the [ProductVariant](undefined/references/product/models/ProductVariant) data model, has a `manage_inventory` field that's disabled by default. This field indicates whether you'll manage the inventory quantity of the product variant. - -The Product Module doesn't provide inventory-management features. Instead, the Medusa application uses the [Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory) to manage inventory for products and variants. When `manage_inventory` is disabled, the Medusa application always considers the product variant to be in stock. This is useful if your product's variants aren't items that can be stocked, such as digital products, or they don't have a limited stock quantity. - -When `manage_inventory` is enabled, the Medusa application tracks the inventory of the product variant using the [Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory). For example, when a customer purchases a product variant, the Medusa application decrements the stocked quantity of the product variant. - -*** - -## How the Medusa Application Manages Inventory - -When a product variant has `manage_inventory` enabled, the Medusa application creates an inventory item using the [Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory) and links it to the product variant. - -![Diagram showcasing the link between a product variant and its inventory item](https://res.cloudinary.com/dza7lstvk/image/upload/v1709652779/Medusa%20Resources/product-inventory_kmjnud.jpg) - -The inventory item has one or more locations, called inventory levels, that represent the stock quantity of the product variant at a specific location. This allows you to manage inventory across multiple warehouses, such as a warehouse in the US and another in Europe. - -![Diagram showcasing the link between a variant and its inventory item, and the inventory item's level.](https://res.cloudinary.com/dza7lstvk/image/upload/v1738580390/Medusa%20Resources/variant-inventory-level_bbee2t.jpg) - -Learn more about inventory concepts in the [Inventory Module's documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/concepts). - -The Medusa application represents and manages stock locations using the [Stock Location Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/stock-location). It creates a read-only link between the `InventoryLevel` and `StockLocation` data models so that it can retrieve the stock location of an inventory level. - -![Diagram showcasing the read-only link between an inventory level and a stock location](https://res.cloudinary.com/dza7lstvk/image/upload/v1738582163/Medusa%20Resources/inventory-level-stock_amxfg5.jpg) - -Learn more about the Stock Location Module in the [Stock Location Module's documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/stock-location/concepts). - -### Product Inventory in Storefronts - -When a storefront sends a request to the Medusa application, it must always pass a [publishable API key](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys) in the request header. This API key specifies the sales channels, available through the [Sales Channel Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/sales-channel), of the storefront. - -The Medusa application links sales channels to stock locations, indicating the locations available for a specific sales channel. So, all inventory-related operations are scoped by the sales channel and its associated stock locations. - -For example, the availability of a product variant is determined by the `stocked_quantity` of its inventory level at the stock location linked to the storefront's sales channel. - -![Diagram showcasing the overall relations between inventory, stock location, and sales channel concepts](https://res.cloudinary.com/dza7lstvk/image/upload/v1738582163/Medusa%20Resources/inventory-stock-sales_fknoxw.jpg) - -*** - -## Variant Back Orders - -Product variants have an `allow_backorder` field that's disabled by default. When enabled, the Medusa application allows customers to purchase the product variant even when it's out of stock. Use this when your product variant is available through on-demand or pre-order purchase. - -You can also allow customers to subscribe to restock notifications of a product variant as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/recipes/commerce-automation/restock-notification). - -*** - -## Additional Resources - -The following guides provide more details on inventory management in the Medusa application: - -- [Inventory Kits in the Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit): Learn how you can implement bundled or multi-part products through the Inventory Module. -- [Inventory in Flows](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-in-flows): Learn how Medusa utilizes inventory management in different flows. -- [Storefront guide: how to retrieve a product variant's inventory details](https://docs.medusajs.com/resources/storefront-development/products/inventory/index.html.md). - - -# Promotion Actions - -In this document, you’ll learn about promotion actions and how they’re computed using the [computeActions method](undefined/references/promotion/computeActions). - -## computeActions Method - -The Promotion Module's main service has a [computeActions method](undefined/references/promotion/computeActions) that returns an array of actions to perform on a cart when one or more promotions are applied. - -Actions inform you what adjustment must be made to a cart item or shipping method. Each action is an object having the `action` property indicating the type of action. - -*** - -## Action Types - -### `addItemAdjustment` Action - -The `addItemAdjustment` action indicates that an adjustment must be made to an item. For example, removing $5 off its amount. - -This action has the following format: - -```ts -export interface AddItemAdjustmentAction { - action: "addItemAdjustment" - item_id: string - amount: number - code: string - description?: string -} -``` - -This action means that a new record should be created of the `LineItemAdjustment` data model in the Cart Module, or `OrderLineItemAdjustment` data model in the Order Module. - -Refer to [this reference](undefined/references/promotion/interfaces/promotion.AddItemAdjustmentAction) for details on the object’s properties. - -### `removeItemAdjustment` Action - -The `removeItemAdjustment` action indicates that an adjustment must be removed from a line item. For example, remove the $5 discount. - -The `computeActions` method accepts any previous item adjustments in the `items` property of the second parameter. - -This action has the following format: - -```ts -export interface RemoveItemAdjustmentAction { - action: "removeItemAdjustment" - adjustment_id: string - description?: string - code: string -} -``` - -This action means that a new record should be removed of the `LineItemAdjustment` (or `OrderLineItemAdjustment`) with the specified ID in the `adjustment_id` property. - -Refer to [this reference](undefined/references/promotion/interfaces/promotion.RemoveItemAdjustmentAction) for details on the object’s properties. - -### `addShippingMethodAdjustment` Action - -The `addShippingMethodAdjustment` action indicates that an adjustment must be made on a shipping method. For example, make the shipping method free. - -This action has the following format: - -```ts -export interface AddShippingMethodAdjustment { - action: "addShippingMethodAdjustment" - shipping_method_id: string - amount: number - code: string - description?: string -} -``` - -This action means that a new record should be created of the `ShippingMethodAdjustment` data model in the Cart Module, or `OrderShippingMethodAdjustment` data model in the Order Module. - -Refer to [this reference](undefined/references/promotion/interfaces/promotion.AddShippingMethodAdjustment) for details on the object’s properties. - -### `removeShippingMethodAdjustment` Action - -The `removeShippingMethodAdjustment` action indicates that an adjustment must be removed from a shipping method. For example, remove the free shipping discount. - -The `computeActions` method accepts any previous shipping method adjustments in the `shipping_methods` property of the second parameter. - -This action has the following format: - -```ts -export interface RemoveShippingMethodAdjustment { - action: "removeShippingMethodAdjustment" - adjustment_id: string - code: string -} -``` - -When the Medusa application receives this action type, it removes the `ShippingMethodAdjustment` (or `OrderShippingMethodAdjustment`) with the specified ID in the `adjustment_id` property. - -Refer to [this reference](undefined/references/promotion/interfaces/promotion.RemoveShippingMethodAdjustment) for details on the object’s properties. - -### `campaignBudgetExceeded` Action - -When the `campaignBudgetExceeded` action is returned, the promotions within a campaign can no longer be used as the campaign budget has been exceeded. - -This action has the following format: - -```ts -export interface CampaignBudgetExceededAction { - action: "campaignBudgetExceeded" - code: string -} -``` - -Refer to [this reference](undefined/references/promotion/interfaces/promotion.CampaignBudgetExceededAction) for details on the object’s properties. - - -# Application Method - -In this document, you'll learn what an application method is. - -## What is an Application Method? - -The [ApplicationMethod data model](undefined/references/promotion/models/ApplicationMethod) 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. - -![A diagram showcasing the target\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898273/Medusa%20Resources/application-method-target-rules_hqaymz.jpg) - -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. - -![A diagram showcasing the buy\_rules relation between the ApplicationMethod and PromotionRule data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709898453/Medusa%20Resources/application-method-buy-rules_djjuhw.jpg) - -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. - -## What is a Campaign? - -A [Campaign](undefined/references/promotion/models/Campaign) combines promotions under the same conditions, such as start and end dates. - -![A diagram showcasing the relation between the Campaign and Promotion data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899225/Medusa%20Resources/campagin-promotion_hh3qsi.jpg) - -*** - -## Campaign Limits - -Each campaign has a budget represented by the [CampaignBudget data model](undefined/references/promotion/models/CampaignBudget). 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. - -![A diagram showcasing the relation between the Campaign and CampaignBudget data models](https://res.cloudinary.com/dza7lstvk/image/upload/v1709899463/Medusa%20Resources/campagin-budget_rvqlmi.jpg) - - -# Promotion Concepts - -In this document, you’ll learn about the main promotion and rule concepts in the Promotion Module. - -## What is a Promotion? - -A promotion, represented by the [Promotion data model](undefined/references/promotion/models/Promotion), is a discount that can be applied on cart items, shipping methods, or entire orders. - -A promotion has two types: - -- `standard`: A standard promotion with rules. -- `buyget`: “A buy X get Y” promotion with rules. - -|\`standard\`|\`buyget\`| -|---|---| -|A coupon code that gives customers 10% off their entire order.|Buy two shirts and get another for free.| -|A coupon code that gives customers $15 off any shirt in their order.|Buy two shirts and get 10% off the entire order.| -|A discount applied automatically for VIP customers that removes 10% off their shipping method’s amount.|Spend $100 and get free shipping.| - -The Medusa Admin UI may not provide a way to create each of these promotion examples. However, they are supported by the Promotion Module and Medusa's workflows and API routes. - -*** - -## PromotionRule - -A promotion can be restricted by a set of rules, each rule is represented by the [PromotionRule data model](undefined/references/promotion/models/PromotionRule). - -For example, you can create a promotion that only customers of the `VIP` customer group can use. - -![A diagram showcasing the relation between Promotion and PromotionRule](https://res.cloudinary.com/dza7lstvk/image/upload/v1709833196/Medusa%20Resources/promotion-promotion-rule_msbx0w.jpg) - -A `PromotionRule`'s `attribute` property indicates the property's name to which this rule is applied. - -For example, `customer_group_id`. Its value is stored in the `PromotionRuleValue` data model. So, a rule can have multiple values. - -When testing whether a promotion can be applied to a cart, the rule's `attribute` property and its values are tested on the cart itself. - -For example, the cart's customer must be part of the customer group(s) indicated in the promotion rule's value. - -*** - -## Flexible Rules - -The `PromotionRule`'s `operator` property adds more flexibility to the rule’s condition rather than simple equality (`eq`). - -For example, to restrict the promotion to only `VIP` and `B2B` customer groups: - -- Add a `PromotionRule` record with its `attribute` property set to `customer_group_id` and `operator` property to `in`. -- Add two `PromotionRuleValue` records associated with the rule: one with the value `VIP` and the other `B2B`. - -![A diagram showcasing the relation between PromotionRule and PromotionRuleValue when a rule has multiple values](https://res.cloudinary.com/dza7lstvk/image/upload/v1709897383/Medusa%20Resources/promotion-promotion-rule-multiple_hctpmt.jpg) - -In this case, a customer’s group must be in the `VIP` and `B2B` set of values to use the promotion. - - -# Links between Promotion Module and Other Modules - -This document showcases the module links defined between the Promotion Module and other commerce modules. - -## Summary - -The Promotion 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. - -- [`Cart` data model of the Cart Module \<> `Promotion` data model](#cart-module). -- [`LineItemAdjustment` data model of the Cart Module \<> `Promotion` data model](#cart-module). (Read-only). -- [`Order` data model of the Order Module \<> `Promotion` data model](#order-module). - -*** - -## Cart Module - -A promotion can be applied on line items and shipping methods of a cart. Medusa defines a link between the `Cart` and `Promotion` data models. - -![A diagram showcasing an example of how data models from the Cart and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711538015/Medusa%20Resources/cart-promotion_kuh9vm.jpg) - -Medusa also defines a read-only link between the `Promotion` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `LineItemAdjustment` data model. This means you can retrieve the details of the promotion applied on a line item, but you don't manage the links in a pivot table in the database. The promotion of a line item is determined by the `promotion_id` property of the `LineItemAdjustment` data model. - -### Retrieve with Query - -To retrieve the carts that a promotion is applied on with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `carts.*` in `fields`: - -To retrieve the line item adjustments of a promotion, pass `line_item_adjustments.*` in `fields`. - -### query.graph - -```ts -const { data: promotions } = await query.graph({ - entity: "promotion", + entity: "variant", fields: [ - "carts.*", + "price_set.*", ], }) -// promotions.carts +// variants.price_set ``` ### useQueryGraphStep @@ -24016,19 +23540,19 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: promotions } = useQueryGraphStep({ - entity: "promotion", +const { data: variants } = useQueryGraphStep({ + entity: "variant", fields: [ - "carts.*", + "price_set.*", ], }) -// promotions.carts +// variants.price_set ``` ### Manage with Link -To manage the promotions of a cart, use [Link](undefined/docs/learn/fundamentals/module-links/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 @@ -24038,11 +23562,11 @@ import { Modules } from "@medusajs/framework/utils" // ... await link.create({ - [Modules.CART]: { - cart_id: "cart_123", + [Modules.PRODUCT]: { + variant_id: "variant_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", + [Modules.PRICING]: { + price_set_id: "pset_123", }, }) ``` @@ -24056,38 +23580,40 @@ import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" // ... createRemoteLinkStep({ - [Modules.CART]: { - cart_id: "cart_123", + [Modules.PRODUCT]: { + variant_id: "variant_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", + [Modules.PRICING]: { + price_set_id: "pset_123", }, }) ``` *** -## Order Module +## Sales Channel Module -An order is associated with the promotion applied on it. Medusa defines a link between the `Order` and `Promotion` data models. +The Sales Channel Module provides functionalities to manage multiple selling channels in your store. -![A diagram showcasing an example of how data models from the Order and Promotion modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716555015/Medusa%20Resources/order-promotion_dgjzzd.jpg) +Medusa defines a link between the `Product` and `SalesChannel` data models. A product can have different availability in different sales channels. + +![A diagram showcasing an example of how data models from the Product and Sales Channel modules are linked.](https://res.cloudinary.com/dza7lstvk/image/upload/v1709651840/Medusa%20Resources/product-sales-channel_t848ik.jpg) ### Retrieve with Query -To retrieve the orders a promotion is applied on with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `orders.*` in `fields`: +To retrieve the sales channels of a product with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channels.*` in `fields`: ### query.graph ```ts -const { data: promotions } = await query.graph({ - entity: "promotion", +const { data: products } = await query.graph({ + entity: "product", fields: [ - "orders.*", + "sales_channels.*", ], }) -// promotions.orders +// products.sales_channels ``` ### useQueryGraphStep @@ -24097,19 +23623,19 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: promotions } = useQueryGraphStep({ - entity: "promotion", +const { data: products } = useQueryGraphStep({ + entity: "product", fields: [ - "orders.*", + "sales_channels.*", ], }) -// promotions.orders +// products.sales_channels ``` ### Manage with Link -To manage the promotion of an order, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the sales channels of a product, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -24119,11 +23645,11 @@ import { Modules } from "@medusajs/framework/utils" // ... await link.create({ - [Modules.ORDER]: { - order_id: "order_123", + [Modules.PRODUCT]: { + product_id: "prod_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", + [Modules.SALES_CHANNEL]: { + sales_channel_id: "sc_123", }, }) ``` @@ -24137,158 +23663,154 @@ import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" // ... createRemoteLinkStep({ - [Modules.ORDER]: { - order_id: "order_123", + [Modules.PRODUCT]: { + product_id: "prod_123", }, - [Modules.PROMOTION]: { - promotion_id: "promo_123", + [Modules.SALES_CHANNEL]: { + sales_channel_id: "sc_123", }, }) ``` -# Stock Location Concepts +# Product Variant Inventory -In this document, you’ll learn about the main concepts in the Stock Location Module. +# Product Variant Inventory -## Stock Location +In this guide, you'll learn about the inventory management features related to product variants. -A stock location, represented by the `StockLocation` data model, represents a location where stock items are kept. For example, a warehouse. +## Configure Inventory Management of Product Variants -Medusa uses stock locations to provide inventory details, from the Inventory Module, per location. +A product variant, represented by the [ProductVariant](https://docs.medusajs.com/references/product/models/ProductVariant/index.html.md) data model, has a `manage_inventory` field that's disabled by default. This field indicates whether you'll manage the inventory quantity of the product variant. + +The Product Module doesn't provide inventory-management features. Instead, the Medusa application uses the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md) to manage inventory for products and variants. When `manage_inventory` is disabled, the Medusa application always considers the product variant to be in stock. This is useful if your product's variants aren't items that can be stocked, such as digital products, or they don't have a limited stock quantity. + +When `manage_inventory` is enabled, the Medusa application tracks the inventory of the product variant using the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md). For example, when a customer purchases a product variant, the Medusa application decrements the stocked quantity of the product variant. *** -## StockLocationAddress +## How the Medusa Application Manages Inventory -The `StockLocationAddress` data model belongs to the `StockLocation` data model. It provides more detailed information of the location, such as country code or street address. +When a product variant has `manage_inventory` enabled, the Medusa application creates an inventory item using the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md) and links it to the product variant. +![Diagram showcasing the link between a product variant and its inventory item](https://res.cloudinary.com/dza7lstvk/image/upload/v1709652779/Medusa%20Resources/product-inventory_kmjnud.jpg) -# Links between Stock Location Module and Other Modules +The inventory item has one or more locations, called inventory levels, that represent the stock quantity of the product variant at a specific location. This allows you to manage inventory across multiple warehouses, such as a warehouse in the US and another in Europe. -This document showcases the module links defined between the Stock Location Module and other commerce modules. +![Diagram showcasing the link between a variant and its inventory item, and the inventory item's level.](https://res.cloudinary.com/dza7lstvk/image/upload/v1738580390/Medusa%20Resources/variant-inventory-level_bbee2t.jpg) -## Summary +Learn more about inventory concepts in the [Inventory Module's documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/concepts/index.html.md). -The Stock Location Module has the following links to other modules: +The Medusa application represents and manages stock locations using the [Stock Location Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/index.html.md). It creates a read-only link between the `InventoryLevel` and `StockLocation` data models so that it can retrieve the stock location of an inventory level. -Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. +![Diagram showcasing the read-only link between an inventory level and a stock location](https://res.cloudinary.com/dza7lstvk/image/upload/v1738582163/Medusa%20Resources/inventory-level-stock_amxfg5.jpg) -- [`FulfillmentSet` data model of the Fulfillment Module \<> `StockLocation` data model](#fulfillment-module). -- [`FulfillmentProvider` data model of the Fulfillment Module \<> `StockLocation` data model](#fulfillment-module). -- [`StockLocation` data model \<> `Inventory` data model of the Inventory Module](#inventory-module). -- [`SalesChannel` data model of the Sales Channel Module \<> `StockLocation` data model](#sales-channel-module). +Learn more about the Stock Location Module in the [Stock Location Module's documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/stock-location/concepts/index.html.md). -*** +### Product Inventory in Storefronts -## Fulfillment Module +When a storefront sends a request to the Medusa application, it must always pass a [publishable API key](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/publishable-api-keys/index.html.md) in the request header. This API key specifies the sales channels, available through the [Sales Channel Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/sales-channel/index.html.md), of the storefront. -A fulfillment set can be conditioned to a specific stock location. +The Medusa application links sales channels to stock locations, indicating the locations available for a specific sales channel. So, all inventory-related operations are scoped by the sales channel and its associated stock locations. -Medusa defines a link between the `FulfillmentSet` and `StockLocation` data models. +For example, the availability of a product variant is determined by the `stocked_quantity` of its inventory level at the stock location linked to the storefront's sales channel. -![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg) +![Diagram showcasing the overall relations between inventory, stock location, and sales channel concepts](https://res.cloudinary.com/dza7lstvk/image/upload/v1738582163/Medusa%20Resources/inventory-stock-sales_fknoxw.jpg) -Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` data models to indicate the providers that can be used in a location. +*** -![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399492/Medusa%20Resources/fulfillment-provider-stock-location_b0mulo.jpg) +## Variant Back Orders -### Retrieve with Query +Product variants have an `allow_backorder` field that's disabled by default. When enabled, the Medusa application allows customers to purchase the product variant even when it's out of stock. Use this when your product variant is available through on-demand or pre-order purchase. -To retrieve the fulfillment sets of a stock location with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `fulfillment_sets.*` in `fields`: +You can also allow customers to subscribe to restock notifications of a product variant as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/recipes/commerce-automation/restock-notification/index.html.md). -To retrieve the fulfillment providers, pass `fulfillment_providers.*` in `fields`. +*** -### query.graph +## Additional Resources -```ts -const { data: stockLocations } = await query.graph({ - entity: "stock_location", - fields: [ - "fulfillment_sets.*", - ], -}) +The following guides provide more details on inventory management in the Medusa application: -// stockLocations.fulfillment_sets -``` +- [Inventory Kits in the Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-kit/index.html.md): Learn how you can implement bundled or multi-part products through the Inventory Module. +- [Inventory in Flows](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/inventory-in-flows/index.html.md): Learn how Medusa utilizes inventory management in different flows. +- [Storefront guide: how to retrieve a product variant's inventory details](https://docs.medusajs.com/resources/storefront-development/products/inventory/index.html.md). -### useQueryGraphStep -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" +# Links between Region Module and Other Modules -// ... +This document showcases the module links defined between the Region Module and other commerce modules. -const { data: stockLocations } = useQueryGraphStep({ - entity: "stock_location", - fields: [ - "fulfillment_sets.*", - ], -}) +## Summary -// stockLocations.fulfillment_sets -``` +The Region Module has the following links to other modules: -### Manage with Link +Read-only links are used to query data across modules, but the relations aren't stored in a pivot table in the database. -To manage the stock location of a fulfillment set, use [Link](undefined/docs/learn/fundamentals/module-links/link): +- [`Region` data model \<> `Cart` data model of the Cart Module](#cart-module). (Read-only) +- [`Region` data model \<> `Order` data model of the Order Module](#order-module). (Read-only) +- [`Region` data model \<> `PaymentProvider` data model of the Payment Module](#payment-module). -### link.create +*** -```ts -import { Modules } from "@medusajs/framework/utils" +## Cart Module -// ... +Medusa defines a read-only link between the `Region` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model. This means you can retrieve the details of a region's carts, but you don't manage the links in a pivot table in the database. The region of a cart is determined by the `region_id` property of the `Cart` data model. -await link.create({ - [Modules.STOCK_LOCATION]: { - stock_location_id: "sloc_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_set_id: "fset_123", - }, +### Retrieve with Query + +To retrieve the carts of a region with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: + +### query.graph + +```ts +const { data: regions } = await query.graph({ + entity: "region", + fields: [ + "carts.*", + ], }) + +// regions.carts ``` -### createRemoteLinkStep +### useQueryGraphStep ```ts -import { Modules } from "@medusajs/framework/utils" -import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -createRemoteLinkStep({ - [Modules.STOCK_LOCATION]: { - stock_location_id: "sloc_123", - }, - [Modules.FULFILLMENT]: { - fulfillment_set_id: "fset_123", - }, +const { data: regions } = useQueryGraphStep({ + entity: "region", + fields: [ + "carts.*", + ], }) + +// regions.carts ``` *** -## Inventory Module +## Order Module -Medusa defines a read-only link between the `StockLocation` data model and the [Inventory Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/inventory)'s `InventoryLevel` data model. This means you can retrieve the details of a stock location's inventory levels, but you don't manage the links in a pivot table in the database. The stock location of an inventory level is determined by the `location_id` property of the `InventoryLevel` data model. +Medusa defines a read-only link between the `Region` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model. This means you can retrieve the details of a region's orders, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. ### Retrieve with Query -To retrieve the inventory levels of a stock location with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `inventory_levels.*` in `fields`: +To retrieve the orders of a region with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: ### query.graph ```ts -const { data: stockLocations } = await query.graph({ - entity: "stock_location", +const { data: regions } = await query.graph({ + entity: "region", fields: [ - "inventory_levels.*", + "orders.*", ], }) -// stockLocations.inventory_levels +// regions.orders ``` ### useQueryGraphStep @@ -24298,41 +23820,41 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: stockLocations } = useQueryGraphStep({ - entity: "stock_location", +const { data: regions } = useQueryGraphStep({ + entity: "region", fields: [ - "inventory_levels.*", + "orders.*", ], }) -// stockLocations.inventory_levels +// regions.orders ``` *** -## Sales Channel Module +## Payment Module -A stock location is associated with a sales channel. This scopes inventory quantities in a stock location by the associated sales channel. +You can specify for each region which payment providers are available for use. -Medusa defines a link between the `SalesChannel` and `StockLocation` data models. +Medusa defines a module link between the `PaymentProvider` and the `Region` data models. -![A diagram showcasing an example of how resources from the Sales Channel and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716796872/Medusa%20Resources/sales-channel-location_cqrih1.jpg) +![A diagram showcasing an example of how resources from the Payment and Region modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711569520/Medusa%20Resources/payment-region_jyo2dz.jpg) ### Retrieve with Query -To retrieve the sales channels of a stock location with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `sales_channels.*` in `fields`: +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: stockLocations } = await query.graph({ - entity: "stock_location", +const { data: regions } = await query.graph({ + entity: "region", fields: [ - "sales_channels.*", + "payment_providers.*", ], }) -// stockLocations.sales_channels +// regions.payment_providers ``` ### useQueryGraphStep @@ -24342,19 +23864,19 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows" // ... -const { data: stockLocations } = useQueryGraphStep({ - entity: "stock_location", +const { data: regions } = useQueryGraphStep({ + entity: "region", fields: [ - "sales_channels.*", + "payment_providers.*", ], }) -// stockLocations.sales_channels +// regions.payment_providers ``` ### Manage with Link -To manage the stock locations of a sales channel, use [Link](undefined/docs/learn/fundamentals/module-links/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 @@ -24364,11 +23886,11 @@ import { Modules } from "@medusajs/framework/utils" // ... await link.create({ - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", + [Modules.REGION]: { + region_id: "reg_123", }, - [Modules.STOCK_LOCATION]: { - sales_channel_id: "sloc_123", + [Modules.PAYMENT]: { + payment_provider_id: "pp_stripe_stripe", }, }) ``` @@ -24382,11 +23904,11 @@ import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" // ... createRemoteLinkStep({ - [Modules.SALES_CHANNEL]: { - sales_channel_id: "sc_123", + [Modules.REGION]: { + region_id: "reg_123", }, - [Modules.STOCK_LOCATION]: { - sales_channel_id: "sloc_123", + [Modules.PAYMENT]: { + payment_provider_id: "pp_stripe_stripe", }, }) ``` @@ -24420,7 +23942,7 @@ Medusa defines a link between the `ApiKey` and the `SalesChannel` data models. ### Retrieve with Query -To retrieve the API keys associated with a sales channel with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `publishable_api_keys.*` in `fields`: +To retrieve the API keys associated with a sales channel with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `publishable_api_keys.*` in `fields`: ### query.graph @@ -24454,7 +23976,7 @@ const { data: salesChannels } = useQueryGraphStep({ ### Manage with Link -To manage the sales channels of an API key, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the sales channels of an API key, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -24495,11 +24017,11 @@ createRemoteLinkStep({ ## Cart Module -Medusa defines a read-only link between the `SalesChannel` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `Cart` data model. This means you can retrieve the details of a sales channel's carts, but you don't manage the links in a pivot table in the database. The sales channel of a cart is determined by the `sales_channel_id` property of the `Cart` data model. +Medusa defines a read-only link between the `SalesChannel` data model and the [Cart Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/cart/index.html.md)'s `Cart` data model. This means you can retrieve the details of a sales channel's carts, but you don't manage the links in a pivot table in the database. The sales channel of a cart is determined by the `sales_channel_id` property of the `Cart` data model. ### Retrieve with Query -To retrieve the carts of a sales channel with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `carts.*` in `fields`: +To retrieve the carts of a sales channel with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `carts.*` in `fields`: ### query.graph @@ -24535,11 +24057,11 @@ const { data: salesChannels } = useQueryGraphStep({ ## Order Module -Medusa defines a read-only link between the `SalesChannel` data model and the [Order Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/order)'s `Order` data model. This means you can retrieve the details of a sales channel's orders, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. +Medusa defines a read-only link between the `SalesChannel` data model and the [Order Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/order/index.html.md)'s `Order` data model. This means you can retrieve the details of a sales channel's orders, but you don't manage the links in a pivot table in the database. The sales channel of an order is determined by the `sales_channel_id` property of the `Order` data model. ### Retrieve with Query -To retrieve the orders of a sales channel with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `orders.*` in `fields`: +To retrieve the orders of a sales channel with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `orders.*` in `fields`: ### query.graph @@ -24583,7 +24105,7 @@ A product can be available in more than one sales channel. You can retrieve only ### Retrieve with Query -To retrieve the products of a sales channel with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `products.*` in `fields`: +To retrieve the products of a sales channel with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `products.*` in `fields`: ### query.graph @@ -24617,7 +24139,7 @@ const { data: salesChannels } = useQueryGraphStep({ ### Manage with Link -To manage the sales channels of a product, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the sales channels of a product, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -24666,7 +24188,7 @@ Medusa defines a link between the `SalesChannel` and `StockLocation` data models ### Retrieve with Query -To retrieve the stock locations of a sales channel with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `stock_locations.*` in `fields`: +To retrieve the stock locations of a sales channel with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `stock_locations.*` in `fields`: ### query.graph @@ -24700,7 +24222,276 @@ const { data: salesChannels } = useQueryGraphStep({ ### Manage with Link -To manage the stock locations of a sales channel, use [Link](undefined/docs/learn/fundamentals/module-links/link): +To manage the stock locations of a sales channel, 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.SALES_CHANNEL]: { + sales_channel_id: "sc_123", + }, + [Modules.STOCK_LOCATION]: { + sales_channel_id: "sloc_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.SALES_CHANNEL]: { + sales_channel_id: "sc_123", + }, + [Modules.STOCK_LOCATION]: { + sales_channel_id: "sloc_123", + }, +}) +``` + + +# Publishable API Keys with Sales Channels + +In this document, you’ll learn what publishable API keys are and how to use them with sales channels. + +## Publishable API Keys with Sales Channels + +A publishable API key, provided by the API Key Module, is a client key scoped to one or more sales channels. + +When sending a request to a Store API route, you must pass a publishable API key in the header of the request: + +```bash +curl http://localhost:9000/store/products \ + x-publishable-api-key: {your_publishable_api_key} +``` + +The Medusa application infers the associated sales channels and ensures that only data relevant to the sales channel are used. + +*** + +## How to Create a Publishable API Key? + +To create a publishable API key, either use the Medusa Admin or the [Admin API Routes](https://docs.medusajs.com/api/admin#publishable-api-keys). + + +# Stock Location Concepts + +In this document, you’ll learn about the main concepts in the Stock Location Module. + +## Stock Location + +A stock location, represented by the `StockLocation` data model, represents a location where stock items are kept. For example, a warehouse. + +Medusa uses stock locations to provide inventory details, from the Inventory Module, per location. + +*** + +## StockLocationAddress + +The `StockLocationAddress` data model belongs to the `StockLocation` data model. It provides more detailed information of the location, such as country code or street address. + + +# Links between Stock Location Module and Other Modules + +This document showcases the module links defined between the Stock Location Module and other commerce modules. + +## Summary + +The Stock Location 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. + +- [`FulfillmentSet` data model of the Fulfillment Module \<> `StockLocation` data model](#fulfillment-module). +- [`FulfillmentProvider` data model of the Fulfillment Module \<> `StockLocation` data model](#fulfillment-module). +- [`StockLocation` data model \<> `Inventory` data model of the Inventory Module](#inventory-module). +- [`SalesChannel` data model of the Sales Channel Module \<> `StockLocation` data model](#sales-channel-module). + +*** + +## Fulfillment Module + +A fulfillment set can be conditioned to a specific stock location. + +Medusa defines a link between the `FulfillmentSet` and `StockLocation` data models. + +![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1712567101/Medusa%20Resources/fulfillment-stock-location_nlkf7e.jpg) + +Medusa also defines a link between the `FulfillmentProvider` and `StockLocation` data models to indicate the providers that can be used in a location. + +![A diagram showcasing an example of how data models from the Fulfillment and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1728399492/Medusa%20Resources/fulfillment-provider-stock-location_b0mulo.jpg) + +### Retrieve with Query + +To retrieve the fulfillment sets of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `fulfillment_sets.*` in `fields`: + +To retrieve the fulfillment providers, pass `fulfillment_providers.*` in `fields`. + +### query.graph + +```ts +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: [ + "fulfillment_sets.*", + ], +}) + +// stockLocations.fulfillment_sets +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stockLocations } = useQueryGraphStep({ + entity: "stock_location", + fields: [ + "fulfillment_sets.*", + ], +}) + +// stockLocations.fulfillment_sets +``` + +### Manage with Link + +To manage the stock location of a fulfillment set, 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.STOCK_LOCATION]: { + stock_location_id: "sloc_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: "fset_123", + }, +}) +``` + +### createRemoteLinkStep + +```ts +import { Modules } from "@medusajs/framework/utils" +import { createRemoteLinkStep } from "@medusajs/medusa/core-flows" + +// ... + +createRemoteLinkStep({ + [Modules.STOCK_LOCATION]: { + stock_location_id: "sloc_123", + }, + [Modules.FULFILLMENT]: { + fulfillment_set_id: "fset_123", + }, +}) +``` + +*** + +## Inventory Module + +Medusa defines a read-only link between the `StockLocation` data model and the [Inventory Module](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/inventory/index.html.md)'s `InventoryLevel` data model. This means you can retrieve the details of a stock location's inventory levels, but you don't manage the links in a pivot table in the database. The stock location of an inventory level is determined by the `location_id` property of the `InventoryLevel` data model. + +### Retrieve with Query + +To retrieve the inventory levels of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `inventory_levels.*` in `fields`: + +### query.graph + +```ts +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: [ + "inventory_levels.*", + ], +}) + +// stockLocations.inventory_levels +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stockLocations } = useQueryGraphStep({ + entity: "stock_location", + fields: [ + "inventory_levels.*", + ], +}) + +// stockLocations.inventory_levels +``` + +*** + +## Sales Channel Module + +A stock location is associated with a sales channel. This scopes inventory quantities in a stock location by the associated sales channel. + +Medusa defines a link between the `SalesChannel` and `StockLocation` data models. + +![A diagram showcasing an example of how resources from the Sales Channel and Stock Location modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1716796872/Medusa%20Resources/sales-channel-location_cqrih1.jpg) + +### Retrieve with Query + +To retrieve the sales channels of a stock location with [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md), pass `sales_channels.*` in `fields`: + +### query.graph + +```ts +const { data: stockLocations } = await query.graph({ + entity: "stock_location", + fields: [ + "sales_channels.*", + ], +}) + +// stockLocations.sales_channels +``` + +### useQueryGraphStep + +```ts +import { useQueryGraphStep } from "@medusajs/medusa/core-flows" + +// ... + +const { data: stockLocations } = useQueryGraphStep({ + entity: "stock_location", + fields: [ + "sales_channels.*", + ], +}) + +// stockLocations.sales_channels +``` + +### Manage with Link + +To manage the stock locations of a sales channel, use [Link](https://docs.medusajs.com/docs/learn/fundamentals/module-links/link/index.html.md): ### link.create @@ -24738,208 +24529,6 @@ createRemoteLinkStep({ ``` -# Publishable API Keys with Sales Channels - -In this document, you’ll learn what publishable API keys are and how to use them with sales channels. - -## Publishable API Keys with Sales Channels - -A publishable API key, provided by the API Key Module, is a client key scoped to one or more sales channels. - -When sending a request to a Store API route, you must pass a publishable API key in the header of the request: - -```bash -curl http://localhost:9000/store/products \ - x-publishable-api-key: {your_publishable_api_key} -``` - -The Medusa application infers the associated sales channels and ensures that only data relevant to the sales channel are used. - -*** - -## How to Create a Publishable API Key? - -To create a publishable API key, either use the Medusa Admin or the [Admin API Routes](undefined/api/admin#publishable-api-keys). - - -# 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. - -- [`Region` data model \<> `Cart` data model of the Cart Module](#cart-module). (Read-only) -- [`Region` data model \<> `Order` data model of the Order Module](#order-module). (Read-only) -- [`Region` data model \<> `PaymentProvider` data model of the Payment Module](#payment-module). - -*** - -## Cart Module - -Medusa defines a read-only link between the `Region` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `Cart` data model. This means you can retrieve the details of a region's carts, but you don't manage the links in a pivot table in the database. The region of a cart is determined by the `region_id` property of the `Cart` data model. - -### Retrieve with Query - -To retrieve the carts of a region with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `carts.*` in `fields`: - -### query.graph - -```ts -const { data: regions } = await query.graph({ - entity: "region", - fields: [ - "carts.*", - ], -}) - -// regions.carts -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: regions } = useQueryGraphStep({ - entity: "region", - fields: [ - "carts.*", - ], -}) - -// regions.carts -``` - -*** - -## Order Module - -Medusa defines a read-only link between the `Region` data model and the [Cart Module](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/cart)'s `Cart` data model. This means you can retrieve the details of a region's orders, but you don't manage the links in a pivot table in the database. The region of an order is determined by the `region_id` property of the `Order` data model. - -### Retrieve with Query - -To retrieve the orders of a region with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `orders.*` in `fields`: - -### query.graph - -```ts -const { data: regions } = await query.graph({ - entity: "region", - fields: [ - "orders.*", - ], -}) - -// regions.orders -``` - -### useQueryGraphStep - -```ts -import { useQueryGraphStep } from "@medusajs/medusa/core-flows" - -// ... - -const { data: regions } = useQueryGraphStep({ - entity: "region", - fields: [ - "orders.*", - ], -}) - -// regions.orders -``` - -*** - -## 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. - -![A diagram showcasing an example of how resources from the Payment and Region modules are linked](https://res.cloudinary.com/dza7lstvk/image/upload/v1711569520/Medusa%20Resources/payment-region_jyo2dz.jpg) - -### Retrieve with Query - -To retrieve the payment providers of a region with [Query](undefined/docs/learn/fundamentals/module-links/query), 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](undefined/docs/learn/fundamentals/module-links/link): - -### 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. @@ -24958,11 +24547,11 @@ Read-only links are used to query data across modules, but the relations aren't 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/currency)'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` data model in the Store Module. +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` data model in the Store Module. ### Retrieve with Query -To retrieve the details of a store's currencies with [Query](undefined/docs/learn/fundamentals/module-links/query), pass `supported_currencies.currency.*` in `fields`: +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 @@ -25044,7 +24633,7 @@ In this document, you’ll learn how tax lines are calculated and what a tax pro ## Tax Lines Calculation -Tax lines are calculated and retrieved using the [getTaxLines method of the Tax Module’s main service](undefined/references/tax/getTaxLines). It accepts an array of line items and shipping methods, and the context of the 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: @@ -25139,7 +24728,7 @@ You can create tax rates that override the default for specific conditions or ru 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](undefined/references/tax/models/TaxRateRule) represents a tax rate’s rule. +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. ![A diagram showcasing the relation between TaxRegion, TaxRate, and TaxRateRule](https://res.cloudinary.com/dza7lstvk/image/upload/v1711462167/Medusa%20Resources/tax-rate-rule_enzbp2.jpg) @@ -25157,7 +24746,7 @@ In this document, you’ll learn about tax regions and how to use them with the ## What is a Tax Region? -A tax region, represented by the [TaxRegion data model](undefined/references/tax/models/TaxRegion), stores tax settings related to a region that your store serves. +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. @@ -25207,7 +24796,7 @@ In this document, learn the different ways to create a user using the User Modul ## Straightforward User Creation -To create a user, use the [create method of the User Module’s main service](undefined/references/user/create): +To create a user, use the [create method of the User Module’s main service](https://docs.medusajs.com/references/user/create/index.html.md): ```ts const user = await userModuleService.createUsers({ @@ -25221,7 +24810,7 @@ You can pair this with the Auth Module to allow the user to authenticate, as exp ## Invite Users -To create a user, you can create an invite for them using the [createInvites method](undefined/references/user/createInvites) of the User Module's main service: +To create a user, you can create an invite for them using the [createInvites method](https://docs.medusajs.com/references/user/createInvites/index.html.md) of the User Module's main service: ```ts const invite = await userModuleService.createInvites({ @@ -25247,7 +24836,7 @@ const user = await userModuleService.createUsers({ ### Invite Expiry -An invite has an expiry date. You can renew the expiry date and refresh the token using the [refreshInviteTokens method](undefined/references/user/refreshInviteTokens): +An invite has an expiry date. You can renew the expiry date and refresh the token using the [refreshInviteTokens method](https://docs.medusajs.com/references/user/refreshInviteTokens/index.html.md): ```ts await userModuleService.refreshInviteTokens(["invite_123"]) @@ -25338,7 +24927,7 @@ const hashConfig = \{ ## Related Guides -- [How to register a customer using email and password](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/register) +- [How to register a customer using email and password](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/customers/register/index.html.md) # GitHub Auth Module Provider @@ -25347,7 +24936,7 @@ In this document, you’ll learn about the GitHub Auth Module Provider and how t The Github Auth Module Provider authenticates users with their GitHub account. -Learn about the authentication flow in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route). +Learn about the authentication flow in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route/index.html.md). *** @@ -25414,13 +25003,13 @@ GITHUB_CALLBACK_URL= In many cases, you may have different callback URL for actor types. For example, you may redirect admin users to a different URL than customers after authentication. -The [Authenticate or Login API Route](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route) can accept a `callback_url` body parameter to override the provider's `callbackUrl` option. Learn more in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route). +The [Authenticate or Login API Route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route/index.html.md) can accept a `callback_url` body parameter to override the provider's `callbackUrl` option. Learn more in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route/index.html.md). *** ## Examples -- [How to implement third-party / social login in the storefront.](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/third-party-login). +- [How to implement third-party / 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). # Google Auth Module Provider @@ -25429,7 +25018,7 @@ In this document, you’ll learn about the Google Auth Module Provider and how t The Google Auth Module Provider authenticates users with their Google account. -Learn about the authentication flow for third-party providers in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow). +Learn about the authentication flow for third-party providers in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#2-third-party-service-authenticate-flow/index.html.md). *** @@ -25501,13 +25090,13 @@ GOOGLE_CALLBACK_URL= In many cases, you may have different callback URL for actor types. For example, you may redirect admin users to a different URL than customers after authentication. -The [Authenticate or Login API Route](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route) can accept a `callback_url` body parameter to override the provider's `callbackUrl` option. Learn more in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route). +The [Authenticate or Login API Route](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route/index.html.md) can accept a `callback_url` body parameter to override the provider's `callbackUrl` option. Learn more in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/auth/authentication-route#login-route/index.html.md). *** ## Examples -- [How to implement Google social login in the storefront](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/customers/third-party-login). +- [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). # Stripe Module Provider @@ -25605,14 +25194,14 @@ When you set up the webhook in Stripe, choose the following events to listen to: ## Useful Guides -- [Storefront guide: Add Stripe payment method during checkout](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/storefront-development/checkout/payment/stripe). -- [Integrate in Next.js Starter](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/nextjs-starter#stripe-integration). -- [Customize Stripe Integration in Next.js Starter](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/nextjs-starter/guides/customize-stripe). +- [Storefront guide: Add Stripe payment method during checkout](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/storefront-development/checkout/payment/stripe/index.html.md). +- [Integrate in Next.js Starter](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/nextjs-starter#stripe-integration/index.html.md). +- [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](undefined/docs/learn/fundamentals/module-links/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. @@ -25640,15 +25229,15 @@ const { data: products } = await query.graph({ }) ``` -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](undefined/references/pricing/models/Price). +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context), such as the region ID or the currency code. +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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation). +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: @@ -25685,9 +25274,9 @@ const { data: products } = await query.graph({ 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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#calculation-context). +`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](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/pricing/price-calculation#returned-price-object). +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 @@ -25698,7 +25287,7 @@ In this document, you'll learn how to calculate a product variant's price with t You'll need the following resources for the taxes calculation: -1. [Query](undefined/docs/learn/fundamentals/module-links/query) to retrieve the product's variants' prices for a context. Learn more about that in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product/guides/price). +1. [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md) to retrieve the product's variants' prices for a context. Learn more about that in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/guides/price/index.html.md). 2. The Tax Module's main service to get the tax lines for each product. ```ts @@ -25721,7 +25310,7 @@ const taxModuleService = container.resolve( After resolving the resources, use Query to retrieve the products with the variants' prices for a context: -Learn more about retrieving product variants' prices for a context in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/commerce-modules/product/guides/price). +Learn more about retrieving product variants' prices for a context in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/commerce-modules/product/guides/price/index.html.md). ```ts import { QueryContext } from "@medusajs/framework/utils" @@ -25810,13 +25399,13 @@ const taxLines = (await taxModuleService.getTaxLines( )) as unknown as ItemTaxLineDTO[] ``` -You use the Tax Module's main service's [getTaxLines method](undefined/references/tax/getTaxLines) to retrieve the tax line. +You use the Tax Module's main service's [getTaxLines method](https://docs.medusajs.com/references/tax/getTaxLines/index.html.md) to retrieve the tax line. For the first parameter, you use the `asTaxItem` function to format the products as expected by the `getTaxLines` method. For the second parameter, you pass the current context. You can pass other details such as the customer's ID. -Learn about the other context properties to pass in [the getTaxLines method's reference](undefined/references/tax/getTaxLines). +Learn about the other context properties to pass in [the getTaxLines method's reference](https://docs.medusajs.com/references/tax/getTaxLines/index.html.md). *** @@ -25877,600 +25466,600 @@ For each product variant, you: ## Workflows -- [createApiKeysWorkflow](undefined/references/medusa-workflows/createApiKeysWorkflow/index.html.md) -- [deleteApiKeysWorkflow](undefined/references/medusa-workflows/deleteApiKeysWorkflow/index.html.md) -- [linkSalesChannelsToApiKeyWorkflow](undefined/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md) -- [revokeApiKeysWorkflow](undefined/references/medusa-workflows/revokeApiKeysWorkflow/index.html.md) -- [updateApiKeysWorkflow](undefined/references/medusa-workflows/updateApiKeysWorkflow/index.html.md) -- [addToCartWorkflow](undefined/references/medusa-workflows/addToCartWorkflow/index.html.md) -- [addShippingMethodToCartWorkflow](undefined/references/medusa-workflows/addShippingMethodToCartWorkflow/index.html.md) -- [completeCartWorkflow](undefined/references/medusa-workflows/completeCartWorkflow/index.html.md) -- [confirmVariantInventoryWorkflow](undefined/references/medusa-workflows/confirmVariantInventoryWorkflow/index.html.md) -- [createCartWorkflow](undefined/references/medusa-workflows/createCartWorkflow/index.html.md) -- [createPaymentCollectionForCartWorkflow](undefined/references/medusa-workflows/createPaymentCollectionForCartWorkflow/index.html.md) -- [listShippingOptionsForCartWithPricingWorkflow](undefined/references/medusa-workflows/listShippingOptionsForCartWithPricingWorkflow/index.html.md) -- [listShippingOptionsForCartWorkflow](undefined/references/medusa-workflows/listShippingOptionsForCartWorkflow/index.html.md) -- [refreshCartItemsWorkflow](undefined/references/medusa-workflows/refreshCartItemsWorkflow/index.html.md) -- [refreshCartShippingMethodsWorkflow](undefined/references/medusa-workflows/refreshCartShippingMethodsWorkflow/index.html.md) -- [refreshPaymentCollectionForCartWorkflow](undefined/references/medusa-workflows/refreshPaymentCollectionForCartWorkflow/index.html.md) -- [transferCartCustomerWorkflow](undefined/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md) -- [updateCartPromotionsWorkflow](undefined/references/medusa-workflows/updateCartPromotionsWorkflow/index.html.md) -- [updateCartWorkflow](undefined/references/medusa-workflows/updateCartWorkflow/index.html.md) -- [updateLineItemInCartWorkflow](undefined/references/medusa-workflows/updateLineItemInCartWorkflow/index.html.md) -- [updateTaxLinesWorkflow](undefined/references/medusa-workflows/updateTaxLinesWorkflow/index.html.md) -- [validateExistingPaymentCollectionStep](undefined/references/medusa-workflows/validateExistingPaymentCollectionStep/index.html.md) -- [generateResetPasswordTokenWorkflow](undefined/references/medusa-workflows/generateResetPasswordTokenWorkflow/index.html.md) -- [batchLinksWorkflow](undefined/references/medusa-workflows/batchLinksWorkflow/index.html.md) -- [createLinksWorkflow](undefined/references/medusa-workflows/createLinksWorkflow/index.html.md) -- [updateLinksWorkflow](undefined/references/medusa-workflows/updateLinksWorkflow/index.html.md) -- [dismissLinksWorkflow](undefined/references/medusa-workflows/dismissLinksWorkflow/index.html.md) -- [createCustomerAccountWorkflow](undefined/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md) -- [createCustomerAddressesWorkflow](undefined/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md) -- [createCustomersWorkflow](undefined/references/medusa-workflows/createCustomersWorkflow/index.html.md) -- [deleteCustomerAddressesWorkflow](undefined/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md) -- [deleteCustomersWorkflow](undefined/references/medusa-workflows/deleteCustomersWorkflow/index.html.md) -- [removeCustomerAccountWorkflow](undefined/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md) -- [updateCustomerAddressesWorkflow](undefined/references/medusa-workflows/updateCustomerAddressesWorkflow/index.html.md) -- [updateCustomersWorkflow](undefined/references/medusa-workflows/updateCustomersWorkflow/index.html.md) -- [createDefaultsWorkflow](undefined/references/medusa-workflows/createDefaultsWorkflow/index.html.md) -- [createCustomerGroupsWorkflow](undefined/references/medusa-workflows/createCustomerGroupsWorkflow/index.html.md) -- [deleteCustomerGroupsWorkflow](undefined/references/medusa-workflows/deleteCustomerGroupsWorkflow/index.html.md) -- [linkCustomerGroupsToCustomerWorkflow](undefined/references/medusa-workflows/linkCustomerGroupsToCustomerWorkflow/index.html.md) -- [linkCustomersToCustomerGroupWorkflow](undefined/references/medusa-workflows/linkCustomersToCustomerGroupWorkflow/index.html.md) -- [updateCustomerGroupsWorkflow](undefined/references/medusa-workflows/updateCustomerGroupsWorkflow/index.html.md) -- [deleteFilesWorkflow](undefined/references/medusa-workflows/deleteFilesWorkflow/index.html.md) -- [uploadFilesWorkflow](undefined/references/medusa-workflows/uploadFilesWorkflow/index.html.md) -- [batchShippingOptionRulesWorkflow](undefined/references/medusa-workflows/batchShippingOptionRulesWorkflow/index.html.md) -- [calculateShippingOptionsPricesWorkflow](undefined/references/medusa-workflows/calculateShippingOptionsPricesWorkflow/index.html.md) -- [cancelFulfillmentWorkflow](undefined/references/medusa-workflows/cancelFulfillmentWorkflow/index.html.md) -- [createFulfillmentWorkflow](undefined/references/medusa-workflows/createFulfillmentWorkflow/index.html.md) -- [createReturnFulfillmentWorkflow](undefined/references/medusa-workflows/createReturnFulfillmentWorkflow/index.html.md) -- [createServiceZonesWorkflow](undefined/references/medusa-workflows/createServiceZonesWorkflow/index.html.md) -- [createShipmentWorkflow](undefined/references/medusa-workflows/createShipmentWorkflow/index.html.md) -- [createShippingOptionsWorkflow](undefined/references/medusa-workflows/createShippingOptionsWorkflow/index.html.md) -- [createShippingProfilesWorkflow](undefined/references/medusa-workflows/createShippingProfilesWorkflow/index.html.md) -- [deleteFulfillmentSetsWorkflow](undefined/references/medusa-workflows/deleteFulfillmentSetsWorkflow/index.html.md) -- [deleteServiceZonesWorkflow](undefined/references/medusa-workflows/deleteServiceZonesWorkflow/index.html.md) -- [deleteShippingOptionsWorkflow](undefined/references/medusa-workflows/deleteShippingOptionsWorkflow/index.html.md) -- [markFulfillmentAsDeliveredWorkflow](undefined/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md) -- [updateFulfillmentWorkflow](undefined/references/medusa-workflows/updateFulfillmentWorkflow/index.html.md) -- [updateServiceZonesWorkflow](undefined/references/medusa-workflows/updateServiceZonesWorkflow/index.html.md) -- [updateShippingOptionsWorkflow](undefined/references/medusa-workflows/updateShippingOptionsWorkflow/index.html.md) -- [updateShippingProfilesWorkflow](undefined/references/medusa-workflows/updateShippingProfilesWorkflow/index.html.md) -- [validateFulfillmentDeliverabilityStep](undefined/references/medusa-workflows/validateFulfillmentDeliverabilityStep/index.html.md) -- [batchInventoryItemLevelsWorkflow](undefined/references/medusa-workflows/batchInventoryItemLevelsWorkflow/index.html.md) -- [bulkCreateDeleteLevelsWorkflow](undefined/references/medusa-workflows/bulkCreateDeleteLevelsWorkflow/index.html.md) -- [createInventoryItemsWorkflow](undefined/references/medusa-workflows/createInventoryItemsWorkflow/index.html.md) -- [createInventoryLevelsWorkflow](undefined/references/medusa-workflows/createInventoryLevelsWorkflow/index.html.md) -- [deleteInventoryItemWorkflow](undefined/references/medusa-workflows/deleteInventoryItemWorkflow/index.html.md) -- [deleteInventoryLevelsWorkflow](undefined/references/medusa-workflows/deleteInventoryLevelsWorkflow/index.html.md) -- [updateInventoryItemsWorkflow](undefined/references/medusa-workflows/updateInventoryItemsWorkflow/index.html.md) -- [updateInventoryLevelsWorkflow](undefined/references/medusa-workflows/updateInventoryLevelsWorkflow/index.html.md) -- [validateInventoryLevelsDelete](undefined/references/medusa-workflows/validateInventoryLevelsDelete/index.html.md) -- [acceptInviteWorkflow](undefined/references/medusa-workflows/acceptInviteWorkflow/index.html.md) -- [createInvitesWorkflow](undefined/references/medusa-workflows/createInvitesWorkflow/index.html.md) -- [deleteInvitesWorkflow](undefined/references/medusa-workflows/deleteInvitesWorkflow/index.html.md) -- [refreshInviteTokensWorkflow](undefined/references/medusa-workflows/refreshInviteTokensWorkflow/index.html.md) -- [deleteLineItemsWorkflow](undefined/references/medusa-workflows/deleteLineItemsWorkflow/index.html.md) -- [capturePaymentWorkflow](undefined/references/medusa-workflows/capturePaymentWorkflow/index.html.md) -- [processPaymentWorkflow](undefined/references/medusa-workflows/processPaymentWorkflow/index.html.md) -- [refundPaymentWorkflow](undefined/references/medusa-workflows/refundPaymentWorkflow/index.html.md) -- [refundPaymentsWorkflow](undefined/references/medusa-workflows/refundPaymentsWorkflow/index.html.md) -- [validatePaymentsRefundStep](undefined/references/medusa-workflows/validatePaymentsRefundStep/index.html.md) -- [validateRefundStep](undefined/references/medusa-workflows/validateRefundStep/index.html.md) -- [createPaymentSessionsWorkflow](undefined/references/medusa-workflows/createPaymentSessionsWorkflow/index.html.md) -- [createRefundReasonsWorkflow](undefined/references/medusa-workflows/createRefundReasonsWorkflow/index.html.md) -- [deletePaymentSessionsWorkflow](undefined/references/medusa-workflows/deletePaymentSessionsWorkflow/index.html.md) -- [deleteRefundReasonsWorkflow](undefined/references/medusa-workflows/deleteRefundReasonsWorkflow/index.html.md) -- [updateRefundReasonsWorkflow](undefined/references/medusa-workflows/updateRefundReasonsWorkflow/index.html.md) -- [acceptOrderTransferValidationStep](undefined/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md) -- [acceptOrderTransferWorkflow](undefined/references/medusa-workflows/acceptOrderTransferWorkflow/index.html.md) -- [addOrderLineItemsWorkflow](undefined/references/medusa-workflows/addOrderLineItemsWorkflow/index.html.md) -- [beginClaimOrderValidationStep](undefined/references/medusa-workflows/beginClaimOrderValidationStep/index.html.md) -- [beginClaimOrderWorkflow](undefined/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md) -- [beginOrderEditOrderWorkflow](undefined/references/medusa-workflows/beginOrderEditOrderWorkflow/index.html.md) -- [archiveOrderWorkflow](undefined/references/medusa-workflows/archiveOrderWorkflow/index.html.md) -- [beginExchangeOrderWorkflow](undefined/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md) -- [beginOrderEditValidationStep](undefined/references/medusa-workflows/beginOrderEditValidationStep/index.html.md) -- [beginOrderExchangeValidationStep](undefined/references/medusa-workflows/beginOrderExchangeValidationStep/index.html.md) -- [beginReceiveReturnValidationStep](undefined/references/medusa-workflows/beginReceiveReturnValidationStep/index.html.md) -- [beginReceiveReturnWorkflow](undefined/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md) -- [beginReturnOrderValidationStep](undefined/references/medusa-workflows/beginReturnOrderValidationStep/index.html.md) -- [beginReturnOrderWorkflow](undefined/references/medusa-workflows/beginReturnOrderWorkflow/index.html.md) -- [cancelBeginOrderClaimValidationStep](undefined/references/medusa-workflows/cancelBeginOrderClaimValidationStep/index.html.md) -- [cancelBeginOrderEditValidationStep](undefined/references/medusa-workflows/cancelBeginOrderEditValidationStep/index.html.md) -- [cancelBeginOrderClaimWorkflow](undefined/references/medusa-workflows/cancelBeginOrderClaimWorkflow/index.html.md) -- [cancelBeginOrderEditWorkflow](undefined/references/medusa-workflows/cancelBeginOrderEditWorkflow/index.html.md) -- [cancelBeginOrderExchangeValidationStep](undefined/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md) -- [cancelBeginOrderExchangeWorkflow](undefined/references/medusa-workflows/cancelBeginOrderExchangeWorkflow/index.html.md) -- [cancelClaimValidateOrderStep](undefined/references/medusa-workflows/cancelClaimValidateOrderStep/index.html.md) -- [cancelExchangeValidateOrder](undefined/references/medusa-workflows/cancelExchangeValidateOrder/index.html.md) -- [cancelOrderChangeWorkflow](undefined/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md) -- [cancelOrderClaimWorkflow](undefined/references/medusa-workflows/cancelOrderClaimWorkflow/index.html.md) -- [cancelOrderExchangeWorkflow](undefined/references/medusa-workflows/cancelOrderExchangeWorkflow/index.html.md) -- [cancelOrderFulfillmentValidateOrder](undefined/references/medusa-workflows/cancelOrderFulfillmentValidateOrder/index.html.md) -- [cancelOrderFulfillmentWorkflow](undefined/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md) -- [cancelOrderTransferRequestWorkflow](undefined/references/medusa-workflows/cancelOrderTransferRequestWorkflow/index.html.md) -- [cancelOrderWorkflow](undefined/references/medusa-workflows/cancelOrderWorkflow/index.html.md) -- [cancelRequestReturnValidationStep](undefined/references/medusa-workflows/cancelRequestReturnValidationStep/index.html.md) -- [cancelReceiveReturnValidationStep](undefined/references/medusa-workflows/cancelReceiveReturnValidationStep/index.html.md) -- [cancelReturnReceiveWorkflow](undefined/references/medusa-workflows/cancelReturnReceiveWorkflow/index.html.md) -- [cancelReturnRequestWorkflow](undefined/references/medusa-workflows/cancelReturnRequestWorkflow/index.html.md) -- [cancelReturnValidateOrder](undefined/references/medusa-workflows/cancelReturnValidateOrder/index.html.md) -- [cancelReturnWorkflow](undefined/references/medusa-workflows/cancelReturnWorkflow/index.html.md) -- [cancelTransferOrderRequestValidationStep](undefined/references/medusa-workflows/cancelTransferOrderRequestValidationStep/index.html.md) -- [cancelValidateOrder](undefined/references/medusa-workflows/cancelValidateOrder/index.html.md) -- [completeOrderWorkflow](undefined/references/medusa-workflows/completeOrderWorkflow/index.html.md) -- [confirmClaimRequestValidationStep](undefined/references/medusa-workflows/confirmClaimRequestValidationStep/index.html.md) -- [confirmClaimRequestWorkflow](undefined/references/medusa-workflows/confirmClaimRequestWorkflow/index.html.md) -- [confirmExchangeRequestValidationStep](undefined/references/medusa-workflows/confirmExchangeRequestValidationStep/index.html.md) -- [confirmExchangeRequestWorkflow](undefined/references/medusa-workflows/confirmExchangeRequestWorkflow/index.html.md) -- [confirmOrderEditRequestValidationStep](undefined/references/medusa-workflows/confirmOrderEditRequestValidationStep/index.html.md) -- [confirmOrderEditRequestWorkflow](undefined/references/medusa-workflows/confirmOrderEditRequestWorkflow/index.html.md) -- [confirmReceiveReturnValidationStep](undefined/references/medusa-workflows/confirmReceiveReturnValidationStep/index.html.md) -- [confirmReturnReceiveWorkflow](undefined/references/medusa-workflows/confirmReturnReceiveWorkflow/index.html.md) -- [confirmReturnRequestValidationStep](undefined/references/medusa-workflows/confirmReturnRequestValidationStep/index.html.md) -- [confirmReturnRequestWorkflow](undefined/references/medusa-workflows/confirmReturnRequestWorkflow/index.html.md) -- [createAndCompleteReturnOrderWorkflow](undefined/references/medusa-workflows/createAndCompleteReturnOrderWorkflow/index.html.md) -- [createClaimShippingMethodValidationStep](undefined/references/medusa-workflows/createClaimShippingMethodValidationStep/index.html.md) -- [createCompleteReturnValidationStep](undefined/references/medusa-workflows/createCompleteReturnValidationStep/index.html.md) -- [createClaimShippingMethodWorkflow](undefined/references/medusa-workflows/createClaimShippingMethodWorkflow/index.html.md) -- [createExchangeShippingMethodValidationStep](undefined/references/medusa-workflows/createExchangeShippingMethodValidationStep/index.html.md) -- [createExchangeShippingMethodWorkflow](undefined/references/medusa-workflows/createExchangeShippingMethodWorkflow/index.html.md) -- [createFulfillmentValidateOrder](undefined/references/medusa-workflows/createFulfillmentValidateOrder/index.html.md) -- [createOrUpdateOrderPaymentCollectionWorkflow](undefined/references/medusa-workflows/createOrUpdateOrderPaymentCollectionWorkflow/index.html.md) -- [createOrderChangeActionsWorkflow](undefined/references/medusa-workflows/createOrderChangeActionsWorkflow/index.html.md) -- [createOrderChangeWorkflow](undefined/references/medusa-workflows/createOrderChangeWorkflow/index.html.md) -- [createOrderEditShippingMethodValidationStep](undefined/references/medusa-workflows/createOrderEditShippingMethodValidationStep/index.html.md) -- [createOrderEditShippingMethodWorkflow](undefined/references/medusa-workflows/createOrderEditShippingMethodWorkflow/index.html.md) -- [createOrderFulfillmentWorkflow](undefined/references/medusa-workflows/createOrderFulfillmentWorkflow/index.html.md) -- [createOrderPaymentCollectionWorkflow](undefined/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md) -- [createOrderWorkflow](undefined/references/medusa-workflows/createOrderWorkflow/index.html.md) -- [createOrderShipmentWorkflow](undefined/references/medusa-workflows/createOrderShipmentWorkflow/index.html.md) -- [createOrdersWorkflow](undefined/references/medusa-workflows/createOrdersWorkflow/index.html.md) -- [createReturnShippingMethodWorkflow](undefined/references/medusa-workflows/createReturnShippingMethodWorkflow/index.html.md) -- [createReturnShippingMethodValidationStep](undefined/references/medusa-workflows/createReturnShippingMethodValidationStep/index.html.md) -- [createShipmentValidateOrder](undefined/references/medusa-workflows/createShipmentValidateOrder/index.html.md) -- [declineOrderTransferRequestWorkflow](undefined/references/medusa-workflows/declineOrderTransferRequestWorkflow/index.html.md) -- [declineOrderChangeWorkflow](undefined/references/medusa-workflows/declineOrderChangeWorkflow/index.html.md) -- [declineTransferOrderRequestValidationStep](undefined/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md) -- [deleteOrderChangeWorkflow](undefined/references/medusa-workflows/deleteOrderChangeWorkflow/index.html.md) -- [deleteOrderChangeActionsWorkflow](undefined/references/medusa-workflows/deleteOrderChangeActionsWorkflow/index.html.md) -- [deleteOrderPaymentCollections](undefined/references/medusa-workflows/deleteOrderPaymentCollections/index.html.md) -- [dismissItemReturnRequestValidationStep](undefined/references/medusa-workflows/dismissItemReturnRequestValidationStep/index.html.md) -- [dismissItemReturnRequestWorkflow](undefined/references/medusa-workflows/dismissItemReturnRequestWorkflow/index.html.md) -- [getOrderDetailWorkflow](undefined/references/medusa-workflows/getOrderDetailWorkflow/index.html.md) -- [exchangeAddNewItemValidationStep](undefined/references/medusa-workflows/exchangeAddNewItemValidationStep/index.html.md) -- [exchangeRequestItemReturnValidationStep](undefined/references/medusa-workflows/exchangeRequestItemReturnValidationStep/index.html.md) -- [getOrdersListWorkflow](undefined/references/medusa-workflows/getOrdersListWorkflow/index.html.md) -- [markOrderFulfillmentAsDeliveredWorkflow](undefined/references/medusa-workflows/markOrderFulfillmentAsDeliveredWorkflow/index.html.md) -- [orderClaimAddNewItemValidationStep](undefined/references/medusa-workflows/orderClaimAddNewItemValidationStep/index.html.md) -- [markPaymentCollectionAsPaid](undefined/references/medusa-workflows/markPaymentCollectionAsPaid/index.html.md) -- [orderClaimAddNewItemWorkflow](undefined/references/medusa-workflows/orderClaimAddNewItemWorkflow/index.html.md) -- [orderClaimItemWorkflow](undefined/references/medusa-workflows/orderClaimItemWorkflow/index.html.md) -- [orderClaimItemValidationStep](undefined/references/medusa-workflows/orderClaimItemValidationStep/index.html.md) -- [orderClaimRequestItemReturnValidationStep](undefined/references/medusa-workflows/orderClaimRequestItemReturnValidationStep/index.html.md) -- [orderEditAddNewItemValidationStep](undefined/references/medusa-workflows/orderEditAddNewItemValidationStep/index.html.md) -- [orderClaimRequestItemReturnWorkflow](undefined/references/medusa-workflows/orderClaimRequestItemReturnWorkflow/index.html.md) -- [orderEditAddNewItemWorkflow](undefined/references/medusa-workflows/orderEditAddNewItemWorkflow/index.html.md) -- [orderEditUpdateItemQuantityWorkflow](undefined/references/medusa-workflows/orderEditUpdateItemQuantityWorkflow/index.html.md) -- [orderEditUpdateItemQuantityValidationStep](undefined/references/medusa-workflows/orderEditUpdateItemQuantityValidationStep/index.html.md) -- [orderExchangeAddNewItemWorkflow](undefined/references/medusa-workflows/orderExchangeAddNewItemWorkflow/index.html.md) -- [orderExchangeRequestItemReturnWorkflow](undefined/references/medusa-workflows/orderExchangeRequestItemReturnWorkflow/index.html.md) -- [orderFulfillmentDeliverablilityValidationStep](undefined/references/medusa-workflows/orderFulfillmentDeliverablilityValidationStep/index.html.md) -- [receiveAndCompleteReturnOrderWorkflow](undefined/references/medusa-workflows/receiveAndCompleteReturnOrderWorkflow/index.html.md) -- [receiveCompleteReturnValidationStep](undefined/references/medusa-workflows/receiveCompleteReturnValidationStep/index.html.md) -- [receiveItemReturnRequestValidationStep](undefined/references/medusa-workflows/receiveItemReturnRequestValidationStep/index.html.md) -- [receiveItemReturnRequestWorkflow](undefined/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md) -- [removeClaimAddItemActionValidationStep](undefined/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md) -- [removeAddItemClaimActionWorkflow](undefined/references/medusa-workflows/removeAddItemClaimActionWorkflow/index.html.md) -- [removeClaimItemActionValidationStep](undefined/references/medusa-workflows/removeClaimItemActionValidationStep/index.html.md) -- [removeClaimShippingMethodValidationStep](undefined/references/medusa-workflows/removeClaimShippingMethodValidationStep/index.html.md) -- [removeClaimShippingMethodWorkflow](undefined/references/medusa-workflows/removeClaimShippingMethodWorkflow/index.html.md) -- [removeExchangeItemActionValidationStep](undefined/references/medusa-workflows/removeExchangeItemActionValidationStep/index.html.md) -- [removeExchangeShippingMethodValidationStep](undefined/references/medusa-workflows/removeExchangeShippingMethodValidationStep/index.html.md) -- [removeExchangeShippingMethodWorkflow](undefined/references/medusa-workflows/removeExchangeShippingMethodWorkflow/index.html.md) -- [removeItemClaimActionWorkflow](undefined/references/medusa-workflows/removeItemClaimActionWorkflow/index.html.md) -- [removeItemExchangeActionWorkflow](undefined/references/medusa-workflows/removeItemExchangeActionWorkflow/index.html.md) -- [removeItemOrderEditActionWorkflow](undefined/references/medusa-workflows/removeItemOrderEditActionWorkflow/index.html.md) -- [removeItemReceiveReturnActionValidationStep](undefined/references/medusa-workflows/removeItemReceiveReturnActionValidationStep/index.html.md) -- [removeItemReceiveReturnActionWorkflow](undefined/references/medusa-workflows/removeItemReceiveReturnActionWorkflow/index.html.md) -- [removeItemReturnActionWorkflow](undefined/references/medusa-workflows/removeItemReturnActionWorkflow/index.html.md) -- [removeOrderEditItemActionValidationStep](undefined/references/medusa-workflows/removeOrderEditItemActionValidationStep/index.html.md) -- [removeOrderEditShippingMethodValidationStep](undefined/references/medusa-workflows/removeOrderEditShippingMethodValidationStep/index.html.md) -- [removeOrderEditShippingMethodWorkflow](undefined/references/medusa-workflows/removeOrderEditShippingMethodWorkflow/index.html.md) -- [removeReturnItemActionValidationStep](undefined/references/medusa-workflows/removeReturnItemActionValidationStep/index.html.md) -- [removeReturnShippingMethodValidationStep](undefined/references/medusa-workflows/removeReturnShippingMethodValidationStep/index.html.md) -- [removeReturnShippingMethodWorkflow](undefined/references/medusa-workflows/removeReturnShippingMethodWorkflow/index.html.md) -- [requestItemReturnValidationStep](undefined/references/medusa-workflows/requestItemReturnValidationStep/index.html.md) -- [requestItemReturnWorkflow](undefined/references/medusa-workflows/requestItemReturnWorkflow/index.html.md) -- [requestOrderEditRequestValidationStep](undefined/references/medusa-workflows/requestOrderEditRequestValidationStep/index.html.md) -- [requestOrderEditRequestWorkflow](undefined/references/medusa-workflows/requestOrderEditRequestWorkflow/index.html.md) -- [requestOrderTransferValidationStep](undefined/references/medusa-workflows/requestOrderTransferValidationStep/index.html.md) -- [requestOrderTransferWorkflow](undefined/references/medusa-workflows/requestOrderTransferWorkflow/index.html.md) -- [throwUnlessPaymentCollectionNotPaid](undefined/references/medusa-workflows/throwUnlessPaymentCollectionNotPaid/index.html.md) -- [throwUnlessStatusIsNotPaid](undefined/references/medusa-workflows/throwUnlessStatusIsNotPaid/index.html.md) -- [updateClaimAddItemValidationStep](undefined/references/medusa-workflows/updateClaimAddItemValidationStep/index.html.md) -- [updateClaimAddItemWorkflow](undefined/references/medusa-workflows/updateClaimAddItemWorkflow/index.html.md) -- [updateClaimItemValidationStep](undefined/references/medusa-workflows/updateClaimItemValidationStep/index.html.md) -- [updateClaimItemWorkflow](undefined/references/medusa-workflows/updateClaimItemWorkflow/index.html.md) -- [updateClaimShippingMethodValidationStep](undefined/references/medusa-workflows/updateClaimShippingMethodValidationStep/index.html.md) -- [updateClaimShippingMethodWorkflow](undefined/references/medusa-workflows/updateClaimShippingMethodWorkflow/index.html.md) -- [updateExchangeAddItemValidationStep](undefined/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md) -- [updateExchangeAddItemWorkflow](undefined/references/medusa-workflows/updateExchangeAddItemWorkflow/index.html.md) -- [updateExchangeShippingMethodValidationStep](undefined/references/medusa-workflows/updateExchangeShippingMethodValidationStep/index.html.md) -- [updateExchangeShippingMethodWorkflow](undefined/references/medusa-workflows/updateExchangeShippingMethodWorkflow/index.html.md) -- [updateOrderChangeActionsWorkflow](undefined/references/medusa-workflows/updateOrderChangeActionsWorkflow/index.html.md) -- [updateOrderChangesWorkflow](undefined/references/medusa-workflows/updateOrderChangesWorkflow/index.html.md) -- [updateOrderEditAddItemValidationStep](undefined/references/medusa-workflows/updateOrderEditAddItemValidationStep/index.html.md) -- [updateOrderEditAddItemWorkflow](undefined/references/medusa-workflows/updateOrderEditAddItemWorkflow/index.html.md) -- [updateOrderEditItemQuantityValidationStep](undefined/references/medusa-workflows/updateOrderEditItemQuantityValidationStep/index.html.md) -- [updateOrderEditItemQuantityWorkflow](undefined/references/medusa-workflows/updateOrderEditItemQuantityWorkflow/index.html.md) -- [updateOrderEditShippingMethodValidationStep](undefined/references/medusa-workflows/updateOrderEditShippingMethodValidationStep/index.html.md) -- [updateOrderEditShippingMethodWorkflow](undefined/references/medusa-workflows/updateOrderEditShippingMethodWorkflow/index.html.md) -- [updateOrderTaxLinesWorkflow](undefined/references/medusa-workflows/updateOrderTaxLinesWorkflow/index.html.md) -- [updateOrderValidationStep](undefined/references/medusa-workflows/updateOrderValidationStep/index.html.md) -- [updateOrderWorkflow](undefined/references/medusa-workflows/updateOrderWorkflow/index.html.md) -- [updateReceiveItemReturnRequestValidationStep](undefined/references/medusa-workflows/updateReceiveItemReturnRequestValidationStep/index.html.md) -- [updateReceiveItemReturnRequestWorkflow](undefined/references/medusa-workflows/updateReceiveItemReturnRequestWorkflow/index.html.md) -- [updateRequestItemReturnValidationStep](undefined/references/medusa-workflows/updateRequestItemReturnValidationStep/index.html.md) -- [updateRequestItemReturnWorkflow](undefined/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md) -- [updateReturnShippingMethodValidationStep](undefined/references/medusa-workflows/updateReturnShippingMethodValidationStep/index.html.md) -- [updateReturnShippingMethodWorkflow](undefined/references/medusa-workflows/updateReturnShippingMethodWorkflow/index.html.md) -- [updateReturnValidationStep](undefined/references/medusa-workflows/updateReturnValidationStep/index.html.md) -- [updateReturnWorkflow](undefined/references/medusa-workflows/updateReturnWorkflow/index.html.md) -- [batchPriceListPricesWorkflow](undefined/references/medusa-workflows/batchPriceListPricesWorkflow/index.html.md) -- [createPriceListPricesWorkflow](undefined/references/medusa-workflows/createPriceListPricesWorkflow/index.html.md) -- [createPriceListsWorkflow](undefined/references/medusa-workflows/createPriceListsWorkflow/index.html.md) -- [deletePriceListsWorkflow](undefined/references/medusa-workflows/deletePriceListsWorkflow/index.html.md) -- [removePriceListPricesWorkflow](undefined/references/medusa-workflows/removePriceListPricesWorkflow/index.html.md) -- [updatePriceListPricesWorkflow](undefined/references/medusa-workflows/updatePriceListPricesWorkflow/index.html.md) -- [updatePriceListsWorkflow](undefined/references/medusa-workflows/updatePriceListsWorkflow/index.html.md) -- [createPricePreferencesWorkflow](undefined/references/medusa-workflows/createPricePreferencesWorkflow/index.html.md) -- [deletePricePreferencesWorkflow](undefined/references/medusa-workflows/deletePricePreferencesWorkflow/index.html.md) -- [updatePricePreferencesWorkflow](undefined/references/medusa-workflows/updatePricePreferencesWorkflow/index.html.md) -- [batchLinkProductsToCategoryWorkflow](undefined/references/medusa-workflows/batchLinkProductsToCategoryWorkflow/index.html.md) -- [batchLinkProductsToCollectionWorkflow](undefined/references/medusa-workflows/batchLinkProductsToCollectionWorkflow/index.html.md) -- [batchProductVariantsWorkflow](undefined/references/medusa-workflows/batchProductVariantsWorkflow/index.html.md) -- [batchProductsWorkflow](undefined/references/medusa-workflows/batchProductsWorkflow/index.html.md) -- [createCollectionsWorkflow](undefined/references/medusa-workflows/createCollectionsWorkflow/index.html.md) -- [createProductOptionsWorkflow](undefined/references/medusa-workflows/createProductOptionsWorkflow/index.html.md) -- [createProductTagsWorkflow](undefined/references/medusa-workflows/createProductTagsWorkflow/index.html.md) -- [createProductTypesWorkflow](undefined/references/medusa-workflows/createProductTypesWorkflow/index.html.md) -- [createProductVariantsWorkflow](undefined/references/medusa-workflows/createProductVariantsWorkflow/index.html.md) -- [createProductsWorkflow](undefined/references/medusa-workflows/createProductsWorkflow/index.html.md) -- [deleteCollectionsWorkflow](undefined/references/medusa-workflows/deleteCollectionsWorkflow/index.html.md) -- [deleteProductOptionsWorkflow](undefined/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md) -- [deleteProductTagsWorkflow](undefined/references/medusa-workflows/deleteProductTagsWorkflow/index.html.md) -- [deleteProductTypesWorkflow](undefined/references/medusa-workflows/deleteProductTypesWorkflow/index.html.md) -- [deleteProductVariantsWorkflow](undefined/references/medusa-workflows/deleteProductVariantsWorkflow/index.html.md) -- [deleteProductsWorkflow](undefined/references/medusa-workflows/deleteProductsWorkflow/index.html.md) -- [exportProductsWorkflow](undefined/references/medusa-workflows/exportProductsWorkflow/index.html.md) -- [importProductsWorkflow](undefined/references/medusa-workflows/importProductsWorkflow/index.html.md) -- [updateCollectionsWorkflow](undefined/references/medusa-workflows/updateCollectionsWorkflow/index.html.md) -- [updateProductOptionsWorkflow](undefined/references/medusa-workflows/updateProductOptionsWorkflow/index.html.md) -- [updateProductTagsWorkflow](undefined/references/medusa-workflows/updateProductTagsWorkflow/index.html.md) -- [updateProductTypesWorkflow](undefined/references/medusa-workflows/updateProductTypesWorkflow/index.html.md) -- [updateProductVariantsWorkflow](undefined/references/medusa-workflows/updateProductVariantsWorkflow/index.html.md) -- [updateProductsWorkflow](undefined/references/medusa-workflows/updateProductsWorkflow/index.html.md) -- [upsertVariantPricesWorkflow](undefined/references/medusa-workflows/upsertVariantPricesWorkflow/index.html.md) -- [validateProductInputStep](undefined/references/medusa-workflows/validateProductInputStep/index.html.md) -- [createProductCategoriesWorkflow](undefined/references/medusa-workflows/createProductCategoriesWorkflow/index.html.md) -- [deleteProductCategoriesWorkflow](undefined/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md) -- [updateProductCategoriesWorkflow](undefined/references/medusa-workflows/updateProductCategoriesWorkflow/index.html.md) -- [addOrRemoveCampaignPromotionsWorkflow](undefined/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/index.html.md) -- [batchPromotionRulesWorkflow](undefined/references/medusa-workflows/batchPromotionRulesWorkflow/index.html.md) -- [createCampaignsWorkflow](undefined/references/medusa-workflows/createCampaignsWorkflow/index.html.md) -- [createPromotionRulesWorkflow](undefined/references/medusa-workflows/createPromotionRulesWorkflow/index.html.md) -- [createPromotionsWorkflow](undefined/references/medusa-workflows/createPromotionsWorkflow/index.html.md) -- [deleteCampaignsWorkflow](undefined/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md) -- [deletePromotionRulesWorkflow](undefined/references/medusa-workflows/deletePromotionRulesWorkflow/index.html.md) -- [deletePromotionsWorkflow](undefined/references/medusa-workflows/deletePromotionsWorkflow/index.html.md) -- [updateCampaignsWorkflow](undefined/references/medusa-workflows/updateCampaignsWorkflow/index.html.md) -- [updatePromotionRulesWorkflow](undefined/references/medusa-workflows/updatePromotionRulesWorkflow/index.html.md) -- [updatePromotionsStatusWorkflow](undefined/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md) -- [updatePromotionsValidationStep](undefined/references/medusa-workflows/updatePromotionsValidationStep/index.html.md) -- [updatePromotionsWorkflow](undefined/references/medusa-workflows/updatePromotionsWorkflow/index.html.md) -- [createRegionsWorkflow](undefined/references/medusa-workflows/createRegionsWorkflow/index.html.md) -- [deleteRegionsWorkflow](undefined/references/medusa-workflows/deleteRegionsWorkflow/index.html.md) -- [updateRegionsWorkflow](undefined/references/medusa-workflows/updateRegionsWorkflow/index.html.md) -- [createReservationsWorkflow](undefined/references/medusa-workflows/createReservationsWorkflow/index.html.md) -- [deleteReservationsByLineItemsWorkflow](undefined/references/medusa-workflows/deleteReservationsByLineItemsWorkflow/index.html.md) -- [deleteReservationsWorkflow](undefined/references/medusa-workflows/deleteReservationsWorkflow/index.html.md) -- [updateReservationsWorkflow](undefined/references/medusa-workflows/updateReservationsWorkflow/index.html.md) -- [createSalesChannelsWorkflow](undefined/references/medusa-workflows/createSalesChannelsWorkflow/index.html.md) -- [deleteSalesChannelsWorkflow](undefined/references/medusa-workflows/deleteSalesChannelsWorkflow/index.html.md) -- [linkProductsToSalesChannelWorkflow](undefined/references/medusa-workflows/linkProductsToSalesChannelWorkflow/index.html.md) -- [updateSalesChannelsWorkflow](undefined/references/medusa-workflows/updateSalesChannelsWorkflow/index.html.md) -- [deleteShippingProfileWorkflow](undefined/references/medusa-workflows/deleteShippingProfileWorkflow/index.html.md) -- [validateStepShippingProfileDelete](undefined/references/medusa-workflows/validateStepShippingProfileDelete/index.html.md) -- [createLocationFulfillmentSetWorkflow](undefined/references/medusa-workflows/createLocationFulfillmentSetWorkflow/index.html.md) -- [createStockLocationsWorkflow](undefined/references/medusa-workflows/createStockLocationsWorkflow/index.html.md) -- [deleteStockLocationsWorkflow](undefined/references/medusa-workflows/deleteStockLocationsWorkflow/index.html.md) -- [linkSalesChannelsToStockLocationWorkflow](undefined/references/medusa-workflows/linkSalesChannelsToStockLocationWorkflow/index.html.md) -- [updateStockLocationsWorkflow](undefined/references/medusa-workflows/updateStockLocationsWorkflow/index.html.md) -- [createStoresWorkflow](undefined/references/medusa-workflows/createStoresWorkflow/index.html.md) -- [deleteStoresWorkflow](undefined/references/medusa-workflows/deleteStoresWorkflow/index.html.md) -- [updateStoresWorkflow](undefined/references/medusa-workflows/updateStoresWorkflow/index.html.md) -- [createTaxRateRulesWorkflow](undefined/references/medusa-workflows/createTaxRateRulesWorkflow/index.html.md) -- [createTaxRatesWorkflow](undefined/references/medusa-workflows/createTaxRatesWorkflow/index.html.md) -- [deleteTaxRateRulesWorkflow](undefined/references/medusa-workflows/deleteTaxRateRulesWorkflow/index.html.md) -- [createTaxRegionsWorkflow](undefined/references/medusa-workflows/createTaxRegionsWorkflow/index.html.md) -- [deleteTaxRatesWorkflow](undefined/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md) -- [deleteTaxRegionsWorkflow](undefined/references/medusa-workflows/deleteTaxRegionsWorkflow/index.html.md) -- [maybeListTaxRateRuleIdsStep](undefined/references/medusa-workflows/maybeListTaxRateRuleIdsStep/index.html.md) -- [setTaxRateRulesWorkflow](undefined/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md) -- [updateTaxRatesWorkflow](undefined/references/medusa-workflows/updateTaxRatesWorkflow/index.html.md) -- [updateTaxRegionsWorkflow](undefined/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md) -- [createUserAccountWorkflow](undefined/references/medusa-workflows/createUserAccountWorkflow/index.html.md) -- [createUsersWorkflow](undefined/references/medusa-workflows/createUsersWorkflow/index.html.md) -- [deleteUsersWorkflow](undefined/references/medusa-workflows/deleteUsersWorkflow/index.html.md) -- [removeUserAccountWorkflow](undefined/references/medusa-workflows/removeUserAccountWorkflow/index.html.md) -- [updateUsersWorkflow](undefined/references/medusa-workflows/updateUsersWorkflow/index.html.md) -- [createReturnReasonsWorkflow](undefined/references/medusa-workflows/createReturnReasonsWorkflow/index.html.md) -- [deleteReturnReasonsWorkflow](undefined/references/medusa-workflows/deleteReturnReasonsWorkflow/index.html.md) -- [updateReturnReasonsWorkflow](undefined/references/medusa-workflows/updateReturnReasonsWorkflow/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) +- [linkSalesChannelsToApiKeyWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToApiKeyWorkflow/index.html.md) +- [updateApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateApiKeysWorkflow/index.html.md) +- [revokeApiKeysWorkflow](https://docs.medusajs.com/references/medusa-workflows/revokeApiKeysWorkflow/index.html.md) +- [batchLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinksWorkflow/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) +- [generateResetPasswordTokenWorkflow](https://docs.medusajs.com/references/medusa-workflows/generateResetPasswordTokenWorkflow/index.html.md) +- [updateLinksWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLinksWorkflow/index.html.md) +- [createCustomersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomersWorkflow/index.html.md) +- [createCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAccountWorkflow/index.html.md) +- [deleteCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerAddressesWorkflow/index.html.md) +- [removeCustomerAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeCustomerAccountWorkflow/index.html.md) +- [createCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerAddressesWorkflow/index.html.md) +- [updateCustomerAddressesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCustomerAddressesWorkflow/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) +- [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) +- [createCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCartWorkflow/index.html.md) +- [createPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPaymentCollectionForCartWorkflow/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) +- [transferCartCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/transferCartCustomerWorkflow/index.html.md) +- [refreshPaymentCollectionForCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshPaymentCollectionForCartWorkflow/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) +- [updateTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxLinesWorkflow/index.html.md) +- [updateLineItemInCartWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateLineItemInCartWorkflow/index.html.md) +- [validateExistingPaymentCollectionStep](https://docs.medusajs.com/references/medusa-workflows/validateExistingPaymentCollectionStep/index.html.md) +- [createDefaultsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createDefaultsWorkflow/index.html.md) +- [uploadFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/uploadFilesWorkflow/index.html.md) +- [deleteFilesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFilesWorkflow/index.html.md) +- [linkCustomerGroupsToCustomerWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkCustomerGroupsToCustomerWorkflow/index.html.md) +- [deleteCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCustomerGroupsWorkflow/index.html.md) +- [createCustomerGroupsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCustomerGroupsWorkflow/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) +- [batchShippingOptionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchShippingOptionRulesWorkflow/index.html.md) +- [calculateShippingOptionsPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/calculateShippingOptionsPricesWorkflow/index.html.md) +- [cancelFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelFulfillmentWorkflow/index.html.md) +- [createFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentWorkflow/index.html.md) +- [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) +- [deleteFulfillmentSetsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteFulfillmentSetsWorkflow/index.html.md) +- [createShippingOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createShippingOptionsWorkflow/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) +- [markFulfillmentAsDeliveredWorkflow](https://docs.medusajs.com/references/medusa-workflows/markFulfillmentAsDeliveredWorkflow/index.html.md) +- [updateFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateFulfillmentWorkflow/index.html.md) +- [updateServiceZonesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateServiceZonesWorkflow/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) +- [validateFulfillmentDeliverabilityStep](https://docs.medusajs.com/references/medusa-workflows/validateFulfillmentDeliverabilityStep/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) +- [deleteInventoryItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteInventoryItemWorkflow/index.html.md) +- [createInventoryLevelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInventoryLevelsWorkflow/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) +- [createInvitesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createInvitesWorkflow/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) +- [refreshInviteTokensWorkflow](https://docs.medusajs.com/references/medusa-workflows/refreshInviteTokensWorkflow/index.html.md) +- [deleteLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteLineItemsWorkflow/index.html.md) +- [processPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/processPaymentWorkflow/index.html.md) +- [capturePaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/capturePaymentWorkflow/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) +- [refundPaymentWorkflow](https://docs.medusajs.com/references/medusa-workflows/refundPaymentWorkflow/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) +- [acceptOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferValidationStep/index.html.md) +- [acceptOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/acceptOrderTransferWorkflow/index.html.md) +- [archiveOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/archiveOrderWorkflow/index.html.md) +- [addOrderLineItemsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrderLineItemsWorkflow/index.html.md) +- [beginClaimOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderValidationStep/index.html.md) +- [beginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditValidationStep/index.html.md) +- [beginOrderEditOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginOrderEditOrderWorkflow/index.html.md) +- [beginExchangeOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginExchangeOrderWorkflow/index.html.md) +- [beginClaimOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginClaimOrderWorkflow/index.html.md) +- [beginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginOrderExchangeValidationStep/index.html.md) +- [beginReceiveReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnValidationStep/index.html.md) +- [beginReceiveReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReceiveReturnWorkflow/index.html.md) +- [beginReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/beginReturnOrderWorkflow/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) +- [cancelBeginOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderClaimWorkflow/index.html.md) +- [cancelBeginOrderEditValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditValidationStep/index.html.md) +- [cancelBeginOrderEditWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderEditWorkflow/index.html.md) +- [cancelBeginOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeWorkflow/index.html.md) +- [cancelBeginOrderExchangeValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelBeginOrderExchangeValidationStep/index.html.md) +- [cancelClaimValidateOrderStep](https://docs.medusajs.com/references/medusa-workflows/cancelClaimValidateOrderStep/index.html.md) +- [cancelOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderChangeWorkflow/index.html.md) +- [cancelOrderClaimWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderClaimWorkflow/index.html.md) +- [cancelExchangeValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelExchangeValidateOrder/index.html.md) +- [cancelOrderExchangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderExchangeWorkflow/index.html.md) +- [cancelOrderFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentValidateOrder/index.html.md) +- [cancelOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderFulfillmentWorkflow/index.html.md) +- [cancelOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelOrderTransferRequestWorkflow/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) +- [cancelReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnRequestWorkflow/index.html.md) +- [cancelReturnReceiveWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnReceiveWorkflow/index.html.md) +- [cancelReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/cancelReturnWorkflow/index.html.md) +- [cancelReturnValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelReturnValidateOrder/index.html.md) +- [cancelValidateOrder](https://docs.medusajs.com/references/medusa-workflows/cancelValidateOrder/index.html.md) +- [cancelTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/cancelTransferOrderRequestValidationStep/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) +- [confirmExchangeRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmExchangeRequestWorkflow/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) +- [confirmOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/confirmOrderEditRequestWorkflow/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) +- [createClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createClaimShippingMethodValidationStep/index.html.md) +- [createAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/createAndCompleteReturnOrderWorkflow/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) +- [createOrUpdateOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrUpdateOrderPaymentCollectionWorkflow/index.html.md) +- [createFulfillmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createFulfillmentValidateOrder/index.html.md) +- [createOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeWorkflow/index.html.md) +- [createOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderChangeActionsWorkflow/index.html.md) +- [createOrderEditShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodWorkflow/index.html.md) +- [createOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/createOrderEditShippingMethodValidationStep/index.html.md) +- [createOrderPaymentCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderPaymentCollectionWorkflow/index.html.md) +- [createOrderFulfillmentWorkflow](https://docs.medusajs.com/references/medusa-workflows/createOrderFulfillmentWorkflow/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) +- [createShipmentValidateOrder](https://docs.medusajs.com/references/medusa-workflows/createShipmentValidateOrder/index.html.md) +- [declineOrderChangeWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderChangeWorkflow/index.html.md) +- [declineOrderTransferRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/declineOrderTransferRequestWorkflow/index.html.md) +- [declineTransferOrderRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/declineTransferOrderRequestValidationStep/index.html.md) +- [deleteOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteOrderChangeActionsWorkflow/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) +- [dismissItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestWorkflow/index.html.md) +- [dismissItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/dismissItemReturnRequestValidationStep/index.html.md) +- [exchangeRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeRequestItemReturnValidationStep/index.html.md) +- [exchangeAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/exchangeAddNewItemValidationStep/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) +- [orderClaimAddNewItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemValidationStep/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) +- [orderClaimAddNewItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimAddNewItemWorkflow/index.html.md) +- [orderClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemValidationStep/index.html.md) +- [orderClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderClaimItemWorkflow/index.html.md) +- [orderClaimRequestItemReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderClaimRequestItemReturnValidationStep/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) +- [orderEditUpdateItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityWorkflow/index.html.md) +- [orderEditUpdateItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderEditUpdateItemQuantityValidationStep/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) +- [orderFulfillmentDeliverablilityValidationStep](https://docs.medusajs.com/references/medusa-workflows/orderFulfillmentDeliverablilityValidationStep/index.html.md) +- [receiveAndCompleteReturnOrderWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveAndCompleteReturnOrderWorkflow/index.html.md) +- [receiveItemReturnRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestValidationStep/index.html.md) +- [receiveCompleteReturnValidationStep](https://docs.medusajs.com/references/medusa-workflows/receiveCompleteReturnValidationStep/index.html.md) +- [removeAddItemClaimActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeAddItemClaimActionWorkflow/index.html.md) +- [receiveItemReturnRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/receiveItemReturnRequestWorkflow/index.html.md) +- [removeClaimAddItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimAddItemActionValidationStep/index.html.md) +- [removeClaimItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimItemActionValidationStep/index.html.md) +- [removeClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodWorkflow/index.html.md) +- [removeClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeClaimShippingMethodValidationStep/index.html.md) +- [removeExchangeItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeExchangeItemActionValidationStep/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) +- [removeItemExchangeActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemExchangeActionWorkflow/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) +- [removeItemReceiveReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionWorkflow/index.html.md) +- [removeItemReceiveReturnActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeItemReceiveReturnActionValidationStep/index.html.md) +- [removeOrderEditItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeOrderEditItemActionValidationStep/index.html.md) +- [removeItemReturnActionWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeItemReturnActionWorkflow/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) +- [removeReturnItemActionValidationStep](https://docs.medusajs.com/references/medusa-workflows/removeReturnItemActionValidationStep/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) +- [requestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestItemReturnWorkflow/index.html.md) +- [requestOrderTransferValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferValidationStep/index.html.md) +- [requestOrderEditRequestWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestWorkflow/index.html.md) +- [requestOrderEditRequestValidationStep](https://docs.medusajs.com/references/medusa-workflows/requestOrderEditRequestValidationStep/index.html.md) +- [requestOrderTransferWorkflow](https://docs.medusajs.com/references/medusa-workflows/requestOrderTransferWorkflow/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) +- [updateClaimAddItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimAddItemWorkflow/index.html.md) +- [updateClaimItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemValidationStep/index.html.md) +- [updateClaimShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodWorkflow/index.html.md) +- [updateClaimItemWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateClaimItemWorkflow/index.html.md) +- [updateClaimShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateClaimShippingMethodValidationStep/index.html.md) +- [updateExchangeAddItemValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateExchangeAddItemValidationStep/index.html.md) +- [updateExchangeShippingMethodWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateExchangeShippingMethodWorkflow/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) +- [updateOrderChangeActionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangeActionsWorkflow/index.html.md) +- [updateOrderChangesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderChangesWorkflow/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) +- [updateOrderEditItemQuantityValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityValidationStep/index.html.md) +- [updateOrderEditItemQuantityWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditItemQuantityWorkflow/index.html.md) +- [updateOrderEditShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderEditShippingMethodValidationStep/index.html.md) +- [updateOrderValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateOrderValidationStep/index.html.md) +- [updateOrderTaxLinesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateOrderTaxLinesWorkflow/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) +- [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) +- [updateRequestItemReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateRequestItemReturnWorkflow/index.html.md) +- [updateReturnShippingMethodValidationStep](https://docs.medusajs.com/references/medusa-workflows/updateReturnShippingMethodValidationStep/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) +- [updateReturnWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnWorkflow/index.html.md) +- [batchPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchPriceListPricesWorkflow/index.html.md) +- [createPriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createPriceListPricesWorkflow/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) +- [deletePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePriceListsWorkflow/index.html.md) +- [updatePriceListPricesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListPricesWorkflow/index.html.md) +- [updatePriceListsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePriceListsWorkflow/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) +- [batchLinkProductsToCollectionWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchLinkProductsToCollectionWorkflow/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) +- [batchProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/batchProductVariantsWorkflow/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) +- [createProductTagsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductTagsWorkflow/index.html.md) +- [createProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductOptionsWorkflow/index.html.md) +- [createProductVariantsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductVariantsWorkflow/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) +- [deleteProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductOptionsWorkflow/index.html.md) +- [deleteProductTypesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductTypesWorkflow/index.html.md) +- [deleteCollectionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCollectionsWorkflow/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) +- [deleteProductsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductsWorkflow/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) +- [updateProductOptionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductOptionsWorkflow/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) +- [validateProductInputStep](https://docs.medusajs.com/references/medusa-workflows/validateProductInputStep/index.html.md) +- [createProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createProductCategoriesWorkflow/index.html.md) +- [updateProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateProductCategoriesWorkflow/index.html.md) +- [deleteProductCategoriesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteProductCategoriesWorkflow/index.html.md) +- [addOrRemoveCampaignPromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/addOrRemoveCampaignPromotionsWorkflow/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) +- [createCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createCampaignsWorkflow/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) +- [deleteCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteCampaignsWorkflow/index.html.md) +- [deletePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deletePromotionsWorkflow/index.html.md) +- [updateCampaignsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateCampaignsWorkflow/index.html.md) +- [updatePromotionRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionRulesWorkflow/index.html.md) +- [updatePromotionsStatusWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsStatusWorkflow/index.html.md) +- [updatePromotionsValidationStep](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsValidationStep/index.html.md) +- [updatePromotionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updatePromotionsWorkflow/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) +- [deleteRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteRegionsWorkflow/index.html.md) +- [createReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReservationsWorkflow/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) +- [deleteReservationsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReservationsWorkflow/index.html.md) +- [updateReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateReturnReasonsWorkflow/index.html.md) +- [createReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createReturnReasonsWorkflow/index.html.md) +- [deleteReturnReasonsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteReturnReasonsWorkflow/index.html.md) +- [linkProductsToSalesChannelWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkProductsToSalesChannelWorkflow/index.html.md) +- [deleteSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteSalesChannelsWorkflow/index.html.md) +- [createSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createSalesChannelsWorkflow/index.html.md) +- [updateSalesChannelsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateSalesChannelsWorkflow/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) +- [createStoresWorkflow](https://docs.medusajs.com/references/medusa-workflows/createStoresWorkflow/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) +- [createUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUserAccountWorkflow/index.html.md) +- [createUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/createUsersWorkflow/index.html.md) +- [deleteUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteUsersWorkflow/index.html.md) +- [updateUsersWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateUsersWorkflow/index.html.md) +- [removeUserAccountWorkflow](https://docs.medusajs.com/references/medusa-workflows/removeUserAccountWorkflow/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) +- [linkSalesChannelsToStockLocationWorkflow](https://docs.medusajs.com/references/medusa-workflows/linkSalesChannelsToStockLocationWorkflow/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) +- [createTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRateRulesWorkflow/index.html.md) +- [createTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRatesWorkflow/index.html.md) +- [deleteTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRatesWorkflow/index.html.md) +- [deleteTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRegionsWorkflow/index.html.md) +- [createTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/createTaxRegionsWorkflow/index.html.md) +- [deleteTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/deleteTaxRateRulesWorkflow/index.html.md) +- [setTaxRateRulesWorkflow](https://docs.medusajs.com/references/medusa-workflows/setTaxRateRulesWorkflow/index.html.md) +- [maybeListTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/maybeListTaxRateRuleIdsStep/index.html.md) +- [updateTaxRatesWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRatesWorkflow/index.html.md) +- [updateTaxRegionsWorkflow](https://docs.medusajs.com/references/medusa-workflows/updateTaxRegionsWorkflow/index.html.md) ## Steps -- [createApiKeysStep](undefined/references/medusa-workflows/steps/createApiKeysStep/index.html.md) -- [deleteApiKeysStep](undefined/references/medusa-workflows/steps/deleteApiKeysStep/index.html.md) -- [linkSalesChannelsToApiKeyStep](undefined/references/medusa-workflows/steps/linkSalesChannelsToApiKeyStep/index.html.md) -- [revokeApiKeysStep](undefined/references/medusa-workflows/steps/revokeApiKeysStep/index.html.md) -- [updateApiKeysStep](undefined/references/medusa-workflows/steps/updateApiKeysStep/index.html.md) -- [validateSalesChannelsExistStep](undefined/references/medusa-workflows/steps/validateSalesChannelsExistStep/index.html.md) -- [addShippingMethodToCartStep](undefined/references/medusa-workflows/steps/addShippingMethodToCartStep/index.html.md) -- [confirmInventoryStep](undefined/references/medusa-workflows/steps/confirmInventoryStep/index.html.md) -- [createLineItemAdjustmentsStep](undefined/references/medusa-workflows/steps/createLineItemAdjustmentsStep/index.html.md) -- [createCartsStep](undefined/references/medusa-workflows/steps/createCartsStep/index.html.md) -- [createLineItemsStep](undefined/references/medusa-workflows/steps/createLineItemsStep/index.html.md) -- [createPaymentCollectionsStep](undefined/references/medusa-workflows/steps/createPaymentCollectionsStep/index.html.md) -- [createShippingMethodAdjustmentsStep](undefined/references/medusa-workflows/steps/createShippingMethodAdjustmentsStep/index.html.md) -- [findOneOrAnyRegionStep](undefined/references/medusa-workflows/steps/findOneOrAnyRegionStep/index.html.md) -- [findSalesChannelStep](undefined/references/medusa-workflows/steps/findSalesChannelStep/index.html.md) -- [findOrCreateCustomerStep](undefined/references/medusa-workflows/steps/findOrCreateCustomerStep/index.html.md) -- [getActionsToComputeFromPromotionsStep](undefined/references/medusa-workflows/steps/getActionsToComputeFromPromotionsStep/index.html.md) -- [getLineItemActionsStep](undefined/references/medusa-workflows/steps/getLineItemActionsStep/index.html.md) -- [getPromotionCodesToApply](undefined/references/medusa-workflows/steps/getPromotionCodesToApply/index.html.md) -- [getVariantPriceSetsStep](undefined/references/medusa-workflows/steps/getVariantPriceSetsStep/index.html.md) -- [prepareAdjustmentsFromPromotionActionsStep](undefined/references/medusa-workflows/steps/prepareAdjustmentsFromPromotionActionsStep/index.html.md) -- [getVariantsStep](undefined/references/medusa-workflows/steps/getVariantsStep/index.html.md) -- [removeLineItemAdjustmentsStep](undefined/references/medusa-workflows/steps/removeLineItemAdjustmentsStep/index.html.md) -- [removeShippingMethodFromCartStep](undefined/references/medusa-workflows/steps/removeShippingMethodFromCartStep/index.html.md) -- [removeShippingMethodAdjustmentsStep](undefined/references/medusa-workflows/steps/removeShippingMethodAdjustmentsStep/index.html.md) -- [reserveInventoryStep](undefined/references/medusa-workflows/steps/reserveInventoryStep/index.html.md) -- [retrieveCartStep](undefined/references/medusa-workflows/steps/retrieveCartStep/index.html.md) -- [setTaxLinesForItemsStep](undefined/references/medusa-workflows/steps/setTaxLinesForItemsStep/index.html.md) -- [updateCartPromotionsStep](undefined/references/medusa-workflows/steps/updateCartPromotionsStep/index.html.md) -- [updateLineItemsStep](undefined/references/medusa-workflows/steps/updateLineItemsStep/index.html.md) -- [updateCartsStep](undefined/references/medusa-workflows/steps/updateCartsStep/index.html.md) -- [updateShippingMethodsStep](undefined/references/medusa-workflows/steps/updateShippingMethodsStep/index.html.md) -- [validateAndReturnShippingMethodsDataStep](undefined/references/medusa-workflows/steps/validateAndReturnShippingMethodsDataStep/index.html.md) -- [validateCartPaymentsStep](undefined/references/medusa-workflows/steps/validateCartPaymentsStep/index.html.md) -- [validateCartShippingOptionsPriceStep](undefined/references/medusa-workflows/steps/validateCartShippingOptionsPriceStep/index.html.md) -- [validateCartStep](undefined/references/medusa-workflows/steps/validateCartStep/index.html.md) -- [validateCartShippingOptionsStep](undefined/references/medusa-workflows/steps/validateCartShippingOptionsStep/index.html.md) -- [validateLineItemPricesStep](undefined/references/medusa-workflows/steps/validateLineItemPricesStep/index.html.md) -- [validateVariantPricesStep](undefined/references/medusa-workflows/steps/validateVariantPricesStep/index.html.md) -- [validateShippingStep](undefined/references/medusa-workflows/steps/validateShippingStep/index.html.md) -- [setAuthAppMetadataStep](undefined/references/medusa-workflows/steps/setAuthAppMetadataStep/index.html.md) -- [dismissRemoteLinkStep](undefined/references/medusa-workflows/steps/dismissRemoteLinkStep/index.html.md) -- [createRemoteLinkStep](undefined/references/medusa-workflows/steps/createRemoteLinkStep/index.html.md) -- [emitEventStep](undefined/references/medusa-workflows/steps/emitEventStep/index.html.md) -- [removeRemoteLinkStep](undefined/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md) -- [updateRemoteLinksStep](undefined/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md) -- [useQueryGraphStep](undefined/references/medusa-workflows/steps/useQueryGraphStep/index.html.md) -- [validatePresenceOfStep](undefined/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md) -- [useRemoteQueryStep](undefined/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md) -- [createCustomerAddressesStep](undefined/references/medusa-workflows/steps/createCustomerAddressesStep/index.html.md) -- [deleteCustomerAddressesStep](undefined/references/medusa-workflows/steps/deleteCustomerAddressesStep/index.html.md) -- [deleteCustomersStep](undefined/references/medusa-workflows/steps/deleteCustomersStep/index.html.md) -- [createCustomersStep](undefined/references/medusa-workflows/steps/createCustomersStep/index.html.md) -- [maybeUnsetDefaultBillingAddressesStep](undefined/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md) -- [maybeUnsetDefaultShippingAddressesStep](undefined/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/index.html.md) -- [updateCustomerAddressesStep](undefined/references/medusa-workflows/steps/updateCustomerAddressesStep/index.html.md) -- [updateCustomersStep](undefined/references/medusa-workflows/steps/updateCustomersStep/index.html.md) -- [validateCustomerAccountCreation](undefined/references/medusa-workflows/steps/validateCustomerAccountCreation/index.html.md) -- [deleteCustomerGroupStep](undefined/references/medusa-workflows/steps/deleteCustomerGroupStep/index.html.md) -- [createCustomerGroupsStep](undefined/references/medusa-workflows/steps/createCustomerGroupsStep/index.html.md) -- [linkCustomerGroupsToCustomerStep](undefined/references/medusa-workflows/steps/linkCustomerGroupsToCustomerStep/index.html.md) -- [linkCustomersToCustomerGroupStep](undefined/references/medusa-workflows/steps/linkCustomersToCustomerGroupStep/index.html.md) -- [updateCustomerGroupsStep](undefined/references/medusa-workflows/steps/updateCustomerGroupsStep/index.html.md) -- [deleteFilesStep](undefined/references/medusa-workflows/steps/deleteFilesStep/index.html.md) -- [uploadFilesStep](undefined/references/medusa-workflows/steps/uploadFilesStep/index.html.md) -- [createDefaultStoreStep](undefined/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md) -- [buildPriceSet](undefined/references/medusa-workflows/steps/buildPriceSet/index.html.md) -- [calculateShippingOptionsPricesStep](undefined/references/medusa-workflows/steps/calculateShippingOptionsPricesStep/index.html.md) -- [cancelFulfillmentStep](undefined/references/medusa-workflows/steps/cancelFulfillmentStep/index.html.md) -- [createFulfillmentSets](undefined/references/medusa-workflows/steps/createFulfillmentSets/index.html.md) -- [createFulfillmentStep](undefined/references/medusa-workflows/steps/createFulfillmentStep/index.html.md) -- [createReturnFulfillmentStep](undefined/references/medusa-workflows/steps/createReturnFulfillmentStep/index.html.md) -- [createServiceZonesStep](undefined/references/medusa-workflows/steps/createServiceZonesStep/index.html.md) -- [createShippingOptionRulesStep](undefined/references/medusa-workflows/steps/createShippingOptionRulesStep/index.html.md) -- [createShippingOptionsPriceSetsStep](undefined/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/index.html.md) -- [createShippingProfilesStep](undefined/references/medusa-workflows/steps/createShippingProfilesStep/index.html.md) -- [deleteFulfillmentSetsStep](undefined/references/medusa-workflows/steps/deleteFulfillmentSetsStep/index.html.md) -- [deleteServiceZonesStep](undefined/references/medusa-workflows/steps/deleteServiceZonesStep/index.html.md) -- [deleteShippingOptionRulesStep](undefined/references/medusa-workflows/steps/deleteShippingOptionRulesStep/index.html.md) -- [deleteShippingOptionsStep](undefined/references/medusa-workflows/steps/deleteShippingOptionsStep/index.html.md) -- [setShippingOptionsPricesStep](undefined/references/medusa-workflows/steps/setShippingOptionsPricesStep/index.html.md) -- [updateFulfillmentStep](undefined/references/medusa-workflows/steps/updateFulfillmentStep/index.html.md) -- [updateServiceZonesStep](undefined/references/medusa-workflows/steps/updateServiceZonesStep/index.html.md) -- [updateShippingOptionRulesStep](undefined/references/medusa-workflows/steps/updateShippingOptionRulesStep/index.html.md) -- [updateShippingProfilesStep](undefined/references/medusa-workflows/steps/updateShippingProfilesStep/index.html.md) -- [upsertShippingOptionsStep](undefined/references/medusa-workflows/steps/upsertShippingOptionsStep/index.html.md) -- [validateShipmentStep](undefined/references/medusa-workflows/steps/validateShipmentStep/index.html.md) -- [validateShippingOptionPricesStep](undefined/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md) -- [adjustInventoryLevelsStep](undefined/references/medusa-workflows/steps/adjustInventoryLevelsStep/index.html.md) -- [attachInventoryItemToVariants](undefined/references/medusa-workflows/steps/attachInventoryItemToVariants/index.html.md) -- [createInventoryItemsStep](undefined/references/medusa-workflows/steps/createInventoryItemsStep/index.html.md) -- [deleteInventoryItemStep](undefined/references/medusa-workflows/steps/deleteInventoryItemStep/index.html.md) -- [createInventoryLevelsStep](undefined/references/medusa-workflows/steps/createInventoryLevelsStep/index.html.md) -- [updateInventoryItemsStep](undefined/references/medusa-workflows/steps/updateInventoryItemsStep/index.html.md) -- [deleteInventoryLevelsStep](undefined/references/medusa-workflows/steps/deleteInventoryLevelsStep/index.html.md) -- [updateInventoryLevelsStep](undefined/references/medusa-workflows/steps/updateInventoryLevelsStep/index.html.md) -- [validateInventoryDeleteStep](undefined/references/medusa-workflows/steps/validateInventoryDeleteStep/index.html.md) -- [validateInventoryLocationsStep](undefined/references/medusa-workflows/steps/validateInventoryLocationsStep/index.html.md) -- [validateInventoryItemsForCreate](undefined/references/medusa-workflows/steps/validateInventoryItemsForCreate/index.html.md) -- [deleteLineItemsStep](undefined/references/medusa-workflows/steps/deleteLineItemsStep/index.html.md) -- [updateLineItemsStepWithSelector](undefined/references/medusa-workflows/steps/updateLineItemsStepWithSelector/index.html.md) -- [listLineItemsStep](undefined/references/medusa-workflows/steps/listLineItemsStep/index.html.md) -- [createInviteStep](undefined/references/medusa-workflows/steps/createInviteStep/index.html.md) -- [refreshInviteTokensStep](undefined/references/medusa-workflows/steps/refreshInviteTokensStep/index.html.md) -- [deleteInvitesStep](undefined/references/medusa-workflows/steps/deleteInvitesStep/index.html.md) -- [validateTokenStep](undefined/references/medusa-workflows/steps/validateTokenStep/index.html.md) -- [notifyOnFailureStep](undefined/references/medusa-workflows/steps/notifyOnFailureStep/index.html.md) -- [sendNotificationsStep](undefined/references/medusa-workflows/steps/sendNotificationsStep/index.html.md) -- [addOrderTransactionStep](undefined/references/medusa-workflows/steps/addOrderTransactionStep/index.html.md) -- [archiveOrdersStep](undefined/references/medusa-workflows/steps/archiveOrdersStep/index.html.md) -- [cancelOrderChangeStep](undefined/references/medusa-workflows/steps/cancelOrderChangeStep/index.html.md) -- [cancelOrderClaimStep](undefined/references/medusa-workflows/steps/cancelOrderClaimStep/index.html.md) -- [cancelOrderExchangeStep](undefined/references/medusa-workflows/steps/cancelOrderExchangeStep/index.html.md) -- [cancelOrderFulfillmentStep](undefined/references/medusa-workflows/steps/cancelOrderFulfillmentStep/index.html.md) -- [cancelOrderReturnStep](undefined/references/medusa-workflows/steps/cancelOrderReturnStep/index.html.md) -- [completeOrdersStep](undefined/references/medusa-workflows/steps/completeOrdersStep/index.html.md) -- [cancelOrdersStep](undefined/references/medusa-workflows/steps/cancelOrdersStep/index.html.md) -- [createCompleteReturnStep](undefined/references/medusa-workflows/steps/createCompleteReturnStep/index.html.md) -- [createOrderChangeStep](undefined/references/medusa-workflows/steps/createOrderChangeStep/index.html.md) -- [createOrderClaimsStep](undefined/references/medusa-workflows/steps/createOrderClaimsStep/index.html.md) -- [createOrderClaimItemsFromActionsStep](undefined/references/medusa-workflows/steps/createOrderClaimItemsFromActionsStep/index.html.md) -- [createOrderExchangesStep](undefined/references/medusa-workflows/steps/createOrderExchangesStep/index.html.md) -- [createOrderExchangeItemsFromActionsStep](undefined/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md) -- [createOrdersStep](undefined/references/medusa-workflows/steps/createOrdersStep/index.html.md) -- [createOrderLineItemsStep](undefined/references/medusa-workflows/steps/createOrderLineItemsStep/index.html.md) -- [declineOrderChangeStep](undefined/references/medusa-workflows/steps/declineOrderChangeStep/index.html.md) -- [createReturnsStep](undefined/references/medusa-workflows/steps/createReturnsStep/index.html.md) -- [deleteClaimsStep](undefined/references/medusa-workflows/steps/deleteClaimsStep/index.html.md) -- [deleteExchangesStep](undefined/references/medusa-workflows/steps/deleteExchangesStep/index.html.md) -- [deleteOrderChangesStep](undefined/references/medusa-workflows/steps/deleteOrderChangesStep/index.html.md) -- [deleteOrderChangeActionsStep](undefined/references/medusa-workflows/steps/deleteOrderChangeActionsStep/index.html.md) -- [deleteOrderLineItems](undefined/references/medusa-workflows/steps/deleteOrderLineItems/index.html.md) -- [deleteOrderShippingMethods](undefined/references/medusa-workflows/steps/deleteOrderShippingMethods/index.html.md) -- [previewOrderChangeStep](undefined/references/medusa-workflows/steps/previewOrderChangeStep/index.html.md) -- [deleteReturnsStep](undefined/references/medusa-workflows/steps/deleteReturnsStep/index.html.md) -- [registerOrderChangesStep](undefined/references/medusa-workflows/steps/registerOrderChangesStep/index.html.md) -- [registerOrderFulfillmentStep](undefined/references/medusa-workflows/steps/registerOrderFulfillmentStep/index.html.md) -- [registerOrderShipmentStep](undefined/references/medusa-workflows/steps/registerOrderShipmentStep/index.html.md) -- [setOrderTaxLinesForItemsStep](undefined/references/medusa-workflows/steps/setOrderTaxLinesForItemsStep/index.html.md) -- [updateOrderChangesStep](undefined/references/medusa-workflows/steps/updateOrderChangesStep/index.html.md) -- [updateOrderChangeActionsStep](undefined/references/medusa-workflows/steps/updateOrderChangeActionsStep/index.html.md) -- [updateOrderShippingMethodsStep](undefined/references/medusa-workflows/steps/updateOrderShippingMethodsStep/index.html.md) -- [updateOrdersStep](undefined/references/medusa-workflows/steps/updateOrdersStep/index.html.md) -- [updateReturnItemsStep](undefined/references/medusa-workflows/steps/updateReturnItemsStep/index.html.md) -- [updateReturnsStep](undefined/references/medusa-workflows/steps/updateReturnsStep/index.html.md) -- [authorizePaymentSessionStep](undefined/references/medusa-workflows/steps/authorizePaymentSessionStep/index.html.md) -- [cancelPaymentStep](undefined/references/medusa-workflows/steps/cancelPaymentStep/index.html.md) -- [capturePaymentStep](undefined/references/medusa-workflows/steps/capturePaymentStep/index.html.md) -- [refundPaymentStep](undefined/references/medusa-workflows/steps/refundPaymentStep/index.html.md) -- [refundPaymentsStep](undefined/references/medusa-workflows/steps/refundPaymentsStep/index.html.md) -- [createPaymentAccountHolderStep](undefined/references/medusa-workflows/steps/createPaymentAccountHolderStep/index.html.md) -- [createPaymentSessionStep](undefined/references/medusa-workflows/steps/createPaymentSessionStep/index.html.md) -- [createRefundReasonStep](undefined/references/medusa-workflows/steps/createRefundReasonStep/index.html.md) -- [deletePaymentSessionsStep](undefined/references/medusa-workflows/steps/deletePaymentSessionsStep/index.html.md) -- [deleteRefundReasonsStep](undefined/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md) -- [updatePaymentCollectionStep](undefined/references/medusa-workflows/steps/updatePaymentCollectionStep/index.html.md) -- [updateRefundReasonsStep](undefined/references/medusa-workflows/steps/updateRefundReasonsStep/index.html.md) -- [validateDeletedPaymentSessionsStep](undefined/references/medusa-workflows/steps/validateDeletedPaymentSessionsStep/index.html.md) -- [createPricePreferencesStep](undefined/references/medusa-workflows/steps/createPricePreferencesStep/index.html.md) -- [createPriceSetsStep](undefined/references/medusa-workflows/steps/createPriceSetsStep/index.html.md) -- [deletePricePreferencesStep](undefined/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md) -- [updatePricePreferencesAsArrayStep](undefined/references/medusa-workflows/steps/updatePricePreferencesAsArrayStep/index.html.md) -- [updatePricePreferencesStep](undefined/references/medusa-workflows/steps/updatePricePreferencesStep/index.html.md) -- [updatePriceSetsStep](undefined/references/medusa-workflows/steps/updatePriceSetsStep/index.html.md) -- [batchLinkProductsToCategoryStep](undefined/references/medusa-workflows/steps/batchLinkProductsToCategoryStep/index.html.md) -- [batchLinkProductsToCollectionStep](undefined/references/medusa-workflows/steps/batchLinkProductsToCollectionStep/index.html.md) -- [createCollectionsStep](undefined/references/medusa-workflows/steps/createCollectionsStep/index.html.md) -- [createProductOptionsStep](undefined/references/medusa-workflows/steps/createProductOptionsStep/index.html.md) -- [createProductTagsStep](undefined/references/medusa-workflows/steps/createProductTagsStep/index.html.md) -- [createProductTypesStep](undefined/references/medusa-workflows/steps/createProductTypesStep/index.html.md) -- [createProductVariantsStep](undefined/references/medusa-workflows/steps/createProductVariantsStep/index.html.md) -- [createProductsStep](undefined/references/medusa-workflows/steps/createProductsStep/index.html.md) -- [createVariantPricingLinkStep](undefined/references/medusa-workflows/steps/createVariantPricingLinkStep/index.html.md) -- [deleteCollectionsStep](undefined/references/medusa-workflows/steps/deleteCollectionsStep/index.html.md) -- [deleteProductOptionsStep](undefined/references/medusa-workflows/steps/deleteProductOptionsStep/index.html.md) -- [deleteProductTagsStep](undefined/references/medusa-workflows/steps/deleteProductTagsStep/index.html.md) -- [deleteProductTypesStep](undefined/references/medusa-workflows/steps/deleteProductTypesStep/index.html.md) -- [deleteProductVariantsStep](undefined/references/medusa-workflows/steps/deleteProductVariantsStep/index.html.md) -- [deleteProductsStep](undefined/references/medusa-workflows/steps/deleteProductsStep/index.html.md) -- [generateProductCsvStep](undefined/references/medusa-workflows/steps/generateProductCsvStep/index.html.md) -- [getAllProductsStep](undefined/references/medusa-workflows/steps/getAllProductsStep/index.html.md) -- [getProductsStep](undefined/references/medusa-workflows/steps/getProductsStep/index.html.md) -- [getVariantAvailabilityStep](undefined/references/medusa-workflows/steps/getVariantAvailabilityStep/index.html.md) -- [groupProductsForBatchStep](undefined/references/medusa-workflows/steps/groupProductsForBatchStep/index.html.md) -- [parseProductCsvStep](undefined/references/medusa-workflows/steps/parseProductCsvStep/index.html.md) -- [updateCollectionsStep](undefined/references/medusa-workflows/steps/updateCollectionsStep/index.html.md) -- [updateProductOptionsStep](undefined/references/medusa-workflows/steps/updateProductOptionsStep/index.html.md) -- [updateProductTagsStep](undefined/references/medusa-workflows/steps/updateProductTagsStep/index.html.md) -- [updateProductTypesStep](undefined/references/medusa-workflows/steps/updateProductTypesStep/index.html.md) -- [updateProductVariantsStep](undefined/references/medusa-workflows/steps/updateProductVariantsStep/index.html.md) -- [updateProductsStep](undefined/references/medusa-workflows/steps/updateProductsStep/index.html.md) -- [waitConfirmationProductImportStep](undefined/references/medusa-workflows/steps/waitConfirmationProductImportStep/index.html.md) -- [createProductCategoriesStep](undefined/references/medusa-workflows/steps/createProductCategoriesStep/index.html.md) -- [deleteProductCategoriesStep](undefined/references/medusa-workflows/steps/deleteProductCategoriesStep/index.html.md) -- [updateProductCategoriesStep](undefined/references/medusa-workflows/steps/updateProductCategoriesStep/index.html.md) -- [createRegionsStep](undefined/references/medusa-workflows/steps/createRegionsStep/index.html.md) -- [deleteRegionsStep](undefined/references/medusa-workflows/steps/deleteRegionsStep/index.html.md) -- [setRegionsPaymentProvidersStep](undefined/references/medusa-workflows/steps/setRegionsPaymentProvidersStep/index.html.md) -- [updateRegionsStep](undefined/references/medusa-workflows/steps/updateRegionsStep/index.html.md) -- [addCampaignPromotionsStep](undefined/references/medusa-workflows/steps/addCampaignPromotionsStep/index.html.md) -- [addRulesToPromotionsStep](undefined/references/medusa-workflows/steps/addRulesToPromotionsStep/index.html.md) -- [createPromotionsStep](undefined/references/medusa-workflows/steps/createPromotionsStep/index.html.md) -- [createCampaignsStep](undefined/references/medusa-workflows/steps/createCampaignsStep/index.html.md) -- [deleteCampaignsStep](undefined/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md) -- [deletePromotionsStep](undefined/references/medusa-workflows/steps/deletePromotionsStep/index.html.md) -- [removeCampaignPromotionsStep](undefined/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md) -- [removeRulesFromPromotionsStep](undefined/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md) -- [updatePromotionRulesStep](undefined/references/medusa-workflows/steps/updatePromotionRulesStep/index.html.md) -- [updateCampaignsStep](undefined/references/medusa-workflows/steps/updateCampaignsStep/index.html.md) -- [updatePromotionsStep](undefined/references/medusa-workflows/steps/updatePromotionsStep/index.html.md) -- [createReturnReasonsStep](undefined/references/medusa-workflows/steps/createReturnReasonsStep/index.html.md) -- [deleteReturnReasonStep](undefined/references/medusa-workflows/steps/deleteReturnReasonStep/index.html.md) -- [updateReturnReasonsStep](undefined/references/medusa-workflows/steps/updateReturnReasonsStep/index.html.md) -- [deleteReservationsByLineItemsStep](undefined/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md) -- [createReservationsStep](undefined/references/medusa-workflows/steps/createReservationsStep/index.html.md) -- [deleteReservationsStep](undefined/references/medusa-workflows/steps/deleteReservationsStep/index.html.md) -- [updateReservationsStep](undefined/references/medusa-workflows/steps/updateReservationsStep/index.html.md) -- [listShippingOptionsForContextStep](undefined/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md) -- [associateLocationsWithSalesChannelsStep](undefined/references/medusa-workflows/steps/associateLocationsWithSalesChannelsStep/index.html.md) -- [associateProductsWithSalesChannelsStep](undefined/references/medusa-workflows/steps/associateProductsWithSalesChannelsStep/index.html.md) -- [canDeleteSalesChannelsOrThrowStep](undefined/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md) -- [createSalesChannelsStep](undefined/references/medusa-workflows/steps/createSalesChannelsStep/index.html.md) -- [createDefaultSalesChannelStep](undefined/references/medusa-workflows/steps/createDefaultSalesChannelStep/index.html.md) -- [deleteSalesChannelsStep](undefined/references/medusa-workflows/steps/deleteSalesChannelsStep/index.html.md) -- [detachLocationsFromSalesChannelsStep](undefined/references/medusa-workflows/steps/detachLocationsFromSalesChannelsStep/index.html.md) -- [detachProductsFromSalesChannelsStep](undefined/references/medusa-workflows/steps/detachProductsFromSalesChannelsStep/index.html.md) -- [updateSalesChannelsStep](undefined/references/medusa-workflows/steps/updateSalesChannelsStep/index.html.md) -- [createStockLocations](undefined/references/medusa-workflows/steps/createStockLocations/index.html.md) -- [deleteStockLocationsStep](undefined/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md) -- [updateStockLocationsStep](undefined/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md) -- [deleteShippingProfilesStep](undefined/references/medusa-workflows/steps/deleteShippingProfilesStep/index.html.md) -- [createTaxRateRulesStep](undefined/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md) -- [createTaxRegionsStep](undefined/references/medusa-workflows/steps/createTaxRegionsStep/index.html.md) -- [createTaxRatesStep](undefined/references/medusa-workflows/steps/createTaxRatesStep/index.html.md) -- [deleteTaxRateRulesStep](undefined/references/medusa-workflows/steps/deleteTaxRateRulesStep/index.html.md) -- [deleteTaxRatesStep](undefined/references/medusa-workflows/steps/deleteTaxRatesStep/index.html.md) -- [deleteTaxRegionsStep](undefined/references/medusa-workflows/steps/deleteTaxRegionsStep/index.html.md) -- [getItemTaxLinesStep](undefined/references/medusa-workflows/steps/getItemTaxLinesStep/index.html.md) -- [listTaxRateIdsStep](undefined/references/medusa-workflows/steps/listTaxRateIdsStep/index.html.md) -- [listTaxRateRuleIdsStep](undefined/references/medusa-workflows/steps/listTaxRateRuleIdsStep/index.html.md) -- [updateTaxRatesStep](undefined/references/medusa-workflows/steps/updateTaxRatesStep/index.html.md) -- [updateTaxRegionsStep](undefined/references/medusa-workflows/steps/updateTaxRegionsStep/index.html.md) -- [createStoresStep](undefined/references/medusa-workflows/steps/createStoresStep/index.html.md) -- [deleteStoresStep](undefined/references/medusa-workflows/steps/deleteStoresStep/index.html.md) -- [updateStoresStep](undefined/references/medusa-workflows/steps/updateStoresStep/index.html.md) -- [createUsersStep](undefined/references/medusa-workflows/steps/createUsersStep/index.html.md) -- [deleteUsersStep](undefined/references/medusa-workflows/steps/deleteUsersStep/index.html.md) -- [updateUsersStep](undefined/references/medusa-workflows/steps/updateUsersStep/index.html.md) -- [createPriceListPricesStep](undefined/references/medusa-workflows/steps/createPriceListPricesStep/index.html.md) -- [createPriceListsStep](undefined/references/medusa-workflows/steps/createPriceListsStep/index.html.md) -- [deletePriceListsStep](undefined/references/medusa-workflows/steps/deletePriceListsStep/index.html.md) -- [getExistingPriceListsPriceIdsStep](undefined/references/medusa-workflows/steps/getExistingPriceListsPriceIdsStep/index.html.md) -- [removePriceListPricesStep](undefined/references/medusa-workflows/steps/removePriceListPricesStep/index.html.md) -- [updatePriceListPricesStep](undefined/references/medusa-workflows/steps/updatePriceListPricesStep/index.html.md) -- [validatePriceListsStep](undefined/references/medusa-workflows/steps/validatePriceListsStep/index.html.md) -- [updatePriceListsStep](undefined/references/medusa-workflows/steps/updatePriceListsStep/index.html.md) -- [validateVariantPriceLinksStep](undefined/references/medusa-workflows/steps/validateVariantPriceLinksStep/index.html.md) +- [setAuthAppMetadataStep](https://docs.medusajs.com/references/medusa-workflows/steps/setAuthAppMetadataStep/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) +- [removeRemoteLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRemoteLinkStep/index.html.md) +- [updateRemoteLinksStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateRemoteLinksStep/index.html.md) +- [emitEventStep](https://docs.medusajs.com/references/medusa-workflows/steps/emitEventStep/index.html.md) +- [useQueryGraphStep](https://docs.medusajs.com/references/medusa-workflows/steps/useQueryGraphStep/index.html.md) +- [addShippingMethodToCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/addShippingMethodToCartStep/index.html.md) +- [useRemoteQueryStep](https://docs.medusajs.com/references/medusa-workflows/steps/useRemoteQueryStep/index.html.md) +- [validatePresenceOfStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePresenceOfStep/index.html.md) +- [createLineItemAdjustmentsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemAdjustmentsStep/index.html.md) +- [createLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createLineItemsStep/index.html.md) +- [createCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCartsStep/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) +- [confirmInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/confirmInventoryStep/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) +- [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) +- [getVariantPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantPriceSetsStep/index.html.md) +- [reserveInventoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/reserveInventoryStep/index.html.md) +- [removeShippingMethodFromCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeShippingMethodFromCartStep/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) +- [updateCartsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartsStep/index.html.md) +- [updateCartPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCartPromotionsStep/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) +- [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) +- [validateCartShippingOptionsPriceStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartShippingOptionsPriceStep/index.html.md) +- [validateLineItemPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateLineItemPricesStep/index.html.md) +- [validateCartStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateCartStep/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) +- [revokeApiKeysStep](https://docs.medusajs.com/references/medusa-workflows/steps/revokeApiKeysStep/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) +- [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) +- [createCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createCustomersStep/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) +- [maybeUnsetDefaultBillingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultBillingAddressesStep/index.html.md) +- [deleteCustomersStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCustomersStep/index.html.md) +- [maybeUnsetDefaultShippingAddressesStep](https://docs.medusajs.com/references/medusa-workflows/steps/maybeUnsetDefaultShippingAddressesStep/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) +- [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) +- [validateCustomerAccountCreation](https://docs.medusajs.com/references/medusa-workflows/steps/validateCustomerAccountCreation/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) +- [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) +- [createDefaultStoreStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultStoreStep/index.html.md) +- [buildPriceSet](https://docs.medusajs.com/references/medusa-workflows/steps/buildPriceSet/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) +- [createReturnFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnFulfillmentStep/index.html.md) +- [createFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/createFulfillmentStep/index.html.md) +- [createServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createServiceZonesStep/index.html.md) +- [createShippingOptionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionRulesStep/index.html.md) +- [createShippingOptionsPriceSetsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createShippingOptionsPriceSetsStep/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) +- [deleteServiceZonesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteServiceZonesStep/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) +- [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) +- [updateShippingProfilesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateShippingProfilesStep/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) +- [validateShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShipmentStep/index.html.md) +- [validateShippingOptionPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateShippingOptionPricesStep/index.html.md) +- [adjustInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/adjustInventoryLevelsStep/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) +- [attachInventoryItemToVariants](https://docs.medusajs.com/references/medusa-workflows/steps/attachInventoryItemToVariants/index.html.md) +- [deleteInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteInventoryLevelsStep/index.html.md) +- [updateInventoryLevelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryLevelsStep/index.html.md) +- [updateInventoryItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateInventoryItemsStep/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) +- [validateInventoryItemsForCreate](https://docs.medusajs.com/references/medusa-workflows/steps/validateInventoryItemsForCreate/index.html.md) +- [createInviteStep](https://docs.medusajs.com/references/medusa-workflows/steps/createInviteStep/index.html.md) +- [validateTokenStep](https://docs.medusajs.com/references/medusa-workflows/steps/validateTokenStep/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) +- [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) +- [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) +- [archiveOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/archiveOrdersStep/index.html.md) +- [addOrderTransactionStep](https://docs.medusajs.com/references/medusa-workflows/steps/addOrderTransactionStep/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) +- [cancelOrderReturnStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderReturnStep/index.html.md) +- [cancelOrderExchangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/cancelOrderExchangeStep/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) +- [createOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderChangeStep/index.html.md) +- [completeOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/completeOrdersStep/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) +- [createOrderExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangesStep/index.html.md) +- [createOrderExchangeItemsFromActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderExchangeItemsFromActionsStep/index.html.md) +- [createOrderLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrderLineItemsStep/index.html.md) +- [declineOrderChangeStep](https://docs.medusajs.com/references/medusa-workflows/steps/declineOrderChangeStep/index.html.md) +- [createOrdersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createOrdersStep/index.html.md) +- [createReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createReturnsStep/index.html.md) +- [deleteClaimsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteClaimsStep/index.html.md) +- [deleteOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangesStep/index.html.md) +- [deleteOrderChangeActionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteOrderChangeActionsStep/index.html.md) +- [deleteExchangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteExchangesStep/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) +- [deleteReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReturnsStep/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) +- [setOrderTaxLinesForItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/setOrderTaxLinesForItemsStep/index.html.md) +- [registerOrderFulfillmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderFulfillmentStep/index.html.md) +- [registerOrderShipmentStep](https://docs.medusajs.com/references/medusa-workflows/steps/registerOrderShipmentStep/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) +- [updateOrderChangesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderChangesStep/index.html.md) +- [updateOrderShippingMethodsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateOrderShippingMethodsStep/index.html.md) +- [updateReturnItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnItemsStep/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) +- [updateReturnsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnsStep/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) +- [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) +- [createRefundReasonStep](https://docs.medusajs.com/references/medusa-workflows/steps/createRefundReasonStep/index.html.md) +- [deleteRefundReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteRefundReasonsStep/index.html.md) +- [createPaymentAccountHolderStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPaymentAccountHolderStep/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) +- [createPriceListPricesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListPricesStep/index.html.md) +- [deletePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePriceListsStep/index.html.md) +- [createPriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPriceListsStep/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) +- [validatePriceListsStep](https://docs.medusajs.com/references/medusa-workflows/steps/validatePriceListsStep/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) +- [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) +- [deletePricePreferencesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePricePreferencesStep/index.html.md) +- [updatePricePreferencesAsArrayStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePricePreferencesAsArrayStep/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) +- [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) +- [createPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createPromotionsStep/index.html.md) +- [addRulesToPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/addRulesToPromotionsStep/index.html.md) +- [deleteCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCampaignsStep/index.html.md) +- [removeCampaignPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeCampaignPromotionsStep/index.html.md) +- [removeRulesFromPromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/removeRulesFromPromotionsStep/index.html.md) +- [deletePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deletePromotionsStep/index.html.md) +- [updateCampaignsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCampaignsStep/index.html.md) +- [updatePromotionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionsStep/index.html.md) +- [updatePromotionRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/updatePromotionRulesStep/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) +- [createProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTagsStep/index.html.md) +- [createProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductOptionsStep/index.html.md) +- [batchLinkProductsToCategoryStep](https://docs.medusajs.com/references/medusa-workflows/steps/batchLinkProductsToCategoryStep/index.html.md) +- [createProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductVariantsStep/index.html.md) +- [createProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductsStep/index.html.md) +- [createProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createProductTypesStep/index.html.md) +- [deleteCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteCollectionsStep/index.html.md) +- [deleteProductOptionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductOptionsStep/index.html.md) +- [deleteProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTagsStep/index.html.md) +- [createVariantPricingLinkStep](https://docs.medusajs.com/references/medusa-workflows/steps/createVariantPricingLinkStep/index.html.md) +- [deleteProductTypesStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductTypesStep/index.html.md) +- [deleteProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductVariantsStep/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) +- [deleteProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteProductsStep/index.html.md) +- [parseProductCsvStep](https://docs.medusajs.com/references/medusa-workflows/steps/parseProductCsvStep/index.html.md) +- [getVariantAvailabilityStep](https://docs.medusajs.com/references/medusa-workflows/steps/getVariantAvailabilityStep/index.html.md) +- [groupProductsForBatchStep](https://docs.medusajs.com/references/medusa-workflows/steps/groupProductsForBatchStep/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) +- [updateProductTagsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductTagsStep/index.html.md) +- [updateCollectionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateCollectionsStep/index.html.md) +- [updateProductsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductsStep/index.html.md) +- [waitConfirmationProductImportStep](https://docs.medusajs.com/references/medusa-workflows/steps/waitConfirmationProductImportStep/index.html.md) +- [updateProductVariantsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateProductVariantsStep/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) +- [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) +- [deleteReservationsByLineItemsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteReservationsByLineItemsStep/index.html.md) +- [updateReturnReasonsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateReturnReasonsStep/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) +- [associateLocationsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateLocationsWithSalesChannelsStep/index.html.md) +- [canDeleteSalesChannelsOrThrowStep](https://docs.medusajs.com/references/medusa-workflows/steps/canDeleteSalesChannelsOrThrowStep/index.html.md) +- [associateProductsWithSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/associateProductsWithSalesChannelsStep/index.html.md) +- [createDefaultSalesChannelStep](https://docs.medusajs.com/references/medusa-workflows/steps/createDefaultSalesChannelStep/index.html.md) +- [deleteSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteSalesChannelsStep/index.html.md) +- [detachLocationsFromSalesChannelsStep](https://docs.medusajs.com/references/medusa-workflows/steps/detachLocationsFromSalesChannelsStep/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) +- [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) +- [updateStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStockLocationsStep/index.html.md) +- [deleteStockLocationsStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStockLocationsStep/index.html.md) +- [listShippingOptionsForContextStep](https://docs.medusajs.com/references/medusa-workflows/steps/listShippingOptionsForContextStep/index.html.md) +- [createStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/createStoresStep/index.html.md) +- [deleteStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/deleteStoresStep/index.html.md) +- [updateStoresStep](https://docs.medusajs.com/references/medusa-workflows/steps/updateStoresStep/index.html.md) +- [createTaxRateRulesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRateRulesStep/index.html.md) +- [createTaxRatesStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRatesStep/index.html.md) +- [createTaxRegionsStep](https://docs.medusajs.com/references/medusa-workflows/steps/createTaxRegionsStep/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) +- [listTaxRateIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateIdsStep/index.html.md) +- [getItemTaxLinesStep](https://docs.medusajs.com/references/medusa-workflows/steps/getItemTaxLinesStep/index.html.md) +- [listTaxRateRuleIdsStep](https://docs.medusajs.com/references/medusa-workflows/steps/listTaxRateRuleIdsStep/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) +- [createUsersStep](https://docs.medusajs.com/references/medusa-workflows/steps/createUsersStep/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) # Medusa CLI Reference @@ -26496,6 +26085,84 @@ npx medusa --help *** +# 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. + + +# 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\`| + + # db Commands - Medusa CLI Reference Commands starting with `db:` perform actions on the database. @@ -26616,87 +26283,70 @@ 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 - -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 -``` +# plugin Commands - Medusa CLI Reference -## Options +Commands starting with `plugin:` perform actions related to [plugin](https://docs.medusajs.com/docs/learn/fundamentals/plugins/index.html.md) development. -|Option|Description|Default| -|---|---|---|---|---| -|\`-H \\`|Set host of the Medusa server.|\`localhost\`| -|\`-p \\`|Set port of the Medusa server.|\`9000\`| +These commands are available starting from [Medusa v2.3.0](https://github.com/medusajs/medusa/releases/tag/v2.3.0). +## plugin:publish -# build Command - Medusa CLI Reference +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. -Create a standalone build of the Medusa application. +```bash +npx medusa plugin:publish +``` -This creates a build that: +*** -- Doesn't rely on the source TypeScript files. -- Can be copied to a production server reliably. +## plugin:add -The build is outputted to a new `.medusa/server` directory. +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 build +npx medusa plugin:add [names...] ``` -Refer to [this section](#run-built-medusa-application) for next steps. - -## Options +### Arguments -|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 | +|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| *** -## Run Built Medusa Application - -After running the `build` command, use the following step to run the built Medusa application: +## plugin:develop -- Change to the `.medusa/server` directory and install the dependencies: +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 npm2yarn -cd .medusa/server && npm install +```bash +npx medusa plugin:develop ``` -- 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 -``` +## plugin:db:generate -- In the system environment variables, set `NODE_ENV` to `production`: +Generate migrations for all modules in a plugin. ```bash -NODE_ENV=production -``` - -- Use the `start` command to run the application: - -```bash npm2yarn -cd .medusa/server && npm run start +npx medusa plugin:db:generate ``` *** -## Build Medusa Admin +## plugin:build -By default, the Medusa Admin is built to the `.medusa/server/public/admin` directory. +Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` 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. +```bash +npx medusa plugin:build +``` # exec Command - Medusa CLI Reference -Run a custom CLI script. Learn more about it in [this guide](undefined/docs/learn/fundamentals/custom-cli-scripts). +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 exec [file] [args...] @@ -26710,33 +26360,20 @@ npx medusa exec [file] [args...] |\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| -# new Command - Medusa CLI Reference +# start 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. +Start the Medusa application in production. ```bash -medusa new [ []] +npx medusa start ``` -## Arguments - -|Argument|Description|Required|Default| -|---|---|---|---|---|---|---| -|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-| -|\`starter\_url\`|The name of the directory to create the Medusa application in.|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.| +|Option|Description|Default| +|---|---|---|---|---| +|\`-H \\`|Set host of the Medusa server.|\`localhost\`| +|\`-p \\`|Set port of the Medusa server.|\`9000\`| # start-cluster Command - Medusa CLI Reference @@ -26758,81 +26395,33 @@ npx medusa start-cluster |\`-p \\`|Set port of the Medusa server.|\`9000\`| -# 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\`| - - -# plugin Commands - Medusa CLI Reference - -Commands starting with `plugin:` perform actions related to [plugin](undefined/docs/learn/fundamentals/plugins) 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 +# new Command - Medusa CLI Reference -Generate migrations for all modules in a plugin. +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 -npx medusa plugin:db:generate +medusa new [ []] ``` -*** +## Arguments -## plugin:build +|Argument|Description|Required|Default| +|---|---|---|---|---|---|---| +|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-| +|\`starter\_url\`|The name of the directory to create the Medusa application in.|No|\`https://github.com/medusajs/medusa-starter-default\`| -Build a plugin before publishing it to NPM. The command will compile an output in the `.medusa/server` directory. +## Options -```bash -npx medusa plugin:build -``` +|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.| # telemetry Command - Medusa CLI Reference @@ -26955,6 +26544,35 @@ By default, the Medusa Admin is built to the `.medusa/server/public/admin` direc 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 name of the directory to create the Medusa application in.|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.| + + # db Commands - Medusa CLI Reference Commands starting with `db:` perform actions on the database. @@ -27075,25 +26693,9 @@ 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 - -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\`| - - # exec Command - Medusa CLI Reference -Run a custom CLI script. Learn more about it in [this guide](undefined/docs/learn/fundamentals/custom-cli-scripts). +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 exec [file] [args...] @@ -27107,38 +26709,25 @@ npx medusa exec [file] [args...] |\`args\`|A list of arguments to pass to the function. These arguments are passed in the |No| -# new Command - Medusa CLI Reference +# develop 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. +Start Medusa application in development. This command watches files for any changes, then rebuilds the files and restarts the Medusa application. ```bash -medusa new [ []] +npx medusa develop ``` -## Arguments - -|Argument|Description|Required|Default| -|---|---|---|---|---|---|---| -|\`dir\_name\`|The name of the directory to create the Medusa application in.|Yes|-| -|\`starter\_url\`|The name of the directory to create the Medusa application in.|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.| +|Option|Description|Default| +|---|---|---|---|---| +|\`-H \\`|Set host of the Medusa server.|\`localhost\`| +|\`-p \\`|Set port of the Medusa server.|\`9000\`| # plugin Commands - Medusa CLI Reference -Commands starting with `plugin:` perform actions related to [plugin](undefined/docs/learn/fundamentals/plugins) development. +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). @@ -27332,7 +26921,7 @@ export const sdk = new Medusa({ }) ``` -In the Medusa Admin you use `import.meta.env` to access environment variables becaues the Medusa Admin is built on top of [Vite](https://vitejs.dev/). Learn more in [this documentation](undefined/docs/learn/fundamentals/admin/environment-variables). +In the Medusa Admin you use `import.meta.env` to access environment variables becaues the Medusa Admin is built on top of [Vite](https://vitejs.dev/). Learn more in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/admin/environment-variables/index.html.md). ### JS SDK Configurations @@ -27524,319 +27113,319 @@ Learn more in the [Next.js documentation](https://nextjs.org/docs/app/building-y ## JS SDK Admin -- [addInboundItems](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundItems/index.html.md) -- [addInboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundShipping/index.html.md) -- [addItems](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addItems/index.html.md) -- [addOutboundItems](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundItems/index.html.md) -- [addOutboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundShipping/index.html.md) -- [cancel](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancel/index.html.md) -- [cancelRequest](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancelRequest/index.html.md) -- [create](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.create/index.html.md) -- [deleteInboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteInboundShipping/index.html.md) -- [deleteOutboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteOutboundShipping/index.html.md) -- [list](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.list/index.html.md) -- [removeInboundItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeInboundItem/index.html.md) -- [removeItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeItem/index.html.md) -- [request](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.request/index.html.md) -- [removeOutboundItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeOutboundItem/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.retrieve/index.html.md) -- [updateInboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundShipping/index.html.md) -- [updateInboundItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundItem/index.html.md) -- [updateItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateItem/index.html.md) -- [updateOutboundShipping](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundShipping/index.html.md) -- [updateOutboundItem](undefined/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundItem/index.html.md) -- [batchSalesChannels](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.batchSalesChannels/index.html.md) -- [create](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.retrieve/index.html.md) -- [list](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.list/index.html.md) -- [revoke](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.revoke/index.html.md) -- [update](undefined/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.update/index.html.md) -- [batchPromotions](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.batchPromotions/index.html.md) -- [create](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.update/index.html.md) -- [list](undefined/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.list/index.html.md) -- [list](undefined/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.retrieve/index.html.md) -- [clearToken](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken/index.html.md) -- [fetch](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetch/index.html.md) -- [clearToken\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken_/index.html.md) -- [fetchStream](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetchStream/index.html.md) -- [getApiKeyHeader\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getApiKeyHeader_/index.html.md) -- [getJwtHeader\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getJwtHeader_/index.html.md) -- [getPublishableKeyHeader\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getPublishableKeyHeader_/index.html.md) -- [getTokenStorageInfo\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getTokenStorageInfo_/index.html.md) -- [getToken\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getToken_/index.html.md) -- [initClient](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.initClient/index.html.md) -- [setToken](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken/index.html.md) -- [setToken\_](undefined/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken_/index.html.md) -- [batchCustomerGroups](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.batchCustomerGroups/index.html.md) -- [create](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.update/index.html.md) -- [batchCustomers](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.batchCustomers/index.html.md) -- [create](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.update/index.html.md) -- [addInboundItems](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundItems/index.html.md) -- [addInboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundShipping/index.html.md) -- [addOutboundItems](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundItems/index.html.md) -- [cancel](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancel/index.html.md) -- [addOutboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundShipping/index.html.md) -- [deleteInboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteInboundShipping/index.html.md) -- [create](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.create/index.html.md) -- [cancelRequest](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancelRequest/index.html.md) -- [deleteOutboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteOutboundShipping/index.html.md) -- [removeInboundItem](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeInboundItem/index.html.md) -- [removeOutboundItem](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeOutboundItem/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.retrieve/index.html.md) -- [request](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.request/index.html.md) -- [updateInboundItem](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundItem/index.html.md) -- [updateOutboundItem](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundItem/index.html.md) -- [updateInboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundShipping/index.html.md) -- [updateOutboundShipping](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundShipping/index.html.md) -- [cancel](undefined/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.cancel/index.html.md) -- [create](undefined/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.create/index.html.md) -- [createShipment](undefined/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.createShipment/index.html.md) -- [createServiceZone](undefined/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.createServiceZone/index.html.md) -- [delete](undefined/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.delete/index.html.md) -- [retrieveServiceZone](undefined/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.retrieveServiceZone/index.html.md) -- [deleteServiceZone](undefined/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.deleteServiceZone/index.html.md) -- [list](undefined/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.list/index.html.md) -- [updateServiceZone](undefined/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.updateServiceZone/index.html.md) -- [listFulfillmentOptions](undefined/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.listFulfillmentOptions/index.html.md) -- [batchInventoryItemLocationLevels](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemLocationLevels/index.html.md) -- [batchInventoryItemsLocationLevels](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemsLocationLevels/index.html.md) -- [batchUpdateLevels](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchUpdateLevels/index.html.md) -- [create](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.delete/index.html.md) -- [deleteLevel](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.deleteLevel/index.html.md) -- [list](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.list/index.html.md) -- [listLevels](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.listLevels/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.update/index.html.md) -- [updateLevel](undefined/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.updateLevel/index.html.md) -- [accept](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.accept/index.html.md) -- [create](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.create/index.html.md) -- [resend](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.resend/index.html.md) -- [list](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.retrieve/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.retrieve/index.html.md) -- [capture](undefined/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.capture/index.html.md) -- [list](undefined/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.list/index.html.md) -- [listPaymentProviders](undefined/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.listPaymentProviders/index.html.md) -- [refund](undefined/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.refund/index.html.md) -- [cancel](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancel/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.retrieve/index.html.md) -- [createFulfillment](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createFulfillment/index.html.md) -- [cancelTransfer](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelTransfer/index.html.md) -- [cancelFulfillment](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelFulfillment/index.html.md) -- [createShipment](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createShipment/index.html.md) -- [list](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.list/index.html.md) -- [listChanges](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listChanges/index.html.md) -- [listLineItems](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listLineItems/index.html.md) -- [markAsDelivered](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.markAsDelivered/index.html.md) -- [requestTransfer](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.requestTransfer/index.html.md) -- [retrievePreview](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrievePreview/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.update/index.html.md) -- [addItems](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.addItems/index.html.md) -- [confirm](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.confirm/index.html.md) -- [cancelRequest](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.cancelRequest/index.html.md) -- [initiateRequest](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.initiateRequest/index.html.md) -- [request](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.request/index.html.md) -- [removeAddedItem](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.removeAddedItem/index.html.md) -- [updateOriginalItem](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateOriginalItem/index.html.md) -- [updateAddedItem](undefined/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateAddedItem/index.html.md) -- [delete](undefined/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.delete/index.html.md) -- [create](undefined/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.create/index.html.md) -- [markAsPaid](undefined/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.markAsPaid/index.html.md) -- [create](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.create/index.html.md) -- [batchPrices](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.batchPrices/index.html.md) -- [delete](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.retrieve/index.html.md) -- [list](undefined/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.list/index.html.md) -- [update](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.update/index.html.md) -- [create](undefined/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.retrieve/index.html.md) -- [list](undefined/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.list/index.html.md) -- [update](undefined/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.update/index.html.md) -- [create](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.update/index.html.md) -- [updateProducts](undefined/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.updateProducts/index.html.md) -- [batch](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batch/index.html.md) -- [batchVariantInventoryItems](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariantInventoryItems/index.html.md) -- [batchVariants](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariants/index.html.md) -- [confirmImport](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.confirmImport/index.html.md) -- [create](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.create/index.html.md) -- [createVariant](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createVariant/index.html.md) -- [createOption](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createOption/index.html.md) -- [delete](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.delete/index.html.md) -- [deleteVariant](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteVariant/index.html.md) -- [deleteOption](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteOption/index.html.md) -- [export](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.export/index.html.md) -- [list](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.list/index.html.md) -- [import](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.import/index.html.md) -- [listOptions](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listOptions/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieve/index.html.md) -- [listVariants](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listVariants/index.html.md) -- [retrieveOption](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveOption/index.html.md) -- [retrieveVariant](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveVariant/index.html.md) -- [update](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.update/index.html.md) -- [create](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.create/index.html.md) -- [updateVariant](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateVariant/index.html.md) -- [updateOption](undefined/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateOption/index.html.md) -- [list](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.update/index.html.md) -- [updateProducts](undefined/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.updateProducts/index.html.md) -- [create](undefined/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.update/index.html.md) -- [list](undefined/references/js_sdk/admin/ProductVariant/methods/js_sdk.admin.ProductVariant.list/index.html.md) -- [create](undefined/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.create/index.html.md) -- [list](undefined/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.retrieve/index.html.md) -- [linkProducts](undefined/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.linkProducts/index.html.md) -- [addRules](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.addRules/index.html.md) -- [list](undefined/references/js_sdk/admin/RefundReason/methods/js_sdk.admin.RefundReason.list/index.html.md) -- [create](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.list/index.html.md) -- [listRuleAttributes](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleAttributes/index.html.md) -- [listRuleValues](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleValues/index.html.md) -- [listRules](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRules/index.html.md) -- [removeRules](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.removeRules/index.html.md) -- [update](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.update/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.retrieve/index.html.md) -- [updateRules](undefined/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.updateRules/index.html.md) -- [list](undefined/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.delete/index.html.md) -- [create](undefined/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.create/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.update/index.html.md) -- [delete](undefined/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.delete/index.html.md) -- [create](undefined/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.create/index.html.md) -- [list](undefined/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.update/index.html.md) -- [addReturnItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnItem/index.html.md) -- [addReturnShipping](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnShipping/index.html.md) -- [cancel](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancel/index.html.md) -- [cancelReceive](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelReceive/index.html.md) -- [cancelRequest](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelRequest/index.html.md) -- [confirmReceive](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmReceive/index.html.md) -- [confirmRequest](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmRequest/index.html.md) -- [deleteReturnShipping](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.deleteReturnShipping/index.html.md) -- [dismissItems](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.dismissItems/index.html.md) -- [initiateReceive](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateReceive/index.html.md) -- [initiateRequest](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateRequest/index.html.md) -- [list](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.list/index.html.md) -- [receiveItems](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.receiveItems/index.html.md) -- [removeDismissItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeDismissItem/index.html.md) -- [removeReceiveItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReceiveItem/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.retrieve/index.html.md) -- [removeReturnItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReturnItem/index.html.md) -- [updateDismissItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateDismissItem/index.html.md) -- [updateRequest](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateRequest/index.html.md) -- [updateReceiveItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReceiveItem/index.html.md) -- [updateReturnShipping](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnShipping/index.html.md) -- [updateReturnItem](undefined/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnItem/index.html.md) -- [batchProducts](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.batchProducts/index.html.md) -- [create](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.update/index.html.md) -- [updateProducts](undefined/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.updateProducts/index.html.md) -- [create](undefined/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.update/index.html.md) -- [create](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.create/index.html.md) -- [list](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.update/index.html.md) -- [updateRules](undefined/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.updateRules/index.html.md) -- [create](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.create/index.html.md) -- [createFulfillmentSet](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.createFulfillmentSet/index.html.md) -- [delete](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.delete/index.html.md) -- [list](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.list/index.html.md) -- [update](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.update/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.retrieve/index.html.md) -- [updateFulfillmentProviders](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateFulfillmentProviders/index.html.md) -- [updateSalesChannels](undefined/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateSalesChannels/index.html.md) -- [list](undefined/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.update/index.html.md) -- [delete](undefined/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.delete/index.html.md) -- [create](undefined/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.create/index.html.md) -- [list](undefined/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.retrieve/index.html.md) -- [update](undefined/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.update/index.html.md) -- [delete](undefined/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.delete/index.html.md) -- [update](undefined/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.update/index.html.md) -- [list](undefined/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.retrieve/index.html.md) -- [list](undefined/references/js_sdk/admin/User/methods/js_sdk.admin.User.list/index.html.md) -- [delete](undefined/references/js_sdk/admin/User/methods/js_sdk.admin.User.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/User/methods/js_sdk.admin.User.retrieve/index.html.md) -- [me](undefined/references/js_sdk/admin/User/methods/js_sdk.admin.User.me/index.html.md) -- [update](undefined/references/js_sdk/admin/User/methods/js_sdk.admin.User.update/index.html.md) -- [create](undefined/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.create/index.html.md) -- [delete](undefined/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.delete/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.retrieve/index.html.md) -- [list](undefined/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.retrieve/index.html.md) -- [create](undefined/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.create/index.html.md) -- [create](undefined/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.create/index.html.md) -- [list](undefined/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.list/index.html.md) -- [retrieve](undefined/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.retrieve/index.html.md) -- [delete](undefined/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.delete/index.html.md) -- [update](undefined/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.update/index.html.md) +- [batchSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.batchSalesChannels/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.list/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.retrieve/index.html.md) +- [revoke](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.revoke/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ApiKey/methods/js_sdk.admin.ApiKey.update/index.html.md) +- [batchPromotions](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.batchPromotions/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Campaign/methods/js_sdk.admin.Campaign.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Currency/methods/js_sdk.admin.Currency.retrieve/index.html.md) +- [addInboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundItems/index.html.md) +- [addInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addInboundShipping/index.html.md) +- [addOutboundItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundItems/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancel/index.html.md) +- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addOutboundShipping/index.html.md) +- [addItems](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.addItems/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.cancelRequest/index.html.md) +- [deleteInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteInboundShipping/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.create/index.html.md) +- [deleteOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.deleteOutboundShipping/index.html.md) +- [removeItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeItem/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.list/index.html.md) +- [removeInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeInboundItem/index.html.md) +- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.removeOutboundItem/index.html.md) +- [request](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.request/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.retrieve/index.html.md) +- [updateInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundItem/index.html.md) +- [updateInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateInboundShipping/index.html.md) +- [updateItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateItem/index.html.md) +- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundShipping/index.html.md) +- [updateOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Claim/methods/js_sdk.admin.Claim.updateOutboundItem/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.create/index.html.md) +- [batchCustomerGroups](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.batchCustomerGroups/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Customer/methods/js_sdk.admin.Customer.list/index.html.md) +- [batchCustomers](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.batchCustomers/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/CustomerGroup/methods/js_sdk.admin.CustomerGroup.update/index.html.md) +- [addOutboundItems](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundItems/index.html.md) +- [addInboundItems](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundItems/index.html.md) +- [addInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addInboundShipping/index.html.md) +- [addOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.addOutboundShipping/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancel/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.cancelRequest/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.create/index.html.md) +- [deleteInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteInboundShipping/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.list/index.html.md) +- [deleteOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.deleteOutboundShipping/index.html.md) +- [removeInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeInboundItem/index.html.md) +- [removeOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.removeOutboundItem/index.html.md) +- [updateInboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundItem/index.html.md) +- [request](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.request/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.retrieve/index.html.md) +- [updateInboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateInboundShipping/index.html.md) +- [updateOutboundItem](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundItem/index.html.md) +- [updateOutboundShipping](https://docs.medusajs.com/references/js_sdk/admin/Exchange/methods/js_sdk.admin.Exchange.updateOutboundShipping/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.delete/index.html.md) +- [deleteServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.deleteServiceZone/index.html.md) +- [createServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.createServiceZone/index.html.md) +- [retrieveServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.retrieveServiceZone/index.html.md) +- [updateServiceZone](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentSet/methods/js_sdk.admin.FulfillmentSet.updateServiceZone/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.create/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.cancel/index.html.md) +- [createShipment](https://docs.medusajs.com/references/js_sdk/admin/Fulfillment/methods/js_sdk.admin.Fulfillment.createShipment/index.html.md) +- [listFulfillmentOptions](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.listFulfillmentOptions/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/FulfillmentProvider/methods/js_sdk.admin.FulfillmentProvider.list/index.html.md) +- [accept](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.accept/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.list/index.html.md) +- [resend](https://docs.medusajs.com/references/js_sdk/admin/Invite/methods/js_sdk.admin.Invite.resend/index.html.md) +- [batchInventoryItemLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemLocationLevels/index.html.md) +- [batchInventoryItemsLocationLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchInventoryItemsLocationLevels/index.html.md) +- [batchUpdateLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.batchUpdateLevels/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.delete/index.html.md) +- [listLevels](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.listLevels/index.html.md) +- [deleteLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.deleteLevel/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.update/index.html.md) +- [updateLevel](https://docs.medusajs.com/references/js_sdk/admin/InventoryItem/methods/js_sdk.admin.InventoryItem.updateLevel/index.html.md) +- [fetch](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetch/index.html.md) +- [clearToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken_/index.html.md) +- [clearToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.clearToken/index.html.md) +- [fetchStream](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.fetchStream/index.html.md) +- [getJwtHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getJwtHeader_/index.html.md) +- [getPublishableKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getPublishableKeyHeader_/index.html.md) +- [getApiKeyHeader\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getApiKeyHeader_/index.html.md) +- [getTokenStorageInfo\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getTokenStorageInfo_/index.html.md) +- [getToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.getToken_/index.html.md) +- [setToken](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken/index.html.md) +- [initClient](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.initClient/index.html.md) +- [setToken\_](https://docs.medusajs.com/references/js_sdk/admin/Client/methods/js_sdk.admin.Client.setToken_/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Notification/methods/js_sdk.admin.Notification.list/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancel/index.html.md) +- [cancelTransfer](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelTransfer/index.html.md) +- [cancelFulfillment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.cancelFulfillment/index.html.md) +- [createFulfillment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createFulfillment/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.list/index.html.md) +- [listChanges](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listChanges/index.html.md) +- [createShipment](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.createShipment/index.html.md) +- [listLineItems](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.listLineItems/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrieve/index.html.md) +- [requestTransfer](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.requestTransfer/index.html.md) +- [markAsDelivered](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.markAsDelivered/index.html.md) +- [retrievePreview](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.retrievePreview/index.html.md) +- [addItems](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.addItems/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.cancelRequest/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Order/methods/js_sdk.admin.Order.update/index.html.md) +- [confirm](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.confirm/index.html.md) +- [removeAddedItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.removeAddedItem/index.html.md) +- [initiateRequest](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.initiateRequest/index.html.md) +- [request](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.request/index.html.md) +- [updateAddedItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateAddedItem/index.html.md) +- [updateOriginalItem](https://docs.medusajs.com/references/js_sdk/admin/OrderEdit/methods/js_sdk.admin.OrderEdit.updateOriginalItem/index.html.md) +- [capture](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.capture/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.retrieve/index.html.md) +- [listPaymentProviders](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.listPaymentProviders/index.html.md) +- [refund](https://docs.medusajs.com/references/js_sdk/admin/Payment/methods/js_sdk.admin.Payment.refund/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.create/index.html.md) +- [batch](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batch/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.delete/index.html.md) +- [markAsPaid](https://docs.medusajs.com/references/js_sdk/admin/PaymentCollection/methods/js_sdk.admin.PaymentCollection.markAsPaid/index.html.md) +- [batchVariantInventoryItems](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariantInventoryItems/index.html.md) +- [batchVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.batchVariants/index.html.md) +- [createOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createOption/index.html.md) +- [confirmImport](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.confirmImport/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.create/index.html.md) +- [createVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.createVariant/index.html.md) +- [deleteVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteVariant/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.delete/index.html.md) +- [deleteOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.deleteOption/index.html.md) +- [export](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.export/index.html.md) +- [listOptions](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listOptions/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.list/index.html.md) +- [import](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.import/index.html.md) +- [listVariants](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.listVariants/index.html.md) +- [retrieveVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveVariant/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieve/index.html.md) +- [retrieveOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.retrieveOption/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.update/index.html.md) +- [updateOption](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateOption/index.html.md) +- [updateVariant](https://docs.medusajs.com/references/js_sdk/admin/Product/methods/js_sdk.admin.Product.updateVariant/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/PricePreference/methods/js_sdk.admin.PricePreference.update/index.html.md) +- [batchPrices](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.batchPrices/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.delete/index.html.md) +- [linkProducts](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.linkProducts/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/PriceList/methods/js_sdk.admin.PriceList.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.retrieve/index.html.md) +- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.updateProducts/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductCategory/methods/js_sdk.admin.ProductCategory.update/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductTag/methods/js_sdk.admin.ProductTag.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.update/index.html.md) +- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/ProductCollection/methods/js_sdk.admin.ProductCollection.updateProducts/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductType/methods/js_sdk.admin.ProductType.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.list/index.html.md) +- [listRuleAttributes](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleAttributes/index.html.md) +- [listRuleValues](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRuleValues/index.html.md) +- [listRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.listRules/index.html.md) +- [addRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.addRules/index.html.md) +- [removeRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.removeRules/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/RefundReason/methods/js_sdk.admin.RefundReason.list/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ProductVariant/methods/js_sdk.admin.ProductVariant.list/index.html.md) +- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/Promotion/methods/js_sdk.admin.Promotion.updateRules/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Reservation/methods/js_sdk.admin.Reservation.update/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.retrieve/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Region/methods/js_sdk.admin.Region.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.create/index.html.md) +- [batchProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.batchProducts/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ReturnReason/methods/js_sdk.admin.ReturnReason.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.update/index.html.md) +- [updateProducts](https://docs.medusajs.com/references/js_sdk/admin/SalesChannel/methods/js_sdk.admin.SalesChannel.updateProducts/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.create/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.retrieve/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.delete/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.update/index.html.md) +- [updateRules](https://docs.medusajs.com/references/js_sdk/admin/ShippingOption/methods/js_sdk.admin.ShippingOption.updateRules/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/ShippingProfile/methods/js_sdk.admin.ShippingProfile.update/index.html.md) +- [addReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnItem/index.html.md) +- [addReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.addReturnShipping/index.html.md) +- [cancel](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancel/index.html.md) +- [cancelReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelReceive/index.html.md) +- [cancelRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.cancelRequest/index.html.md) +- [confirmReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmReceive/index.html.md) +- [confirmRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.confirmRequest/index.html.md) +- [dismissItems](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.dismissItems/index.html.md) +- [deleteReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.deleteReturnShipping/index.html.md) +- [initiateReceive](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateReceive/index.html.md) +- [receiveItems](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.receiveItems/index.html.md) +- [initiateRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.initiateRequest/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.list/index.html.md) +- [removeDismissItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeDismissItem/index.html.md) +- [removeReceiveItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReceiveItem/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.retrieve/index.html.md) +- [removeReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.removeReturnItem/index.html.md) +- [updateDismissItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateDismissItem/index.html.md) +- [updateReturnItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnItem/index.html.md) +- [updateRequest](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateRequest/index.html.md) +- [updateReceiveItem](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReceiveItem/index.html.md) +- [updateReturnShipping](https://docs.medusajs.com/references/js_sdk/admin/Return/methods/js_sdk.admin.Return.updateReturnShipping/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.delete/index.html.md) +- [createFulfillmentSet](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.createFulfillmentSet/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.update/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.list/index.html.md) +- [updateFulfillmentProviders](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateFulfillmentProviders/index.html.md) +- [updateSalesChannels](https://docs.medusajs.com/references/js_sdk/admin/StockLocation/methods/js_sdk.admin.StockLocation.updateSalesChannels/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.list/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Store/methods/js_sdk.admin.Store.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.retrieve/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/TaxRate/methods/js_sdk.admin.TaxRate.update/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.delete/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.create/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/Upload/methods/js_sdk.admin.Upload.retrieve/index.html.md) +- [create](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.create/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.delete/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.list/index.html.md) +- [delete](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.delete/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/TaxRegion/methods/js_sdk.admin.TaxRegion.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.list/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.retrieve/index.html.md) +- [me](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.me/index.html.md) +- [update](https://docs.medusajs.com/references/js_sdk/admin/User/methods/js_sdk.admin.User.update/index.html.md) +- [retrieve](https://docs.medusajs.com/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.retrieve/index.html.md) +- [list](https://docs.medusajs.com/references/js_sdk/admin/WorkflowExecution/methods/js_sdk.admin.WorkflowExecution.list/index.html.md) ## JS SDK Auth -- [callback](undefined/references/js-sdk/auth/callback/index.html.md) -- [login](undefined/references/js-sdk/auth/login/index.html.md) -- [logout](undefined/references/js-sdk/auth/logout/index.html.md) -- [refresh](undefined/references/js-sdk/auth/refresh/index.html.md) -- [register](undefined/references/js-sdk/auth/register/index.html.md) -- [resetPassword](undefined/references/js-sdk/auth/resetPassword/index.html.md) -- [updateProvider](undefined/references/js-sdk/auth/updateProvider/index.html.md) +- [callback](https://docs.medusajs.com/references/js-sdk/auth/callback/index.html.md) +- [logout](https://docs.medusajs.com/references/js-sdk/auth/logout/index.html.md) +- [login](https://docs.medusajs.com/references/js-sdk/auth/login/index.html.md) +- [register](https://docs.medusajs.com/references/js-sdk/auth/register/index.html.md) +- [refresh](https://docs.medusajs.com/references/js-sdk/auth/refresh/index.html.md) +- [resetPassword](https://docs.medusajs.com/references/js-sdk/auth/resetPassword/index.html.md) +- [updateProvider](https://docs.medusajs.com/references/js-sdk/auth/updateProvider/index.html.md) ## JS SDK Store -- [cart](undefined/references/js-sdk/store/cart/index.html.md) -- [category](undefined/references/js-sdk/store/category/index.html.md) -- [customer](undefined/references/js-sdk/store/customer/index.html.md) -- [collection](undefined/references/js-sdk/store/collection/index.html.md) -- [fulfillment](undefined/references/js-sdk/store/fulfillment/index.html.md) -- [order](undefined/references/js-sdk/store/order/index.html.md) -- [payment](undefined/references/js-sdk/store/payment/index.html.md) -- [product](undefined/references/js-sdk/store/product/index.html.md) -- [region](undefined/references/js-sdk/store/region/index.html.md) +- [cart](https://docs.medusajs.com/references/js-sdk/store/cart/index.html.md) +- [category](https://docs.medusajs.com/references/js-sdk/store/category/index.html.md) +- [collection](https://docs.medusajs.com/references/js-sdk/store/collection/index.html.md) +- [fulfillment](https://docs.medusajs.com/references/js-sdk/store/fulfillment/index.html.md) +- [payment](https://docs.medusajs.com/references/js-sdk/store/payment/index.html.md) +- [order](https://docs.medusajs.com/references/js-sdk/store/order/index.html.md) +- [customer](https://docs.medusajs.com/references/js-sdk/store/customer/index.html.md) +- [product](https://docs.medusajs.com/references/js-sdk/store/product/index.html.md) +- [region](https://docs.medusajs.com/references/js-sdk/store/region/index.html.md) # Configure Medusa Backend @@ -27849,11 +27438,11 @@ The configurations for your Medusa application are in `medusa-config.ts` located `defineConfig` accepts as a parameter an object with the following properties: -- [projectConfig](undefined/references/medusa-config#projectconfig) (required): An object that holds general configurations related to the Medusa application, such as database or CORS configurations. -- [plugins](undefined/references/medusa-config#plugins): An array of strings or objects that hold the configurations of the plugins installed in the Medusa application. -- [admin](undefined/references/medusa-config#admin): An object that holds admin-related configurations. -- [modules](undefined/references/medusa-config#modules): An object that configures the Medusa application's modules. -- [featureFlags](undefined/references/medusa-config#featureflags): An object that enables or disables features guarded by a feature flag. +- [projectConfig](https://docs.medusajs.com/references/medusa-config#projectconfig/index.html.md) (required): An object that holds general configurations related to the Medusa application, such as database or CORS configurations. +- [plugins](https://docs.medusajs.com/references/medusa-config#plugins/index.html.md): An array of strings or objects that hold the configurations of the plugins installed in the Medusa application. +- [admin](https://docs.medusajs.com/references/medusa-config#admin/index.html.md): An object that holds admin-related configurations. +- [modules](https://docs.medusajs.com/references/medusa-config#modules/index.html.md): An object that configures the Medusa application's modules. +- [featureFlags](https://docs.medusajs.com/references/medusa-config#featureflags/index.html.md): An object that enables or disables features guarded by a feature flag. For example: @@ -28464,9 +28053,9 @@ After enabling a feature flag, make sure to run migrations as it may require mak In this section, you'll find examples of implementing common Medusa Admin components and layouts. -These components are useful to follow the same design conventions as the Medusa Admin, and are build on top of the [Medusa UI package](undefined/ui). +These components are useful to follow the same design conventions as the Medusa Admin, and are build on top of the [Medusa UI package](https://docs.medusajs.com/ui/index.html.md). -Refer to the [Medusa UI documentation](undefined/ui) for a full list of components. +Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/index.html.md) for a full list of components. ## Layouts @@ -28536,7 +28125,7 @@ export const config = defineRouteConfig({ export default CustomPage ``` -This UI route also uses a [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and a [Header]() custom components. +This UI route also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and a [Header]() custom components. # Two Column Layout - Admin Components @@ -28615,66 +28204,7 @@ export const config = defineRouteConfig({ export default CustomPage ``` -This UI route also uses [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and [Header]() custom components. - - -# Container - Admin Components - -The Medusa Admin wraps each section of a page in a container. - -![Example of a container in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728287102/Medusa%20Resources/container_soenir.png) - -To create a component that uses the same container styling in your widgets or UI routes, create the file `src/admin/components/container.tsx` with the following content: - -```tsx -import { - Container as UiContainer, - clx, -} from "@medusajs/ui" - -type ContainerProps = React.ComponentProps - -export const Container = (props: ContainerProps) => { - return ( - - ) -} -``` - -The `Container` component re-uses the component from the [Medusa UI package](undefined/ui/components/container) and applies to it classes to match the Medusa Admin's design conventions. - -*** - -## Example - -Use that `Container` component in any widget or UI route. - -For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: - -```tsx title="src/admin/widgets/product-widget.tsx" -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container } from "../components/container" -import { Header } from "../components/header" - -const ProductWidget = () => { - return ( - -
- - ) -} - -export const config = defineWidgetConfig({ - zone: "product.details.before", -}) - -export default ProductWidget -``` - -This widget also uses a [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom component. +This UI route also uses [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header]() custom components. # Action Menu - Admin Components @@ -28788,7 +28318,7 @@ export const ActionMenu = ({ groups }: ActionMenuProps) => { } ``` -The `ActionMenu` component shows a three-dots icon (or `EllipsisHorizontal`) from the [Medusa Icons package](undefined/ui/icons/overview) in a button. +The `ActionMenu` component shows a three-dots icon (or `EllipsisHorizontal`) from the [Medusa Icons package](https://docs.medusajs.com/ui/icons/overview/index.html.md) in a button. When the button is clicked, a dropdown menu is shown with the actions passed in the props. @@ -28849,11 +28379,11 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -This widget also uses a [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) custom component. +This widget also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) custom component. ### Use in Header -You can also use the action menu in the [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) component as part of its actions. +You can also use the action menu in the [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) component as part of its actions. For example: @@ -28906,15 +28436,15 @@ export default ProductWidget This component is available after [Medusa v2.4.0+](https://github.com/medusajs/medusa/releases/tag/v2.4.0). -The [DataTable component in Medusa UI](undefined/ui/components/data-table) allows you to display data in a table with sorting, filtering, and pagination. +The [DataTable component in Medusa UI](https://docs.medusajs.com/ui/components/data-table/index.html.md) allows you to display data in a table with sorting, filtering, and pagination. You can use this component in your Admin Extensions to display data in a table format, especially if they're retrieved from API routes of the Medusa application. -Refer to the [Medusa UI documentation](undefined/ui/components/data-table) for detailed information about the DataTable component and its different usages. +Refer to the [Medusa UI documentation](https://docs.medusajs.com/ui/components/data-table/index.html.md) for detailed information about the DataTable component and its different usages. ## Example: DataTable with Data Fetching -In this example, you'll create a UI widget that shows the list of products retrieved from the [List Products API Route](undefined/api/admin#products_getproducts) in a data table with pagination, filtering, searching, and sorting. +In this example, you'll create a UI widget that shows the list of products retrieved from the [List Products API Route](https://docs.medusajs.com/api/admin#products_getproducts) in a data table with pagination, filtering, searching, and sorting. Start by initializing the columns in the data table. To do that, use the `createDataTableColumnHelper` from Medusa UI: @@ -29058,7 +28588,7 @@ You've also added two memoized variables: - `offset`: How many items to skip when fetching data based on the current page. - `statusFilters`: The selected status filters, if any. -Next, you'll fetch the products from the Medusa application. Assuming you have the JS SDK configured as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/js-sdk), add the following imports at the top of the file: +Next, you'll fetch the products from the Medusa application. Assuming you have the JS SDK configured as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk/index.html.md), add the following imports at the top of the file: ```tsx title="src/admin/routes/custom/page.tsx" import { sdk } from "../../lib/config" @@ -29167,7 +28697,7 @@ import { SingleColumnLayout } from "../../layouts/single-column" import { Container } from "../../components/container" ``` -Aside from the `DataTable` component, you also import the [SingleColumnLayout](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/layouts/single-column) and [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) components implemented in other Admin Component guides. These components ensure a style consistent to other pages in the admin dashboard. +Aside from the `DataTable` component, you also import the [SingleColumnLayout](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/layouts/single-column/index.html.md) and [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) components implemented in other Admin Component guides. These components ensure a style consistent to other pages in the admin dashboard. Then, replace the `TODO` in the component with the following: @@ -29371,8 +28901,8 @@ export default CustomPage The Medusa Admin has two types of forms: -1. Create forms, created using the [FocusModal UI component](undefined/ui/components/focus-modal). -2. Edit or update forms, created using the [Drawer UI component](undefined/ui/components/drawer). +1. Create forms, created using the [FocusModal UI component](https://docs.medusajs.com/ui/components/focus-modal/index.html.md). +2. Edit or update forms, created using the [Drawer UI component](https://docs.medusajs.com/ui/components/drawer/index.html.md). This guide explains how to create these two form types following the Medusa Admin's conventions. @@ -29534,7 +29064,7 @@ You create the `CreateForm` component. For now, it uses `useForm` from `react-ho You also define a `handleSubmit` function to perform an action when the form is submitted. -You can replace the content of the function with sending a request to Medusa's routes. Refer to [this guide](undefined/docs/learn/fundamentals/admin/tips#send-requests-to-api-routes) for more details on how to do that. +You can replace the content of the function with sending a request to Medusa's routes. Refer to [this guide](https://docs.medusajs.com/docs/learn/fundamentals/admin/tips#send-requests-to-api-routes/index.html.md) for more details on how to do that. ### Render Components @@ -29664,7 +29194,7 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -This component uses the [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom components. +This component uses the [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom components. It will add at the top of a product's details page a new section, and in its header you'll find a Create button. If you click on it, it will open the focus modal with your form. @@ -29811,7 +29341,7 @@ You create the `EditForm` component. For now, it uses `useForm` from `react-hook You also define a `handleSubmit` function to perform an action when the form is submitted. -You can replace the content of the function with sending a request to Medusa's routes. Refer to [this guide](undefined/docs/learn/fundamentals/admin/tips#send-requests-to-api-routes) for more details on how to do that. +You can replace the content of the function with sending a request to Medusa's routes. Refer to [this guide](https://docs.medusajs.com/docs/learn/fundamentals/admin/tips#send-requests-to-api-routes/index.html.md) for more details on how to do that. ### Render Components @@ -29935,116 +29465,134 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -This component uses the [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom components. +This component uses the [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom components. It will add at the top of a product's details page a new section, and in its header you'll find an "Edit Item" button. If you click on it, it will open the drawer with your form. -# Header - Admin Components +# Container - Admin Components -Each section in the Medusa Admin has a header with a title, and optionally a subtitle with buttons to perform an action. +The Medusa Admin wraps each section of a page in a container. -![Example of a header in a section](https://res.cloudinary.com/dza7lstvk/image/upload/v1728288562/Medusa%20Resources/header_dtz4gl.png) +![Example of a container in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728287102/Medusa%20Resources/container_soenir.png) -To create a component that uses the same header styling and structure, create the file `src/admin/components/header.tsx` with the following content: +To create a component that uses the same container styling in your widgets or UI routes, create the file `src/admin/components/container.tsx` with the following content: -```tsx title="src/admin/components/header.tsx" -import { Heading, Button, Text } from "@medusajs/ui" -import React from "react" -import { Link, LinkProps } from "react-router-dom" -import { ActionMenu, ActionMenuProps } from "./action-menu" +```tsx +import { + Container as UiContainer, + clx, +} from "@medusajs/ui" -export type HeadingProps = { - title: string - subtitle?: string - actions?: ( - { - type: "button", - props: React.ComponentProps - link?: LinkProps - } | - { - type: "action-menu" - props: ActionMenuProps - } | - { - type: "custom" - children: React.ReactNode - } - )[] +type ContainerProps = React.ComponentProps + +export const Container = (props: ContainerProps) => { + return ( + + ) } +``` -export const Header = ({ - title, - subtitle, - actions = [], -}: HeadingProps) => { +The `Container` component re-uses the component from the [Medusa UI package](https://docs.medusajs.com/ui/components/container/index.html.md) and applies to it classes to match the Medusa Admin's design conventions. + +*** + +## Example + +Use that `Container` component in any widget or UI route. + +For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: + +```tsx title="src/admin/widgets/product-widget.tsx" +import { defineWidgetConfig } from "@medusajs/admin-sdk" +import { Container } from "../components/container" +import { Header } from "../components/header" + +const ProductWidget = () => { return ( -
-
- {title} - {subtitle && ( - - {subtitle} - - )} -
- {actions.length > 0 && ( -
- {actions.map((action, index) => ( - <> - {action.type === "button" && ( - - )} - {action.type === "action-menu" && ( - - )} - {action.type === "custom" && action.children} - - ))} -
- )} -
+ +
+ ) } + +export const config = defineWidgetConfig({ + zone: "product.details.before", +}) + +export default ProductWidget ``` -The `Header` component shows a title, and optionally a subtitle and action buttons. +This widget also uses a [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom component. -The component also uses the [Action Menu](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/action-menu) custom component. -It accepts the following props: +# Section Row - Admin Components -- title: (\`string\`) The section's title. -- subtitle: (\`string\`) The section's subtitle. -- actions: (\`object\[]\`) An array of actions to show. +The Medusa Admin often shows information in rows of label-values, such as when showing a product's details. - - type: (\`button\` \\| \`action-menu\` \\| \`custom\`) The type of action to add. +![Example of a section row in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728292781/Medusa%20Resources/section-row_kknbnw.png) - \- If its value is \`button\`, it'll show a button that can have a link or an on-click action. +To create a component that shows information in the same structure, create the file `src/admin/components/section-row.tsx` with the following content: - \- If its value is \`action-menu\`, it'll show a three dot icon with a dropdown of actions. +```tsx title="src/admin/components/section-row.tsx" +import { Text, clx } from "@medusajs/ui" - \- If its value is \`custom\`, you can pass any React nodes to render. +export type SectionRowProps = { + title: string + value?: React.ReactNode | string | null + actions?: React.ReactNode +} - - props: (object) +export const SectionRow = ({ title, value, actions }: SectionRowProps) => { + const isValueString = typeof value === "string" || !value - - children: (React.ReactNode) This property is only accepted if \`type\` is \`custom\`. Its content is rendered as part of the actions. + return ( +
+ + {title} + + + {isValueString ? ( + + {value ?? "-"} + + ) : ( +
{value}
+ )} + + {actions &&
{actions}
} +
+ ) +} +``` + +The `SectionRow` component shows a title and a value in the same row. + +It accepts the following props: + +- title: (\`string\`) The title to show on the left side. +- value: (\`React.ReactNode\` \\| \`string\` \\| \`null\`) The value to show on the right side. +- actions: (\`React.ReactNode\`) The actions to show at the end of the row. *** ## Example -Use the `Header` component in any widget or UI route. +Use the `SectionRow` component in any widget or UI route. For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: @@ -30052,26 +29600,13 @@ For example, create the widget `src/admin/widgets/product-widget.tsx` with the f import { defineWidgetConfig } from "@medusajs/admin-sdk" import { Container } from "../components/container" import { Header } from "../components/header" +import { SectionRow } from "../components/section-row" const ProductWidget = () => { return ( -
{ - alert("You clicked the button.") - }, - }, - }, - ]} - /> +
+ ) } @@ -30083,7 +29618,7 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -This widget also uses a [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) custom component. +This widget also uses the [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) and [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom component. # JSON View - Admin Components @@ -30314,70 +29849,111 @@ export default ProductWidget This shows the JSON section at the top of the product page, passing it the object `{ name: "John" }`. -# Section Row - Admin Components +# Header - Admin Components -The Medusa Admin often shows information in rows of label-values, such as when showing a product's details. +Each section in the Medusa Admin has a header with a title, and optionally a subtitle with buttons to perform an action. -![Example of a section row in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728292781/Medusa%20Resources/section-row_kknbnw.png) +![Example of a header in a section](https://res.cloudinary.com/dza7lstvk/image/upload/v1728288562/Medusa%20Resources/header_dtz4gl.png) -To create a component that shows information in the same structure, create the file `src/admin/components/section-row.tsx` with the following content: +To create a component that uses the same header styling and structure, create the file `src/admin/components/header.tsx` with the following content: -```tsx title="src/admin/components/section-row.tsx" -import { Text, clx } from "@medusajs/ui" +```tsx title="src/admin/components/header.tsx" +import { Heading, Button, Text } from "@medusajs/ui" +import React from "react" +import { Link, LinkProps } from "react-router-dom" +import { ActionMenu, ActionMenuProps } from "./action-menu" -export type SectionRowProps = { +export type HeadingProps = { title: string - value?: React.ReactNode | string | null - actions?: React.ReactNode + subtitle?: string + actions?: ( + { + type: "button", + props: React.ComponentProps + link?: LinkProps + } | + { + type: "action-menu" + props: ActionMenuProps + } | + { + type: "custom" + children: React.ReactNode + } + )[] } -export const SectionRow = ({ title, value, actions }: SectionRowProps) => { - const isValueString = typeof value === "string" || !value - +export const Header = ({ + title, + subtitle, + actions = [], +}: HeadingProps) => { return ( -
- - {title} - - - {isValueString ? ( - - {value ?? "-"} - - ) : ( -
{value}
+
+
+ {title} + {subtitle && ( + + {subtitle} + + )} +
+ {actions.length > 0 && ( +
+ {actions.map((action, index) => ( + <> + {action.type === "button" && ( + + )} + {action.type === "action-menu" && ( + + )} + {action.type === "custom" && action.children} + + ))} +
)} - - {actions &&
{actions}
}
) } ``` -The `SectionRow` component shows a title and a value in the same row. +The `Header` component shows a title, and optionally a subtitle and action buttons. + +The component also uses the [Action Menu](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/action-menu/index.html.md) custom component. It accepts the following props: -- title: (\`string\`) The title to show on the left side. -- value: (\`React.ReactNode\` \\| \`string\` \\| \`null\`) The value to show on the right side. -- actions: (\`React.ReactNode\`) The actions to show at the end of the row. +- title: (\`string\`) The section's title. +- subtitle: (\`string\`) The section's subtitle. +- actions: (\`object\[]\`) An array of actions to show. + + - type: (\`button\` \\| \`action-menu\` \\| \`custom\`) The type of action to add. + + \- If its value is \`button\`, it'll show a button that can have a link or an on-click action. + + \- If its value is \`action-menu\`, it'll show a three dot icon with a dropdown of actions. + + \- If its value is \`custom\`, you can pass any React nodes to render. + + - props: (object) + + - children: (React.ReactNode) This property is only accepted if \`type\` is \`custom\`. Its content is rendered as part of the actions. *** ## Example -Use the `SectionRow` component in any widget or UI route. +Use the `Header` component in any widget or UI route. For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: @@ -30385,13 +29961,26 @@ For example, create the widget `src/admin/widgets/product-widget.tsx` with the f import { defineWidgetConfig } from "@medusajs/admin-sdk" import { Container } from "../components/container" import { Header } from "../components/header" -import { SectionRow } from "../components/section-row" const ProductWidget = () => { return ( -
- +
{ + alert("You clicked the button.") + }, + }, + }, + ]} + /> ) } @@ -30403,12 +29992,12 @@ export const config = defineWidgetConfig({ export default ProductWidget ``` -This widget also uses the [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom component. +This widget also uses a [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md) custom component. # Table - Admin Components -If you're using [Medusa v2.4.0+](https://github.com/medusajs/medusa/releases/tag/v2.4.0), it's recommended to use the [Data Table](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/data-table) component instead. +If you're using [Medusa v2.4.0+](https://github.com/medusajs/medusa/releases/tag/v2.4.0), it's recommended to use the [Data Table](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/data-table/index.html.md) component instead. The listing pages in the Admin show a table with pagination. @@ -30511,7 +30100,7 @@ export const Table = ({ } ``` -The `Table` component uses the component from the [UI package](undefined/ui/components/table), with additional styling and rendering of data. +The `Table` component uses the component from the [UI package](https://docs.medusajs.com/ui/components/table/index.html.md), with additional styling and rendering of data. It accepts the following props: @@ -30602,7 +30191,7 @@ This widget also uses the [Container](../container.mdx) custom component. This section shows you how to use the `Table` component when fetching data from the Medusa application's API routes. -Assuming you've set up the JS SDK as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/js-sdk), create the UI route `src/admin/routes/custom/page.tsx` with the following content: +Assuming you've set up the JS SDK as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk/index.html.md), create the UI route `src/admin/routes/custom/page.tsx` with the following content: ```tsx title="src/admin/routes/custom/page.tsx" collapsibleLines="1-10" expandButtonLabel="Show Imports" highlights={tableExampleHighlights} import { defineRouteConfig } from "@medusajs/admin-sdk" @@ -30649,7 +30238,7 @@ In the `CustomPage` component, you define: Then, you use `useQuery` from [Tanstack Query](https://tanstack.com/query/latest) to retrieve products using the JS SDK. You pass `limit` and `offset` as query parameters, and you set the `queryKey`, which is used for caching and revalidation, to be based on the key `products`, along with the current limit and offset. So, whenever the `offset` variable changes, the request is sent again to retrieve the products of the current page. -You can change the query to send a request to a custom API route as explained in [this guide](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/js-sdk#send-requests-to-custom-routes). +You can change the query to send a request to a custom API route as explained in [this guide](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/js-sdk#send-requests-to-custom-routes/index.html.md). 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. @@ -30686,7 +30275,7 @@ return ( ) ``` -Aside from the `Table` component, this UI route also uses the [SingleColumnLayout](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/layouts/single-column), [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container), and [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom component. +Aside from the `Table` component, this UI route also uses the [SingleColumnLayout](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/layouts/single-column/index.html.md), [Container](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/container/index.html.md), and [Header](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/admin-components/components/header/index.html.md) custom component. If `data` isn't `undefined`, you display the `Table` component passing it the following props: @@ -30698,123 +30287,174 @@ If `data` isn't `undefined`, you display the `Table` component passing it the fo To test it out, log into the Medusa Admin and open `http://localhost:9000/app/custom`. You'll find a table of products with pagination. -# Section Row - Admin Components +# Service Factory Reference -The Medusa Admin often shows information in rows of label-values, such as when showing a product's details. +This section of the documentation provides a reference of the methods generated for services extending the service factory (`MedusaService`), and how to use them. -![Example of a section row in the Medusa Admin](https://res.cloudinary.com/dza7lstvk/image/upload/v1728292781/Medusa%20Resources/section-row_kknbnw.png) +Learn more about the service factory in [this documentation](https://docs.medusajs.com/docs/learn/fundamentals/modules/service-factory/index.html.md). -To create a component that shows information in the same structure, create the file `src/admin/components/section-row.tsx` with the following content: +## Method Names -```tsx title="src/admin/components/section-row.tsx" -import { Text, clx } from "@medusajs/ui" +Generated method names are of the format `{operationName}_{dataModelName}`, where: -export type SectionRowProps = { - title: string - value?: React.ReactNode | string | null - actions?: React.ReactNode -} +- `{operationName}` is the name of the operation. For example, `create`. +- `{dataModelName}` is the pascal-case version of the data model's key that's passed in the object parameter of `MedusaService`. The name is pluralized for all operations except for the `retrieve` operation. -export const SectionRow = ({ title, value, actions }: SectionRowProps) => { - const isValueString = typeof value === "string" || !value +Some examples of method names: - return ( -
- - {title} - +- `createPosts` +- `createMyPosts` +- `retrievePost` +- `listPosts` - {isValueString ? ( - - {value ?? "-"} - - ) : ( -
{value}
- )} +*** - {actions &&
{actions}
} -
- ) -} -``` +## Methods Reference -The `SectionRow` component shows a title and a value in the same row. +The reference uses only the operation name to refer to the method. -It accepts the following props: -- title: (\`string\`) The title to show on the left side. -- value: (\`React.ReactNode\` \\| \`string\` \\| \`null\`) The value to show on the right side. -- actions: (\`React.ReactNode\`) The actions to show at the end of the row. +# Filter Records - Service Factory Reference + +Many of the service factory's generated methods allow passing filters to perform an operation, such as to update or delete records matching the filters. + +This guide provides examples of using filters. + +The `list` method is used in the example snippets of this reference, but you can use the same filtering mechanism with any method that accepts filters. *** -## Example +## Match Exact Value -Use the `SectionRow` component in any widget or UI route. +```ts +const posts = await postModuleService.listPosts({ + name: "My Post 2", +}) +``` -For example, create the widget `src/admin/widgets/product-widget.tsx` with the following content: +If you pass a property with its value, only records whose properties exactly match the value are selected. -```tsx title="src/admin/widgets/product-widget.tsx" -import { defineWidgetConfig } from "@medusajs/admin-sdk" -import { Container } from "../components/container" -import { Header } from "../components/header" -import { SectionRow } from "../components/section-row" +In the example above, only posts having the name `My Post 2` are retrieved. -const ProductWidget = () => { - return ( - -
- - - ) -} +*** -export const config = defineWidgetConfig({ - zone: "product.details.before", +## Match Multiple Values + +```ts +const posts = await postModuleService.listPosts({ + views: [ + 50, + 100, + ], }) +``` -export default ProductWidget +To find records with a property matching multiple values, pass an array of those values as the property's value in the filter. + +In the example above, only posts having either `50` or `100` views are retrieved. + +*** + +## Don't Match Values + +```ts +const posts = await postModuleService.listPosts({ + name: { + $nin: [ + "My Post", + ], + }, +}) ``` -This widget also uses the [Container](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/container) and [Header](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/admin-components/components/header) custom component. +To find records with a property that doesn't match one or more values, pass an object with a `$nin` property. Its value is an array of multiple values that a record's property shouldn't match. +In the example above, only posts that don't have the name `My Post` are retrieved. -# Service Factory Reference +*** -This section of the documentation provides a reference of the methods generated for services extending the service factory (`MedusaService`), and how to use them. +## Match Text Like Value -Learn more about the service factory in [this documentation](undefined/docs/learn/fundamentals/modules/service-factory). +This filter only applies to text-like properties, including `id` and `enum` properties. -## Method Names +```ts +const posts = await postModuleService.listPosts({ + name: { + $like: "My%", + }, +}) +``` -Generated method names are of the format `{operationName}_{dataModelName}`, where: +To perform a `like` filter on a record's property, set the property's value to an object with a `$like` property. Its value is the string to use when applying the `like` filter. -- `{operationName}` is the name of the operation. For example, `create`. -- `{dataModelName}` is the pascal-case version of the data model's key that's passed in the object parameter of `MedusaService`. The name is pluralized for all operations except for the `retrieve` operation. +The example above matches all posts whose name starts with `My`. -Some examples of method names: +*** -- `createPosts` -- `createMyPosts` -- `retrievePost` -- `listPosts` +## Apply Range Filters + +This filter only applies to the `number` and `dateTime` properties. + +```ts +const posts = await postModuleService.listPosts({ + published_at: { + $lt: new Date(), + }, +}) +``` + +To filter a record's property to be within a range, set the property's value to an object with any of the following properties: + +1. `$lt`: The property's value must be less than the supplied value. +2. `$lte`: The property's value must be less than or equal to the supplied value. +3. `$gt`: The property's value must be greater than the supplied value. +4. `$gte`: The property's value must be greater than or equal the supplied value. + +In the example above, only posts whose `published_at` property is before the current date and time are retrieved. + +### Example: Retrieve Posts Published Today + +```ts +const startToday = new Date() +startToday.setHours(0, 0, 0, 0) + +const endToday = new Date() +endToday.setHours(23, 59, 59, 59) + +const posts = await postModuleService.listPosts({ + published_at: { + $gte: startToday, + $lte: endToday, + }, +}) +``` + +The `dateTime` property also stores the time. So, when matching for an exact day, you must set a range filter to be between the beginning and end of the day. + +In this example, you retrieve the current date twice: once to set its time to `00:00:00`, and another to set its time `23:59:59`. Then, you retrieve posts whose `published_at` property is between `00:00:00` and `23:59:59` of today. *** -## Methods Reference +## Apply Or Condition -The reference uses only the operation name to refer to the method. +```ts +const posts = await postModuleService.listPosts({ + $or: [ + { + name: "My Post", + }, + { + published_at: { + $lt: new Date(), + }, + }, + ], +}) +``` + +To use an `or` condition, pass to the filter object the `$or` property, whose value is an array of filters. + +In the example above, posts whose name is `My Post` or their `published_at` date is less than the current date and time are retrieved. # create Method - Service Factory Reference @@ -30855,46 +30495,6 @@ const posts = await postModuleService.createPosts([ If an array is passed of the method, an array of the created records is also returned. -# delete Method - Service Factory Reference - -This method deletes one or more records. - -## Delete One Record - -```ts -await postModuleService.deletePosts("123") -``` - -To delete one record, pass its ID as a parameter of the method. - -*** - -## Delete Multiple Records - -```ts -await postModuleService.deletePosts([ - "123", - "321", -]) -``` - -To delete multiple records, pass an array of IDs as a parameter of the method. - -*** - -## Delete Records Matching Filters - -```ts -await postModuleService.deletePosts({ - name: "My Post", -}) -``` - -To delete records matching a set of filters, pass an object of filters as a parameter. - -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). - - # list Method - Service Factory Reference This method retrieves a list of records. @@ -30921,7 +30521,7 @@ const posts = await postModuleService.listPosts({ To retrieve records matching a set of filters, pass an object of the filters as a first parameter. -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns @@ -30931,7 +30531,7 @@ The method returns an array of the first `15` records matching the filters. ## Retrieve Relations -This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](undefined/docs/learn/fundamentals/module-links/query). +This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). ```ts const posts = await postModuleService.listPosts({}, { @@ -31042,7 +30642,7 @@ const [posts, count] = await postModuleService.listAndCountPosts({ To retrieve records matching a set of filters, pass an object of the filters as a first parameter. -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns @@ -31055,7 +30655,7 @@ The method returns an array with two items: ## Retrieve Relations -This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](undefined/docs/learn/fundamentals/module-links/query). +This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). ```ts const [posts, count] = await postModuleService.listAndCountPosts({}, { @@ -31149,91 +30749,44 @@ The method returns an array with two items: 2. The second is the total count of records. -# restore Method - Service Factory Reference +# delete Method - Service Factory Reference -This method restores one or more records of the data model that were [soft-deleted](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/methods/soft-delete). +This method deletes one or more records. -## Restore One Record +## Delete One Record ```ts -const restoredPosts = await postModuleService.restorePosts("123") +await postModuleService.deletePosts("123") ``` -### Parameters - -To restore one record, pass its ID as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: ["123"], -} -``` +To delete one record, pass its ID as a parameter of the method. *** -## Restore Multiple Records +## Delete Multiple Records ```ts -const restoredPosts = await postModuleService.restorePosts([ +await postModuleService.deletePosts([ "123", "321", ]) ``` -### Parameters - -To restore multiple records, pass an array of IDs as a parameter of the method. - -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: [ - "123", - "321", - ], -} -``` +To delete multiple records, pass an array of IDs as a parameter of the method. *** -## Restore Records Matching Filters +## Delete Records Matching Filters ```ts -const restoredPosts = await postModuleService.restorePosts({ +await postModuleService.deletePosts({ name: "My Post", }) ``` -### Parameters - -To restore records matching a set of filters, pass an object of fitlers as a parameter of the method. - -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +To delete records matching a set of filters, pass an object of filters as a parameter. -### Returns - -The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. - -For example, the returned object of the above example is: - -```ts -restoredPosts = { - post_id: [ - "123", - ], -} -``` +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). # retrieve Method - Service Factory Reference @@ -31258,7 +30811,7 @@ The method returns the record as an object. ## Retrieve a Record's Relations -This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](undefined/docs/learn/fundamentals/module-links/query). +This applies to relations between data models of the same module. To retrieve linked records of different modules, use [Query](https://docs.medusajs.com/docs/learn/fundamentals/module-links/query/index.html.md). ```ts const post = await postModuleService.retrievePost("123", { @@ -31365,7 +30918,7 @@ const deletedPosts = await postModuleService.softDeletePosts({ To soft delete records matching a set of filters, pass an object of filters as a parameter. -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns @@ -31380,6 +30933,93 @@ deletedPosts = { ``` +# restore Method - Service Factory Reference + +This method restores one or more records of the data model that were [soft-deleted](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/methods/soft-delete/index.html.md). + +## Restore One Record + +```ts +const restoredPosts = await postModuleService.restorePosts("123") +``` + +### Parameters + +To restore one record, pass its ID as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. + +For example, the returned object of the above example is: + +```ts +restoredPosts = { + post_id: ["123"], +} +``` + +*** + +## Restore Multiple Records + +```ts +const restoredPosts = await postModuleService.restorePosts([ + "123", + "321", +]) +``` + +### Parameters + +To restore multiple records, pass an array of IDs as a parameter of the method. + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. + +For example, the returned object of the above example is: + +```ts +restoredPosts = { + post_id: [ + "123", + "321", + ], +} +``` + +*** + +## Restore Records Matching Filters + +```ts +const restoredPosts = await postModuleService.restorePosts({ + name: "My Post", +}) +``` + +### Parameters + +To restore records matching a set of filters, pass an object of fitlers as a parameter of the method. + +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). + +### Returns + +The method returns an object, whose keys are of the format `{camel_case_data_model_name}_id`, and their values are arrays of restored records' IDs. + +For example, the returned object of the above example is: + +```ts +restoredPosts = { + post_id: [ + "123", + ], +} +``` + + # update Method - Service Factory Reference This method updates one or more records of the data model. @@ -31454,7 +31094,7 @@ To update records that match specified filters, pass as a parameter an object ha In the example above, you update the `published_at` property of every post record whose name is `My Post`. -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns @@ -31496,594 +31136,570 @@ To update records matching different sets of filters, pass an array of objects, In the example above, you update the `published_at` property of post records whose name is `My Post`, and update the `metadata` property of post records whose name is `Another Post`. -Learn more about accepted filters in [this documentation](undefined/home/runner/work/medusa/medusa/www/apps/resources/app/service-factory-reference/tips/filtering). +Learn more about accepted filters in [this documentation](https://docs.medusajs.com/Users/shahednasser/medusa/www/apps/resources/app/service-factory-reference/tips/filtering/index.html.md). ### Returns The method returns an array of objects of updated records. -# Filter Records - Service Factory Reference -Many of the service factory's generated methods allow passing filters to perform an operation, such as to update or delete records matching the filters. -This guide provides examples of using filters. +

Just Getting Started?

-The `list` method is used in the example snippets of this reference, but you can use the same filtering mechanism with any method that accepts filters. +Check out the [Medusa v2 Documentation](https://docs.medusajs.com/docs/index.html.md). -*** +

Medusa JS SDK

-## Match Exact Value +To use Medusa's JS SDK library, install the following packages in your project (not required for admin customizations): -```ts -const posts = await postModuleService.listPosts({ - name: "My Post 2", -}) +```bash +npm install @medusajs/js-sdk@latest @medusajs/types@latest ``` -If you pass a property with its value, only records whose properties exactly match the value are selected. +Learn more about the JS SDK and how to configure it in [this documentation](https://docs.medusajs.com/resources/js-sdk/index.html.md). -In the example above, only posts having the name `My Post 2` are retrieved. +### Download Full Reference -*** +Download this reference as an OpenApi YAML file. You can import this file to tools like Postman and start sending requests directly to your Medusa application. -## Match Multiple Values -```ts -const posts = await postModuleService.listPosts({ - views: [ - 50, - 100, - ], -}) -``` -To find records with a property matching multiple values, pass an array of those values as the property's value in the filter. -In the example above, only posts having either `50` or `100` views are retrieved. +## Admin API Reference -*** +- [GET /admin/api-keys](https://docs.medusajs.com/api/admin#api-keys_getapikeys) +- [POST /admin/api-keys](https://docs.medusajs.com/api/admin#api-keys_postapikeys) +- [GET /admin/api-keys/{id}](https://docs.medusajs.com/api/admin#api-keys_getapikeysid) +- [POST /admin/api-keys/{id}](https://docs.medusajs.com/api/admin#api-keys_postapikeysid) +- [DELETE /admin/api-keys/{id}](https://docs.medusajs.com/api/admin#api-keys_deleteapikeysid) +- [POST /admin/api-keys/{id}/revoke](https://docs.medusajs.com/api/admin#api-keys_postapikeysidrevoke) +- [POST /admin/api-keys/{id}/sales-channels](https://docs.medusajs.com/api/admin#api-keys_postapikeysidsaleschannels) +- [GET /admin/campaigns](https://docs.medusajs.com/api/admin#campaigns_getcampaigns) +- [POST /admin/campaigns](https://docs.medusajs.com/api/admin#campaigns_postcampaigns) +- [GET /admin/campaigns/{id}](https://docs.medusajs.com/api/admin#campaigns_getcampaignsid) +- [POST /admin/campaigns/{id}](https://docs.medusajs.com/api/admin#campaigns_postcampaignsid) +- [DELETE /admin/campaigns/{id}](https://docs.medusajs.com/api/admin#campaigns_deletecampaignsid) +- [POST /admin/campaigns/{id}/promotions](https://docs.medusajs.com/api/admin#campaigns_postcampaignsidpromotions) +- [GET /admin/claims](https://docs.medusajs.com/api/admin#claims_getclaims) +- [POST /admin/claims](https://docs.medusajs.com/api/admin#claims_postclaims) +- [GET /admin/claims/{id}](https://docs.medusajs.com/api/admin#claims_getclaimsid) +- [POST /admin/claims/{id}/cancel](https://docs.medusajs.com/api/admin#claims_postclaimsidcancel) +- [POST /admin/claims/{id}/claim-items](https://docs.medusajs.com/api/admin#claims_postclaimsidclaimitems) +- [POST /admin/claims/{id}/claim-items/{action_id}](https://docs.medusajs.com/api/admin#claims_postclaimsidclaimitemsaction_id) +- [DELETE /admin/claims/{id}/claim-items/{action_id}](https://docs.medusajs.com/api/admin#claims_deleteclaimsidclaimitemsaction_id) +- [POST /admin/claims/{id}/inbound/items](https://docs.medusajs.com/api/admin#claims_postclaimsidinbounditems) +- [POST /admin/claims/{id}/inbound/items/{action_id}](https://docs.medusajs.com/api/admin#claims_postclaimsidinbounditemsaction_id) +- [DELETE /admin/claims/{id}/inbound/items/{action_id}](https://docs.medusajs.com/api/admin#claims_deleteclaimsidinbounditemsaction_id) +- [POST /admin/claims/{id}/inbound/shipping-method](https://docs.medusajs.com/api/admin#claims_postclaimsidinboundshippingmethod) +- [POST /admin/claims/{id}/inbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#claims_postclaimsidinboundshippingmethodaction_id) +- [DELETE /admin/claims/{id}/inbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#claims_deleteclaimsidinboundshippingmethodaction_id) +- [POST /admin/claims/{id}/outbound/items](https://docs.medusajs.com/api/admin#claims_postclaimsidoutbounditems) +- [POST /admin/claims/{id}/outbound/items/{action_id}](https://docs.medusajs.com/api/admin#claims_postclaimsidoutbounditemsaction_id) +- [DELETE /admin/claims/{id}/outbound/items/{action_id}](https://docs.medusajs.com/api/admin#claims_deleteclaimsidoutbounditemsaction_id) +- [POST /admin/claims/{id}/outbound/shipping-method](https://docs.medusajs.com/api/admin#claims_postclaimsidoutboundshippingmethod) +- [POST /admin/claims/{id}/outbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#claims_postclaimsidoutboundshippingmethodaction_id) +- [DELETE /admin/claims/{id}/outbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#claims_deleteclaimsidoutboundshippingmethodaction_id) +- [POST /admin/claims/{id}/request](https://docs.medusajs.com/api/admin#claims_postclaimsidrequest) +- [DELETE /admin/claims/{id}/request](https://docs.medusajs.com/api/admin#claims_deleteclaimsidrequest) +- [GET /admin/collections](https://docs.medusajs.com/api/admin#collections_getcollections) +- [POST /admin/collections](https://docs.medusajs.com/api/admin#collections_postcollections) +- [GET /admin/collections/{id}](https://docs.medusajs.com/api/admin#collections_getcollectionsid) +- [POST /admin/collections/{id}](https://docs.medusajs.com/api/admin#collections_postcollectionsid) +- [DELETE /admin/collections/{id}](https://docs.medusajs.com/api/admin#collections_deletecollectionsid) +- [POST /admin/collections/{id}/products](https://docs.medusajs.com/api/admin#collections_postcollectionsidproducts) +- [GET /admin/currencies](https://docs.medusajs.com/api/admin#currencies_getcurrencies) +- [GET /admin/currencies/{code}](https://docs.medusajs.com/api/admin#currencies_getcurrenciescode) +- [GET /admin/customer-groups](https://docs.medusajs.com/api/admin#customer-groups_getcustomergroups) +- [POST /admin/customer-groups](https://docs.medusajs.com/api/admin#customer-groups_postcustomergroups) +- [GET /admin/customer-groups/{id}](https://docs.medusajs.com/api/admin#customer-groups_getcustomergroupsid) +- [POST /admin/customer-groups/{id}](https://docs.medusajs.com/api/admin#customer-groups_postcustomergroupsid) +- [DELETE /admin/customer-groups/{id}](https://docs.medusajs.com/api/admin#customer-groups_deletecustomergroupsid) +- [POST /admin/customer-groups/{id}/customers](https://docs.medusajs.com/api/admin#customer-groups_postcustomergroupsidcustomers) +- [GET /admin/customers](https://docs.medusajs.com/api/admin#customers_getcustomers) +- [POST /admin/customers](https://docs.medusajs.com/api/admin#customers_postcustomers) +- [GET /admin/customers/{id}](https://docs.medusajs.com/api/admin#customers_getcustomersid) +- [POST /admin/customers/{id}](https://docs.medusajs.com/api/admin#customers_postcustomersid) +- [DELETE /admin/customers/{id}](https://docs.medusajs.com/api/admin#customers_deletecustomersid) +- [GET /admin/customers/{id}/addresses](https://docs.medusajs.com/api/admin#customers_getcustomersidaddresses) +- [POST /admin/customers/{id}/addresses](https://docs.medusajs.com/api/admin#customers_postcustomersidaddresses) +- [GET /admin/customers/{id}/addresses/{address_id}](https://docs.medusajs.com/api/admin#customers_getcustomersidaddressesaddress_id) +- [POST /admin/customers/{id}/addresses/{address_id}](https://docs.medusajs.com/api/admin#customers_postcustomersidaddressesaddress_id) +- [DELETE /admin/customers/{id}/addresses/{address_id}](https://docs.medusajs.com/api/admin#customers_deletecustomersidaddressesaddress_id) +- [POST /admin/customers/{id}/customer-groups](https://docs.medusajs.com/api/admin#customers_postcustomersidcustomergroups) +- [GET /admin/draft-orders](https://docs.medusajs.com/api/admin#draft-orders_getdraftorders) +- [POST /admin/draft-orders](https://docs.medusajs.com/api/admin#draft-orders_postdraftorders) +- [GET /admin/draft-orders/{id}](https://docs.medusajs.com/api/admin#draft-orders_getdraftordersid) +- [GET /admin/exchanges](https://docs.medusajs.com/api/admin#exchanges_getexchanges) +- [POST /admin/exchanges](https://docs.medusajs.com/api/admin#exchanges_postexchanges) +- [GET /admin/exchanges/{id}](https://docs.medusajs.com/api/admin#exchanges_getexchangesid) +- [POST /admin/exchanges/{id}/cancel](https://docs.medusajs.com/api/admin#exchanges_postexchangesidcancel) +- [POST /admin/exchanges/{id}/inbound/items](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinbounditems) +- [POST /admin/exchanges/{id}/inbound/items/{action_id}](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinbounditemsaction_id) +- [DELETE /admin/exchanges/{id}/inbound/items/{action_id}](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidinbounditemsaction_id) +- [POST /admin/exchanges/{id}/inbound/shipping-method](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinboundshippingmethod) +- [POST /admin/exchanges/{id}/inbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#exchanges_postexchangesidinboundshippingmethodaction_id) +- [DELETE /admin/exchanges/{id}/inbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidinboundshippingmethodaction_id) +- [POST /admin/exchanges/{id}/outbound/items](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditems) +- [POST /admin/exchanges/{id}/outbound/items/{action_id}](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutbounditemsaction_id) +- [DELETE /admin/exchanges/{id}/outbound/items/{action_id}](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidoutbounditemsaction_id) +- [POST /admin/exchanges/{id}/outbound/shipping-method](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutboundshippingmethod) +- [POST /admin/exchanges/{id}/outbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#exchanges_postexchangesidoutboundshippingmethodaction_id) +- [DELETE /admin/exchanges/{id}/outbound/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidoutboundshippingmethodaction_id) +- [POST /admin/exchanges/{id}/request](https://docs.medusajs.com/api/admin#exchanges_postexchangesidrequest) +- [DELETE /admin/exchanges/{id}/request](https://docs.medusajs.com/api/admin#exchanges_deleteexchangesidrequest) +- [GET /admin/fulfillment-providers](https://docs.medusajs.com/api/admin#fulfillment-providers_getfulfillmentproviders) +- [GET /admin/fulfillment-providers/{id}/options](https://docs.medusajs.com/api/admin#fulfillment-providers_getfulfillmentprovidersidoptions) +- [DELETE /admin/fulfillment-sets/{id}](https://docs.medusajs.com/api/admin#fulfillment-sets_deletefulfillmentsetsid) +- [POST /admin/fulfillment-sets/{id}/service-zones](https://docs.medusajs.com/api/admin#fulfillment-sets_postfulfillmentsetsidservicezones) +- [GET /admin/fulfillment-sets/{id}/service-zones/{zone_id}](https://docs.medusajs.com/api/admin#fulfillment-sets_getfulfillmentsetsidservicezoneszone_id) +- [POST /admin/fulfillment-sets/{id}/service-zones/{zone_id}](https://docs.medusajs.com/api/admin#fulfillment-sets_postfulfillmentsetsidservicezoneszone_id) +- [DELETE /admin/fulfillment-sets/{id}/service-zones/{zone_id}](https://docs.medusajs.com/api/admin#fulfillment-sets_deletefulfillmentsetsidservicezoneszone_id) +- [POST /admin/fulfillments](https://docs.medusajs.com/api/admin#fulfillments_postfulfillments) +- [POST /admin/fulfillments/{id}/cancel](https://docs.medusajs.com/api/admin#fulfillments_postfulfillmentsidcancel) +- [POST /admin/fulfillments/{id}/shipment](https://docs.medusajs.com/api/admin#fulfillments_postfulfillmentsidshipment) +- [GET /admin/inventory-items](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitems) +- [POST /admin/inventory-items](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitems) +- [POST /admin/inventory-items/location-levels/batch](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemslocationlevelsbatch) +- [GET /admin/inventory-items/{id}](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitemsid) +- [POST /admin/inventory-items/{id}](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsid) +- [DELETE /admin/inventory-items/{id}](https://docs.medusajs.com/api/admin#inventory-items_deleteinventoryitemsid) +- [GET /admin/inventory-items/{id}/location-levels](https://docs.medusajs.com/api/admin#inventory-items_getinventoryitemsidlocationlevels) +- [POST /admin/inventory-items/{id}/location-levels](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsidlocationlevels) +- [POST /admin/inventory-items/{id}/location-levels/batch](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsidlocationlevelsbatch) +- [POST /admin/inventory-items/{id}/location-levels/{location_id}](https://docs.medusajs.com/api/admin#inventory-items_postinventoryitemsidlocationlevelslocation_id) +- [DELETE /admin/inventory-items/{id}/location-levels/{location_id}](https://docs.medusajs.com/api/admin#inventory-items_deleteinventoryitemsidlocationlevelslocation_id) +- [GET /admin/invites](https://docs.medusajs.com/api/admin#invites_getinvites) +- [POST /admin/invites](https://docs.medusajs.com/api/admin#invites_postinvites) +- [POST /admin/invites/accept](https://docs.medusajs.com/api/admin#invites_postinvitesaccept) +- [GET /admin/invites/{id}](https://docs.medusajs.com/api/admin#invites_getinvitesid) +- [DELETE /admin/invites/{id}](https://docs.medusajs.com/api/admin#invites_deleteinvitesid) +- [POST /admin/invites/{id}/resend](https://docs.medusajs.com/api/admin#invites_postinvitesidresend) +- [GET /admin/notifications](https://docs.medusajs.com/api/admin#notifications_getnotifications) +- [GET /admin/notifications/{id}](https://docs.medusajs.com/api/admin#notifications_getnotificationsid) +- [POST /admin/order-edits](https://docs.medusajs.com/api/admin#order-edits_postorderedits) +- [DELETE /admin/order-edits/{id}](https://docs.medusajs.com/api/admin#order-edits_deleteordereditsid) +- [POST /admin/order-edits/{id}/confirm](https://docs.medusajs.com/api/admin#order-edits_postordereditsidconfirm) +- [POST /admin/order-edits/{id}/items](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditems) +- [POST /admin/order-edits/{id}/items/item/{item_id}](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditemsitemitem_id) +- [POST /admin/order-edits/{id}/items/{action_id}](https://docs.medusajs.com/api/admin#order-edits_postordereditsiditemsaction_id) +- [DELETE /admin/order-edits/{id}/items/{action_id}](https://docs.medusajs.com/api/admin#order-edits_deleteordereditsiditemsaction_id) +- [POST /admin/order-edits/{id}/request](https://docs.medusajs.com/api/admin#order-edits_postordereditsidrequest) +- [POST /admin/order-edits/{id}/shipping-method](https://docs.medusajs.com/api/admin#order-edits_postordereditsidshippingmethod) +- [POST /admin/order-edits/{id}/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#order-edits_postordereditsidshippingmethodaction_id) +- [DELETE /admin/order-edits/{id}/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#order-edits_deleteordereditsidshippingmethodaction_id) +- [GET /admin/orders](https://docs.medusajs.com/api/admin#orders_getorders) +- [GET /admin/orders/{id}](https://docs.medusajs.com/api/admin#orders_getordersid) +- [POST /admin/orders/{id}](https://docs.medusajs.com/api/admin#orders_postordersid) +- [POST /admin/orders/{id}/archive](https://docs.medusajs.com/api/admin#orders_postordersidarchive) +- [POST /admin/orders/{id}/cancel](https://docs.medusajs.com/api/admin#orders_postordersidcancel) +- [GET /admin/orders/{id}/changes](https://docs.medusajs.com/api/admin#orders_getordersidchanges) +- [POST /admin/orders/{id}/complete](https://docs.medusajs.com/api/admin#orders_postordersidcomplete) +- [POST /admin/orders/{id}/fulfillments](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments) +- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/cancel](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) +- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/mark-as-delivered](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idmarkasdelivered) +- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/shipments](https://docs.medusajs.com/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) +- [GET /admin/orders/{id}/line-items](https://docs.medusajs.com/api/admin#orders_getordersidlineitems) +- [GET /admin/orders/{id}/preview](https://docs.medusajs.com/api/admin#orders_getordersidpreview) +- [POST /admin/orders/{id}/transfer](https://docs.medusajs.com/api/admin#orders_postordersidtransfer) +- [POST /admin/orders/{id}/transfer/cancel](https://docs.medusajs.com/api/admin#orders_postordersidtransfercancel) +- [POST /admin/payment-collections](https://docs.medusajs.com/api/admin#payment-collections_postpaymentcollections) +- [DELETE /admin/payment-collections/{id}](https://docs.medusajs.com/api/admin#payment-collections_deletepaymentcollectionsid) +- [POST /admin/payment-collections/{id}/mark-as-paid](https://docs.medusajs.com/api/admin#payment-collections_postpaymentcollectionsidmarkaspaid) +- [GET /admin/payments](https://docs.medusajs.com/api/admin#payments_getpayments) +- [GET /admin/payments/payment-providers](https://docs.medusajs.com/api/admin#payments_getpaymentspaymentproviders) +- [GET /admin/payments/{id}](https://docs.medusajs.com/api/admin#payments_getpaymentsid) +- [POST /admin/payments/{id}/capture](https://docs.medusajs.com/api/admin#payments_postpaymentsidcapture) +- [POST /admin/payments/{id}/refund](https://docs.medusajs.com/api/admin#payments_postpaymentsidrefund) +- [GET /admin/price-lists](https://docs.medusajs.com/api/admin#price-lists_getpricelists) +- [POST /admin/price-lists](https://docs.medusajs.com/api/admin#price-lists_postpricelists) +- [GET /admin/price-lists/{id}](https://docs.medusajs.com/api/admin#price-lists_getpricelistsid) +- [POST /admin/price-lists/{id}](https://docs.medusajs.com/api/admin#price-lists_postpricelistsid) +- [DELETE /admin/price-lists/{id}](https://docs.medusajs.com/api/admin#price-lists_deletepricelistsid) +- [POST /admin/price-lists/{id}/prices/batch](https://docs.medusajs.com/api/admin#price-lists_postpricelistsidpricesbatch) +- [POST /admin/price-lists/{id}/products](https://docs.medusajs.com/api/admin#price-lists_postpricelistsidproducts) +- [GET /admin/price-preferences](https://docs.medusajs.com/api/admin#price-preferences_getpricepreferences) +- [POST /admin/price-preferences](https://docs.medusajs.com/api/admin#price-preferences_postpricepreferences) +- [GET /admin/price-preferences/{id}](https://docs.medusajs.com/api/admin#price-preferences_getpricepreferencesid) +- [POST /admin/price-preferences/{id}](https://docs.medusajs.com/api/admin#price-preferences_postpricepreferencesid) +- [DELETE /admin/price-preferences/{id}](https://docs.medusajs.com/api/admin#price-preferences_deletepricepreferencesid) +- [GET /admin/product-categories](https://docs.medusajs.com/api/admin#product-categories_getproductcategories) +- [POST /admin/product-categories](https://docs.medusajs.com/api/admin#product-categories_postproductcategories) +- [GET /admin/product-categories/{id}](https://docs.medusajs.com/api/admin#product-categories_getproductcategoriesid) +- [POST /admin/product-categories/{id}](https://docs.medusajs.com/api/admin#product-categories_postproductcategoriesid) +- [DELETE /admin/product-categories/{id}](https://docs.medusajs.com/api/admin#product-categories_deleteproductcategoriesid) +- [POST /admin/product-categories/{id}/products](https://docs.medusajs.com/api/admin#product-categories_postproductcategoriesidproducts) +- [GET /admin/product-tags](https://docs.medusajs.com/api/admin#product-tags_getproducttags) +- [POST /admin/product-tags](https://docs.medusajs.com/api/admin#product-tags_postproducttags) +- [GET /admin/product-tags/{id}](https://docs.medusajs.com/api/admin#product-tags_getproducttagsid) +- [POST /admin/product-tags/{id}](https://docs.medusajs.com/api/admin#product-tags_postproducttagsid) +- [DELETE /admin/product-tags/{id}](https://docs.medusajs.com/api/admin#product-tags_deleteproducttagsid) +- [GET /admin/product-types](https://docs.medusajs.com/api/admin#product-types_getproducttypes) +- [POST /admin/product-types](https://docs.medusajs.com/api/admin#product-types_postproducttypes) +- [GET /admin/product-types/{id}](https://docs.medusajs.com/api/admin#product-types_getproducttypesid) +- [POST /admin/product-types/{id}](https://docs.medusajs.com/api/admin#product-types_postproducttypesid) +- [DELETE /admin/product-types/{id}](https://docs.medusajs.com/api/admin#product-types_deleteproducttypesid) +- [GET /admin/product-variants](https://docs.medusajs.com/api/admin#product-variants_getproductvariants) +- [GET /admin/products](https://docs.medusajs.com/api/admin#products_getproducts) +- [POST /admin/products](https://docs.medusajs.com/api/admin#products_postproducts) +- [POST /admin/products/batch](https://docs.medusajs.com/api/admin#products_postproductsbatch) +- [POST /admin/products/export](https://docs.medusajs.com/api/admin#products_postproductsexport) +- [POST /admin/products/import](https://docs.medusajs.com/api/admin#products_postproductsimport) +- [POST /admin/products/import/{transaction_id}/confirm](https://docs.medusajs.com/api/admin#products_postproductsimporttransaction_idconfirm) +- [GET /admin/products/{id}](https://docs.medusajs.com/api/admin#products_getproductsid) +- [POST /admin/products/{id}](https://docs.medusajs.com/api/admin#products_postproductsid) +- [DELETE /admin/products/{id}](https://docs.medusajs.com/api/admin#products_deleteproductsid) +- [GET /admin/products/{id}/options](https://docs.medusajs.com/api/admin#products_getproductsidoptions) +- [POST /admin/products/{id}/options](https://docs.medusajs.com/api/admin#products_postproductsidoptions) +- [GET /admin/products/{id}/options/{option_id}](https://docs.medusajs.com/api/admin#products_getproductsidoptionsoption_id) +- [POST /admin/products/{id}/options/{option_id}](https://docs.medusajs.com/api/admin#products_postproductsidoptionsoption_id) +- [DELETE /admin/products/{id}/options/{option_id}](https://docs.medusajs.com/api/admin#products_deleteproductsidoptionsoption_id) +- [GET /admin/products/{id}/variants](https://docs.medusajs.com/api/admin#products_getproductsidvariants) +- [POST /admin/products/{id}/variants](https://docs.medusajs.com/api/admin#products_postproductsidvariants) +- [POST /admin/products/{id}/variants/batch](https://docs.medusajs.com/api/admin#products_postproductsidvariantsbatch) +- [POST /admin/products/{id}/variants/inventory-items/batch](https://docs.medusajs.com/api/admin#products_postproductsidvariantsinventoryitemsbatch) +- [GET /admin/products/{id}/variants/{variant_id}](https://docs.medusajs.com/api/admin#products_getproductsidvariantsvariant_id) +- [POST /admin/products/{id}/variants/{variant_id}](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_id) +- [DELETE /admin/products/{id}/variants/{variant_id}](https://docs.medusajs.com/api/admin#products_deleteproductsidvariantsvariant_id) +- [POST /admin/products/{id}/variants/{variant_id}/inventory-items](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_idinventoryitems) +- [POST /admin/products/{id}/variants/{variant_id}/inventory-items/{inventory_item_id}](https://docs.medusajs.com/api/admin#products_postproductsidvariantsvariant_idinventoryitemsinventory_item_id) +- [DELETE /admin/products/{id}/variants/{variant_id}/inventory-items/{inventory_item_id}](https://docs.medusajs.com/api/admin#products_deleteproductsidvariantsvariant_idinventoryitemsinventory_item_id) +- [GET /admin/promotions](https://docs.medusajs.com/api/admin#promotions_getpromotions) +- [POST /admin/promotions](https://docs.medusajs.com/api/admin#promotions_postpromotions) +- [GET /admin/promotions/rule-attribute-options/{rule_type}](https://docs.medusajs.com/api/admin#promotions_getpromotionsruleattributeoptionsrule_type) +- [GET /admin/promotions/rule-value-options/{rule_type}/{rule_attribute_id}](https://docs.medusajs.com/api/admin#promotions_getpromotionsrulevalueoptionsrule_typerule_attribute_id) +- [GET /admin/promotions/{id}](https://docs.medusajs.com/api/admin#promotions_getpromotionsid) +- [POST /admin/promotions/{id}](https://docs.medusajs.com/api/admin#promotions_postpromotionsid) +- [DELETE /admin/promotions/{id}](https://docs.medusajs.com/api/admin#promotions_deletepromotionsid) +- [POST /admin/promotions/{id}/buy-rules/batch](https://docs.medusajs.com/api/admin#promotions_postpromotionsidbuyrulesbatch) +- [POST /admin/promotions/{id}/rules/batch](https://docs.medusajs.com/api/admin#promotions_postpromotionsidrulesbatch) +- [POST /admin/promotions/{id}/target-rules/batch](https://docs.medusajs.com/api/admin#promotions_postpromotionsidtargetrulesbatch) +- [GET /admin/promotions/{id}/{rule_type}](https://docs.medusajs.com/api/admin#promotions_getpromotionsidrule_type) +- [GET /admin/refund-reasons](https://docs.medusajs.com/api/admin#refund-reasons_getrefundreasons) +- [POST /admin/refund-reasons](https://docs.medusajs.com/api/admin#refund-reasons_postrefundreasons) +- [GET /admin/refund-reasons/{id}](https://docs.medusajs.com/api/admin#refund-reasons_getrefundreasonsid) +- [POST /admin/refund-reasons/{id}](https://docs.medusajs.com/api/admin#refund-reasons_postrefundreasonsid) +- [DELETE /admin/refund-reasons/{id}](https://docs.medusajs.com/api/admin#refund-reasons_deleterefundreasonsid) +- [GET /admin/regions](https://docs.medusajs.com/api/admin#regions_getregions) +- [POST /admin/regions](https://docs.medusajs.com/api/admin#regions_postregions) +- [GET /admin/regions/{id}](https://docs.medusajs.com/api/admin#regions_getregionsid) +- [POST /admin/regions/{id}](https://docs.medusajs.com/api/admin#regions_postregionsid) +- [DELETE /admin/regions/{id}](https://docs.medusajs.com/api/admin#regions_deleteregionsid) +- [GET /admin/reservations](https://docs.medusajs.com/api/admin#reservations_getreservations) +- [POST /admin/reservations](https://docs.medusajs.com/api/admin#reservations_postreservations) +- [GET /admin/reservations/{id}](https://docs.medusajs.com/api/admin#reservations_getreservationsid) +- [POST /admin/reservations/{id}](https://docs.medusajs.com/api/admin#reservations_postreservationsid) +- [DELETE /admin/reservations/{id}](https://docs.medusajs.com/api/admin#reservations_deletereservationsid) +- [GET /admin/return-reasons](https://docs.medusajs.com/api/admin#return-reasons_getreturnreasons) +- [POST /admin/return-reasons](https://docs.medusajs.com/api/admin#return-reasons_postreturnreasons) +- [GET /admin/return-reasons/{id}](https://docs.medusajs.com/api/admin#return-reasons_getreturnreasonsid) +- [POST /admin/return-reasons/{id}](https://docs.medusajs.com/api/admin#return-reasons_postreturnreasonsid) +- [DELETE /admin/return-reasons/{id}](https://docs.medusajs.com/api/admin#return-reasons_deletereturnreasonsid) +- [GET /admin/returns](https://docs.medusajs.com/api/admin#returns_getreturns) +- [POST /admin/returns](https://docs.medusajs.com/api/admin#returns_postreturns) +- [GET /admin/returns/{id}](https://docs.medusajs.com/api/admin#returns_getreturnsid) +- [POST /admin/returns/{id}](https://docs.medusajs.com/api/admin#returns_postreturnsid) +- [POST /admin/returns/{id}/cancel](https://docs.medusajs.com/api/admin#returns_postreturnsidcancel) +- [POST /admin/returns/{id}/dismiss-items](https://docs.medusajs.com/api/admin#returns_postreturnsiddismissitems) +- [POST /admin/returns/{id}/dismiss-items/{action_id}](https://docs.medusajs.com/api/admin#returns_postreturnsiddismissitemsaction_id) +- [DELETE /admin/returns/{id}/dismiss-items/{action_id}](https://docs.medusajs.com/api/admin#returns_deletereturnsiddismissitemsaction_id) +- [POST /admin/returns/{id}/receive-items](https://docs.medusajs.com/api/admin#returns_postreturnsidreceiveitems) +- [POST /admin/returns/{id}/receive-items/{action_id}](https://docs.medusajs.com/api/admin#returns_postreturnsidreceiveitemsaction_id) +- [DELETE /admin/returns/{id}/receive-items/{action_id}](https://docs.medusajs.com/api/admin#returns_deletereturnsidreceiveitemsaction_id) +- [POST /admin/returns/{id}/receive](https://docs.medusajs.com/api/admin#returns_postreturnsidreceive) +- [DELETE /admin/returns/{id}/receive](https://docs.medusajs.com/api/admin#returns_deletereturnsidreceive) +- [POST /admin/returns/{id}/receive/confirm](https://docs.medusajs.com/api/admin#returns_postreturnsidreceiveconfirm) +- [POST /admin/returns/{id}/request-items](https://docs.medusajs.com/api/admin#returns_postreturnsidrequestitems) +- [POST /admin/returns/{id}/request-items/{action_id}](https://docs.medusajs.com/api/admin#returns_postreturnsidrequestitemsaction_id) +- [DELETE /admin/returns/{id}/request-items/{action_id}](https://docs.medusajs.com/api/admin#returns_deletereturnsidrequestitemsaction_id) +- [POST /admin/returns/{id}/request](https://docs.medusajs.com/api/admin#returns_postreturnsidrequest) +- [DELETE /admin/returns/{id}/request](https://docs.medusajs.com/api/admin#returns_deletereturnsidrequest) +- [POST /admin/returns/{id}/shipping-method](https://docs.medusajs.com/api/admin#returns_postreturnsidshippingmethod) +- [POST /admin/returns/{id}/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#returns_postreturnsidshippingmethodaction_id) +- [DELETE /admin/returns/{id}/shipping-method/{action_id}](https://docs.medusajs.com/api/admin#returns_deletereturnsidshippingmethodaction_id) +- [GET /admin/sales-channels](https://docs.medusajs.com/api/admin#sales-channels_getsaleschannels) +- [POST /admin/sales-channels](https://docs.medusajs.com/api/admin#sales-channels_postsaleschannels) +- [GET /admin/sales-channels/{id}](https://docs.medusajs.com/api/admin#sales-channels_getsaleschannelsid) +- [POST /admin/sales-channels/{id}](https://docs.medusajs.com/api/admin#sales-channels_postsaleschannelsid) +- [DELETE /admin/sales-channels/{id}](https://docs.medusajs.com/api/admin#sales-channels_deletesaleschannelsid) +- [POST /admin/sales-channels/{id}/products](https://docs.medusajs.com/api/admin#sales-channels_postsaleschannelsidproducts) +- [GET /admin/shipping-options](https://docs.medusajs.com/api/admin#shipping-options_getshippingoptions) +- [POST /admin/shipping-options](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptions) +- [GET /admin/shipping-options/{id}](https://docs.medusajs.com/api/admin#shipping-options_getshippingoptionsid) +- [POST /admin/shipping-options/{id}](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptionsid) +- [DELETE /admin/shipping-options/{id}](https://docs.medusajs.com/api/admin#shipping-options_deleteshippingoptionsid) +- [POST /admin/shipping-options/{id}/rules/batch](https://docs.medusajs.com/api/admin#shipping-options_postshippingoptionsidrulesbatch) +- [GET /admin/shipping-profiles](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofiles) +- [POST /admin/shipping-profiles](https://docs.medusajs.com/api/admin#shipping-profiles_postshippingprofiles) +- [GET /admin/shipping-profiles/{id}](https://docs.medusajs.com/api/admin#shipping-profiles_getshippingprofilesid) +- [POST /admin/shipping-profiles/{id}](https://docs.medusajs.com/api/admin#shipping-profiles_postshippingprofilesid) +- [DELETE /admin/shipping-profiles/{id}](https://docs.medusajs.com/api/admin#shipping-profiles_deleteshippingprofilesid) +- [GET /admin/stock-locations](https://docs.medusajs.com/api/admin#stock-locations_getstocklocations) +- [POST /admin/stock-locations](https://docs.medusajs.com/api/admin#stock-locations_poststocklocations) +- [GET /admin/stock-locations/{id}](https://docs.medusajs.com/api/admin#stock-locations_getstocklocationsid) +- [POST /admin/stock-locations/{id}](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsid) +- [DELETE /admin/stock-locations/{id}](https://docs.medusajs.com/api/admin#stock-locations_deletestocklocationsid) +- [POST /admin/stock-locations/{id}/fulfillment-providers](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsidfulfillmentproviders) +- [POST /admin/stock-locations/{id}/fulfillment-sets](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsidfulfillmentsets) +- [POST /admin/stock-locations/{id}/sales-channels](https://docs.medusajs.com/api/admin#stock-locations_poststocklocationsidsaleschannels) +- [GET /admin/stores](https://docs.medusajs.com/api/admin#stores_getstores) +- [GET /admin/stores/{id}](https://docs.medusajs.com/api/admin#stores_getstoresid) +- [POST /admin/stores/{id}](https://docs.medusajs.com/api/admin#stores_poststoresid) +- [GET /admin/tax-rates](https://docs.medusajs.com/api/admin#tax-rates_gettaxrates) +- [POST /admin/tax-rates](https://docs.medusajs.com/api/admin#tax-rates_posttaxrates) +- [GET /admin/tax-rates/{id}](https://docs.medusajs.com/api/admin#tax-rates_gettaxratesid) +- [POST /admin/tax-rates/{id}](https://docs.medusajs.com/api/admin#tax-rates_posttaxratesid) +- [DELETE /admin/tax-rates/{id}](https://docs.medusajs.com/api/admin#tax-rates_deletetaxratesid) +- [POST /admin/tax-rates/{id}/rules](https://docs.medusajs.com/api/admin#tax-rates_posttaxratesidrules) +- [DELETE /admin/tax-rates/{id}/rules/{rule_id}](https://docs.medusajs.com/api/admin#tax-rates_deletetaxratesidrulesrule_id) +- [GET /admin/tax-regions](https://docs.medusajs.com/api/admin#tax-regions_gettaxregions) +- [POST /admin/tax-regions](https://docs.medusajs.com/api/admin#tax-regions_posttaxregions) +- [GET /admin/tax-regions/{id}](https://docs.medusajs.com/api/admin#tax-regions_gettaxregionsid) +- [POST /admin/tax-regions/{id}](https://docs.medusajs.com/api/admin#tax-regions_posttaxregionsid) +- [DELETE /admin/tax-regions/{id}](https://docs.medusajs.com/api/admin#tax-regions_deletetaxregionsid) +- [POST /admin/uploads](https://docs.medusajs.com/api/admin#uploads_postuploads) +- [GET /admin/uploads/{id}](https://docs.medusajs.com/api/admin#uploads_getuploadsid) +- [DELETE /admin/uploads/{id}](https://docs.medusajs.com/api/admin#uploads_deleteuploadsid) +- [GET /admin/users](https://docs.medusajs.com/api/admin#users_getusers) +- [GET /admin/users/me](https://docs.medusajs.com/api/admin#users_getusersme) +- [GET /admin/users/{id}](https://docs.medusajs.com/api/admin#users_getusersid) +- [POST /admin/users/{id}](https://docs.medusajs.com/api/admin#users_postusersid) +- [DELETE /admin/users/{id}](https://docs.medusajs.com/api/admin#users_deleteusersid) +- [GET /admin/workflows-executions](https://docs.medusajs.com/api/admin#workflows-executions_getworkflowsexecutions) +- [GET /admin/workflows-executions/{id}](https://docs.medusajs.com/api/admin#workflows-executions_getworkflowsexecutionsid) +- [POST /admin/workflows-executions/{workflow_id}/run](https://docs.medusajs.com/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idrun) +- [POST /admin/workflows-executions/{workflow_id}/steps/failure](https://docs.medusajs.com/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idstepsfailure) +- [POST /admin/workflows-executions/{workflow_id}/steps/success](https://docs.medusajs.com/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idstepssuccess) +- [GET /admin/workflows-executions/{workflow_id}/subscribe](https://docs.medusajs.com/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idsubscribe) +- [GET /admin/workflows-executions/{workflow_id}/{transaction_id}](https://docs.medusajs.com/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idtransaction_id) +- [GET /admin/workflows-executions/{workflow_id}/{transaction_id}/{step_id}/subscribe](https://docs.medusajs.com/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idtransaction_idstep_idsubscribe) +- [POST /auth/session](https://docs.medusajs.com/api/admin#auth_postsession) +- [DELETE /auth/session](https://docs.medusajs.com/api/admin#auth_deletesession) +- [POST /auth/token/refresh](https://docs.medusajs.com/api/admin#auth_postadminauthtokenrefresh) +- [POST /auth/user/{auth_provider}](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_provider) +- [POST /auth/user/{auth_provider}/callback](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providercallback) +- [POST /auth/user/{auth_provider}/register](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_provider_register) +- [POST /auth/user/{auth_provider}/reset-password](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providerresetpassword) +- [POST /auth/user/{auth_provider}/update](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providerupdate) -## Don't Match Values -```ts -const posts = await postModuleService.listPosts({ - name: { - $nin: [ - "My Post", - ], - }, -}) -``` +## Store API Reference -To find records with a property that doesn't match one or more values, pass an object with a `$nin` property. Its value is an array of multiple values that a record's property shouldn't match. +- [POST /auth/customer/{auth_provider}](https://docs.medusajs.com/api/store#auth_postactor_typeauth_provider) +- [POST /auth/customer/{auth_provider}/callback](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providercallback) +- [POST /auth/customer/{auth_provider}/register](https://docs.medusajs.com/api/store#auth_postactor_typeauth_provider_register) +- [POST /auth/customer/{auth_provider}/reset-password](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerresetpassword) +- [POST /auth/customer/{auth_provider}/update](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerupdate) +- [POST /auth/session](https://docs.medusajs.com/api/store#auth_postsession) +- [DELETE /auth/session](https://docs.medusajs.com/api/store#auth_deletesession) +- [POST /auth/token/refresh](https://docs.medusajs.com/api/store#auth_postadminauthtokenrefresh) +- [POST /store/carts](https://docs.medusajs.com/api/store#carts_postcarts) +- [GET /store/carts/{id}](https://docs.medusajs.com/api/store#carts_getcartsid) +- [POST /store/carts/{id}](https://docs.medusajs.com/api/store#carts_postcartsid) +- [POST /store/carts/{id}/complete](https://docs.medusajs.com/api/store#carts_postcartsidcomplete) +- [POST /store/carts/{id}/customer](https://docs.medusajs.com/api/store#carts_postcartsidcustomer) +- [POST /store/carts/{id}/line-items](https://docs.medusajs.com/api/store#carts_postcartsidlineitems) +- [POST /store/carts/{id}/line-items/{line_id}](https://docs.medusajs.com/api/store#carts_postcartsidlineitemsline_id) +- [DELETE /store/carts/{id}/line-items/{line_id}](https://docs.medusajs.com/api/store#carts_deletecartsidlineitemsline_id) +- [POST /store/carts/{id}/promotions](https://docs.medusajs.com/api/store#carts_postcartsidpromotions) +- [DELETE /store/carts/{id}/promotions](https://docs.medusajs.com/api/store#carts_deletecartsidpromotions) +- [POST /store/carts/{id}/shipping-methods](https://docs.medusajs.com/api/store#carts_postcartsidshippingmethods) +- [POST /store/carts/{id}/taxes](https://docs.medusajs.com/api/store#carts_postcartsidtaxes) +- [GET /store/collections](https://docs.medusajs.com/api/store#collections_getcollections) +- [GET /store/collections/{id}](https://docs.medusajs.com/api/store#collections_getcollectionsid) +- [GET /store/currencies](https://docs.medusajs.com/api/store#currencies_getcurrencies) +- [GET /store/currencies/{code}](https://docs.medusajs.com/api/store#currencies_getcurrenciescode) +- [POST /store/customers](https://docs.medusajs.com/api/store#customers_postcustomers) +- [GET /store/customers/me](https://docs.medusajs.com/api/store#customers_getcustomersme) +- [POST /store/customers/me](https://docs.medusajs.com/api/store#customers_postcustomersme) +- [GET /store/customers/me/addresses](https://docs.medusajs.com/api/store#customers_getcustomersmeaddresses) +- [POST /store/customers/me/addresses](https://docs.medusajs.com/api/store#customers_postcustomersmeaddresses) +- [GET /store/customers/me/addresses/{address_id}](https://docs.medusajs.com/api/store#customers_getcustomersmeaddressesaddress_id) +- [POST /store/customers/me/addresses/{address_id}](https://docs.medusajs.com/api/store#customers_postcustomersmeaddressesaddress_id) +- [DELETE /store/customers/me/addresses/{address_id}](https://docs.medusajs.com/api/store#customers_deletecustomersmeaddressesaddress_id) +- [GET /store/orders](https://docs.medusajs.com/api/store#orders_getorders) +- [GET /store/orders/{id}](https://docs.medusajs.com/api/store#orders_getordersid) +- [POST /store/orders/{id}/transfer/accept](https://docs.medusajs.com/api/store#orders_postordersidtransferaccept) +- [POST /store/orders/{id}/transfer/cancel](https://docs.medusajs.com/api/store#orders_postordersidtransfercancel) +- [POST /store/orders/{id}/transfer/decline](https://docs.medusajs.com/api/store#orders_postordersidtransferdecline) +- [POST /store/orders/{id}/transfer/request](https://docs.medusajs.com/api/store#orders_postordersidtransferrequest) +- [POST /store/payment-collections](https://docs.medusajs.com/api/store#payment-collections_postpaymentcollections) +- [POST /store/payment-collections/{id}/payment-sessions](https://docs.medusajs.com/api/store#payment-collections_postpaymentcollectionsidpaymentsessions) +- [GET /store/payment-providers](https://docs.medusajs.com/api/store#payment-providers_getpaymentproviders) +- [GET /store/product-categories](https://docs.medusajs.com/api/store#product-categories_getproductcategories) +- [GET /store/product-categories/{id}](https://docs.medusajs.com/api/store#product-categories_getproductcategoriesid) +- [GET /store/product-tags](https://docs.medusajs.com/api/store#product-tags_getproducttags) +- [GET /store/product-tags/{id}](https://docs.medusajs.com/api/store#product-tags_getproducttagsid) +- [GET /store/product-types](https://docs.medusajs.com/api/store#product-types_getproducttypes) +- [GET /store/product-types/{id}](https://docs.medusajs.com/api/store#product-types_getproducttypesid) +- [GET /store/products](https://docs.medusajs.com/api/store#products_getproducts) +- [GET /store/products/{id}](https://docs.medusajs.com/api/store#products_getproductsid) +- [GET /store/regions](https://docs.medusajs.com/api/store#regions_getregions) +- [GET /store/regions/{id}](https://docs.medusajs.com/api/store#regions_getregionsid) +- [GET /store/return-reasons](https://docs.medusajs.com/api/store#return-reasons_getreturnreasons) +- [GET /store/return-reasons/{id}](https://docs.medusajs.com/api/store#return-reasons_getreturnreasonsid) +- [POST /store/return](https://docs.medusajs.com/api/store#return_postreturn) +- [GET /store/shipping-options](https://docs.medusajs.com/api/store#shipping-options_getshippingoptions) +- [POST /store/shipping-options/{id}/calculate](https://docs.medusajs.com/api/store#shipping-options_postshippingoptionsidcalculate) -In the example above, only posts that don't have the name `My Post` are retrieved. -*** +# Introduction -## Match Text Like Value +Primitives for building Medusa applications. -This filter only applies to text-like properties, including `id` and `enum` properties. +Welcome to Medusa UI, a React implementation of the Medusa design system. +It is a collection of components, hooks, utility functions, icons, and [Tailwind CSS](https://tailwindcss.com/) classes that can be used to build +a consistent user interface across the Medusa Admin and other Medusa applications. -```ts -const posts = await postModuleService.listPosts({ - name: { - $like: "My%", - }, -}) -``` +## Packages -To perform a `like` filter on a record's property, set the property's value to an object with a `$like` property. Its value is the string to use when applying the `like` filter. +*** -The example above matches all posts whose name starts with `My`. +Medusa UI is split into multiple packages. Each package is published to npm +and can be installed separately. -*** +- `@medusajs/ui` - React components, hooks, and utility functions used + in Medusa UI. +- `@medusajs/ui-preset` - Tailwind CSS preset containing all the classes + used in Medusa UI. +- `@medusajs/icons` - Icons used in Medusa UI. -## Apply Range Filters +## About -This filter only applies to the `number` and `dateTime` properties. +*** -```ts -const posts = await postModuleService.listPosts({ - published_at: { - $lt: new Date(), - }, -}) -``` +At its core, Medusa UI is a styled and slightly opinionated implementation of [Radix Primitives](https://www.radix-ui.com/primitives). +Our team have also referenced the fantastic [shadcn/ui](https://ui.shadcn.com/) for inspiration in certain implementations. -To filter a record's property to be within a range, set the property's value to an object with any of the following properties: +Our team strongly believe in keeping the components simple and +composable, much like Medusa's foundation. This allows you to build whatever you need. Our team have tried to avoid overloading +the component API and, instead, leveraged the native HTML API, which gets implemented +and respected accordingly, and passed to the underlying elements. -1. `$lt`: The property's value must be less than the supplied value. -2. `$lte`: The property's value must be less than or equal to the supplied value. -3. `$gt`: The property's value must be greater than the supplied value. -4. `$gte`: The property's value must be greater than or equal the supplied value. -In the example above, only posts whose `published_at` property is before the current date and time are retrieved. +# Installation -### Example: Retrieve Posts Published Today +How to install and setup Medusa UI. -```ts -const startToday = new Date() -startToday.setHours(0, 0, 0, 0) -const endToday = new Date() -endToday.setHours(23, 59, 59, 59) -const posts = await postModuleService.listPosts({ - published_at: { - $gte: startToday, - $lte: endToday, - }, -}) -``` +# Medusa Admin Extension -The `dateTime` property also stores the time. So, when matching for an exact day, you must set a range filter to be between the beginning and end of the day. +How to install and use Medusa UI for building Admin extensions. -In this example, you retrieve the current date twice: once to set its time to `00:00:00`, and another to set its time `23:59:59`. Then, you retrieve posts whose `published_at` property is between `00:00:00` and `23:59:59` of today. +## Installation *** -## Apply Or Condition +The `@medusajs/ui` package is a already installed as a dependency of the `@medusajs/admin` package. Due to this you can simply import the package and use it in your local Admin extensions. -```ts -const posts = await postModuleService.listPosts({ - $or: [ - { - name: "My Post", - }, - { - published_at: { - $lt: new Date(), - }, - }, - ], -}) +If you are building a Admin extension as part of a Medusa plugin, you can install the package as a dependency of your plugin. + +```bash +npm install @medusajs/ui ``` -To use an `or` condition, pass to the filter object the `$or` property, whose value is an array of filters. +## Configuration -In the example above, posts whose name is `My Post` or their `published_at` date is less than the current date and time are retrieved. +*** +The configuration of the UI package is handled by the `@medusajs/admin` package. Therefore, you do not need to any additional configuration to use the UI package in your Admin extensions. +# Standalone Project -

Just Getting Started?

+How to install and use Medusa UI in a standalone project. -Check out the [Medusa v2 Documentation](undefined/docs). +## Installation -

Medusa JS SDK

+*** -To use Medusa's JS SDK library, install the following packages in your project (not required for admin customizations): +Medusa UI is a React UI library and while it's intended for usage within Medusa projects, it can also be used in any React project. -```bash -npm install @medusajs/js-sdk@latest @medusajs/types@latest -``` +### Install Medusa UI -Learn more about the JS SDK and how to configure it in [this documentation](undefined/resources/js-sdk). +Install the React UI library with the following command: -### Download Full Reference +```bash +npm install @medusajs/ui +``` -Download this reference as an OpenApi YAML file. You can import this file to tools like Postman and start sending requests directly to your Medusa application. +### Configuring Tailwind CSS +The components are styled using Tailwind CSS, and in order to use them, you will need to install Tailwind CSS in your project as well. +For more information on how to install Tailwind CSS, please refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs/installation). +All of the classes used for Medusa UI are shipped as a Tailwind CSS customization. +You can install it with the following command: +```bash +npm install @medusajs/ui-preset +``` -## Admin API Reference +After you have installed Tailwind CSS and the Medusa UI preset, you need to add the following to your `tailwind.config.js`file: -- [GET /admin/api-keys](undefined/api/admin#api-keys_getapikeys) -- [POST /admin/api-keys](undefined/api/admin#api-keys_postapikeys) -- [GET /admin/api-keys/{id}](undefined/api/admin#api-keys_getapikeysid) -- [POST /admin/api-keys/{id}](undefined/api/admin#api-keys_postapikeysid) -- [DELETE /admin/api-keys/{id}](undefined/api/admin#api-keys_deleteapikeysid) -- [POST /admin/api-keys/{id}/revoke](undefined/api/admin#api-keys_postapikeysidrevoke) -- [POST /admin/api-keys/{id}/sales-channels](undefined/api/admin#api-keys_postapikeysidsaleschannels) -- [GET /admin/campaigns](undefined/api/admin#campaigns_getcampaigns) -- [POST /admin/campaigns](undefined/api/admin#campaigns_postcampaigns) -- [GET /admin/campaigns/{id}](undefined/api/admin#campaigns_getcampaignsid) -- [POST /admin/campaigns/{id}](undefined/api/admin#campaigns_postcampaignsid) -- [DELETE /admin/campaigns/{id}](undefined/api/admin#campaigns_deletecampaignsid) -- [POST /admin/campaigns/{id}/promotions](undefined/api/admin#campaigns_postcampaignsidpromotions) -- [GET /admin/claims](undefined/api/admin#claims_getclaims) -- [POST /admin/claims](undefined/api/admin#claims_postclaims) -- [GET /admin/claims/{id}](undefined/api/admin#claims_getclaimsid) -- [POST /admin/claims/{id}/cancel](undefined/api/admin#claims_postclaimsidcancel) -- [POST /admin/claims/{id}/claim-items](undefined/api/admin#claims_postclaimsidclaimitems) -- [POST /admin/claims/{id}/claim-items/{action_id}](undefined/api/admin#claims_postclaimsidclaimitemsaction_id) -- [DELETE /admin/claims/{id}/claim-items/{action_id}](undefined/api/admin#claims_deleteclaimsidclaimitemsaction_id) -- [POST /admin/claims/{id}/inbound/items](undefined/api/admin#claims_postclaimsidinbounditems) -- [POST /admin/claims/{id}/inbound/items/{action_id}](undefined/api/admin#claims_postclaimsidinbounditemsaction_id) -- [DELETE /admin/claims/{id}/inbound/items/{action_id}](undefined/api/admin#claims_deleteclaimsidinbounditemsaction_id) -- [POST /admin/claims/{id}/inbound/shipping-method](undefined/api/admin#claims_postclaimsidinboundshippingmethod) -- [POST /admin/claims/{id}/inbound/shipping-method/{action_id}](undefined/api/admin#claims_postclaimsidinboundshippingmethodaction_id) -- [DELETE /admin/claims/{id}/inbound/shipping-method/{action_id}](undefined/api/admin#claims_deleteclaimsidinboundshippingmethodaction_id) -- [POST /admin/claims/{id}/outbound/items](undefined/api/admin#claims_postclaimsidoutbounditems) -- [POST /admin/claims/{id}/outbound/items/{action_id}](undefined/api/admin#claims_postclaimsidoutbounditemsaction_id) -- [DELETE /admin/claims/{id}/outbound/items/{action_id}](undefined/api/admin#claims_deleteclaimsidoutbounditemsaction_id) -- [POST /admin/claims/{id}/outbound/shipping-method](undefined/api/admin#claims_postclaimsidoutboundshippingmethod) -- [POST /admin/claims/{id}/outbound/shipping-method/{action_id}](undefined/api/admin#claims_postclaimsidoutboundshippingmethodaction_id) -- [DELETE /admin/claims/{id}/outbound/shipping-method/{action_id}](undefined/api/admin#claims_deleteclaimsidoutboundshippingmethodaction_id) -- [POST /admin/claims/{id}/request](undefined/api/admin#claims_postclaimsidrequest) -- [DELETE /admin/claims/{id}/request](undefined/api/admin#claims_deleteclaimsidrequest) -- [GET /admin/collections](undefined/api/admin#collections_getcollections) -- [POST /admin/collections](undefined/api/admin#collections_postcollections) -- [GET /admin/collections/{id}](undefined/api/admin#collections_getcollectionsid) -- [POST /admin/collections/{id}](undefined/api/admin#collections_postcollectionsid) -- [DELETE /admin/collections/{id}](undefined/api/admin#collections_deletecollectionsid) -- [POST /admin/collections/{id}/products](undefined/api/admin#collections_postcollectionsidproducts) -- [GET /admin/currencies](undefined/api/admin#currencies_getcurrencies) -- [GET /admin/currencies/{code}](undefined/api/admin#currencies_getcurrenciescode) -- [GET /admin/customer-groups](undefined/api/admin#customer-groups_getcustomergroups) -- [POST /admin/customer-groups](undefined/api/admin#customer-groups_postcustomergroups) -- [GET /admin/customer-groups/{id}](undefined/api/admin#customer-groups_getcustomergroupsid) -- [POST /admin/customer-groups/{id}](undefined/api/admin#customer-groups_postcustomergroupsid) -- [DELETE /admin/customer-groups/{id}](undefined/api/admin#customer-groups_deletecustomergroupsid) -- [POST /admin/customer-groups/{id}/customers](undefined/api/admin#customer-groups_postcustomergroupsidcustomers) -- [GET /admin/customers](undefined/api/admin#customers_getcustomers) -- [POST /admin/customers](undefined/api/admin#customers_postcustomers) -- [GET /admin/customers/{id}](undefined/api/admin#customers_getcustomersid) -- [POST /admin/customers/{id}](undefined/api/admin#customers_postcustomersid) -- [DELETE /admin/customers/{id}](undefined/api/admin#customers_deletecustomersid) -- [GET /admin/customers/{id}/addresses](undefined/api/admin#customers_getcustomersidaddresses) -- [POST /admin/customers/{id}/addresses](undefined/api/admin#customers_postcustomersidaddresses) -- [GET /admin/customers/{id}/addresses/{address_id}](undefined/api/admin#customers_getcustomersidaddressesaddress_id) -- [POST /admin/customers/{id}/addresses/{address_id}](undefined/api/admin#customers_postcustomersidaddressesaddress_id) -- [DELETE /admin/customers/{id}/addresses/{address_id}](undefined/api/admin#customers_deletecustomersidaddressesaddress_id) -- [POST /admin/customers/{id}/customer-groups](undefined/api/admin#customers_postcustomersidcustomergroups) -- [GET /admin/draft-orders](undefined/api/admin#draft-orders_getdraftorders) -- [POST /admin/draft-orders](undefined/api/admin#draft-orders_postdraftorders) -- [GET /admin/draft-orders/{id}](undefined/api/admin#draft-orders_getdraftordersid) -- [GET /admin/exchanges](undefined/api/admin#exchanges_getexchanges) -- [POST /admin/exchanges](undefined/api/admin#exchanges_postexchanges) -- [GET /admin/exchanges/{id}](undefined/api/admin#exchanges_getexchangesid) -- [POST /admin/exchanges/{id}/cancel](undefined/api/admin#exchanges_postexchangesidcancel) -- [POST /admin/exchanges/{id}/inbound/items](undefined/api/admin#exchanges_postexchangesidinbounditems) -- [POST /admin/exchanges/{id}/inbound/items/{action_id}](undefined/api/admin#exchanges_postexchangesidinbounditemsaction_id) -- [DELETE /admin/exchanges/{id}/inbound/items/{action_id}](undefined/api/admin#exchanges_deleteexchangesidinbounditemsaction_id) -- [POST /admin/exchanges/{id}/inbound/shipping-method](undefined/api/admin#exchanges_postexchangesidinboundshippingmethod) -- [POST /admin/exchanges/{id}/inbound/shipping-method/{action_id}](undefined/api/admin#exchanges_postexchangesidinboundshippingmethodaction_id) -- [DELETE /admin/exchanges/{id}/inbound/shipping-method/{action_id}](undefined/api/admin#exchanges_deleteexchangesidinboundshippingmethodaction_id) -- [POST /admin/exchanges/{id}/outbound/items](undefined/api/admin#exchanges_postexchangesidoutbounditems) -- [POST /admin/exchanges/{id}/outbound/items/{action_id}](undefined/api/admin#exchanges_postexchangesidoutbounditemsaction_id) -- [DELETE /admin/exchanges/{id}/outbound/items/{action_id}](undefined/api/admin#exchanges_deleteexchangesidoutbounditemsaction_id) -- [POST /admin/exchanges/{id}/outbound/shipping-method](undefined/api/admin#exchanges_postexchangesidoutboundshippingmethod) -- [POST /admin/exchanges/{id}/outbound/shipping-method/{action_id}](undefined/api/admin#exchanges_postexchangesidoutboundshippingmethodaction_id) -- [DELETE /admin/exchanges/{id}/outbound/shipping-method/{action_id}](undefined/api/admin#exchanges_deleteexchangesidoutboundshippingmethodaction_id) -- [POST /admin/exchanges/{id}/request](undefined/api/admin#exchanges_postexchangesidrequest) -- [DELETE /admin/exchanges/{id}/request](undefined/api/admin#exchanges_deleteexchangesidrequest) -- [GET /admin/fulfillment-providers](undefined/api/admin#fulfillment-providers_getfulfillmentproviders) -- [GET /admin/fulfillment-providers/{id}/options](undefined/api/admin#fulfillment-providers_getfulfillmentprovidersidoptions) -- [DELETE /admin/fulfillment-sets/{id}](undefined/api/admin#fulfillment-sets_deletefulfillmentsetsid) -- [POST /admin/fulfillment-sets/{id}/service-zones](undefined/api/admin#fulfillment-sets_postfulfillmentsetsidservicezones) -- [GET /admin/fulfillment-sets/{id}/service-zones/{zone_id}](undefined/api/admin#fulfillment-sets_getfulfillmentsetsidservicezoneszone_id) -- [POST /admin/fulfillment-sets/{id}/service-zones/{zone_id}](undefined/api/admin#fulfillment-sets_postfulfillmentsetsidservicezoneszone_id) -- [DELETE /admin/fulfillment-sets/{id}/service-zones/{zone_id}](undefined/api/admin#fulfillment-sets_deletefulfillmentsetsidservicezoneszone_id) -- [POST /admin/fulfillments](undefined/api/admin#fulfillments_postfulfillments) -- [POST /admin/fulfillments/{id}/cancel](undefined/api/admin#fulfillments_postfulfillmentsidcancel) -- [POST /admin/fulfillments/{id}/shipment](undefined/api/admin#fulfillments_postfulfillmentsidshipment) -- [GET /admin/inventory-items](undefined/api/admin#inventory-items_getinventoryitems) -- [POST /admin/inventory-items](undefined/api/admin#inventory-items_postinventoryitems) -- [POST /admin/inventory-items/location-levels/batch](undefined/api/admin#inventory-items_postinventoryitemslocationlevelsbatch) -- [GET /admin/inventory-items/{id}](undefined/api/admin#inventory-items_getinventoryitemsid) -- [POST /admin/inventory-items/{id}](undefined/api/admin#inventory-items_postinventoryitemsid) -- [DELETE /admin/inventory-items/{id}](undefined/api/admin#inventory-items_deleteinventoryitemsid) -- [GET /admin/inventory-items/{id}/location-levels](undefined/api/admin#inventory-items_getinventoryitemsidlocationlevels) -- [POST /admin/inventory-items/{id}/location-levels](undefined/api/admin#inventory-items_postinventoryitemsidlocationlevels) -- [POST /admin/inventory-items/{id}/location-levels/batch](undefined/api/admin#inventory-items_postinventoryitemsidlocationlevelsbatch) -- [POST /admin/inventory-items/{id}/location-levels/{location_id}](undefined/api/admin#inventory-items_postinventoryitemsidlocationlevelslocation_id) -- [DELETE /admin/inventory-items/{id}/location-levels/{location_id}](undefined/api/admin#inventory-items_deleteinventoryitemsidlocationlevelslocation_id) -- [GET /admin/invites](undefined/api/admin#invites_getinvites) -- [POST /admin/invites](undefined/api/admin#invites_postinvites) -- [POST /admin/invites/accept](undefined/api/admin#invites_postinvitesaccept) -- [GET /admin/invites/{id}](undefined/api/admin#invites_getinvitesid) -- [DELETE /admin/invites/{id}](undefined/api/admin#invites_deleteinvitesid) -- [POST /admin/invites/{id}/resend](undefined/api/admin#invites_postinvitesidresend) -- [GET /admin/notifications](undefined/api/admin#notifications_getnotifications) -- [GET /admin/notifications/{id}](undefined/api/admin#notifications_getnotificationsid) -- [POST /admin/order-edits](undefined/api/admin#order-edits_postorderedits) -- [DELETE /admin/order-edits/{id}](undefined/api/admin#order-edits_deleteordereditsid) -- [POST /admin/order-edits/{id}/confirm](undefined/api/admin#order-edits_postordereditsidconfirm) -- [POST /admin/order-edits/{id}/items](undefined/api/admin#order-edits_postordereditsiditems) -- [POST /admin/order-edits/{id}/items/item/{item_id}](undefined/api/admin#order-edits_postordereditsiditemsitemitem_id) -- [POST /admin/order-edits/{id}/items/{action_id}](undefined/api/admin#order-edits_postordereditsiditemsaction_id) -- [DELETE /admin/order-edits/{id}/items/{action_id}](undefined/api/admin#order-edits_deleteordereditsiditemsaction_id) -- [POST /admin/order-edits/{id}/request](undefined/api/admin#order-edits_postordereditsidrequest) -- [POST /admin/order-edits/{id}/shipping-method](undefined/api/admin#order-edits_postordereditsidshippingmethod) -- [POST /admin/order-edits/{id}/shipping-method/{action_id}](undefined/api/admin#order-edits_postordereditsidshippingmethodaction_id) -- [DELETE /admin/order-edits/{id}/shipping-method/{action_id}](undefined/api/admin#order-edits_deleteordereditsidshippingmethodaction_id) -- [GET /admin/orders](undefined/api/admin#orders_getorders) -- [GET /admin/orders/{id}](undefined/api/admin#orders_getordersid) -- [POST /admin/orders/{id}](undefined/api/admin#orders_postordersid) -- [POST /admin/orders/{id}/archive](undefined/api/admin#orders_postordersidarchive) -- [POST /admin/orders/{id}/cancel](undefined/api/admin#orders_postordersidcancel) -- [GET /admin/orders/{id}/changes](undefined/api/admin#orders_getordersidchanges) -- [POST /admin/orders/{id}/complete](undefined/api/admin#orders_postordersidcomplete) -- [POST /admin/orders/{id}/fulfillments](undefined/api/admin#orders_postordersidfulfillments) -- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/cancel](undefined/api/admin#orders_postordersidfulfillmentsfulfillment_idcancel) -- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/mark-as-delivered](undefined/api/admin#orders_postordersidfulfillmentsfulfillment_idmarkasdelivered) -- [POST /admin/orders/{id}/fulfillments/{fulfillment_id}/shipments](undefined/api/admin#orders_postordersidfulfillmentsfulfillment_idshipments) -- [GET /admin/orders/{id}/line-items](undefined/api/admin#orders_getordersidlineitems) -- [GET /admin/orders/{id}/preview](undefined/api/admin#orders_getordersidpreview) -- [POST /admin/orders/{id}/transfer](undefined/api/admin#orders_postordersidtransfer) -- [POST /admin/orders/{id}/transfer/cancel](undefined/api/admin#orders_postordersidtransfercancel) -- [POST /admin/payment-collections](undefined/api/admin#payment-collections_postpaymentcollections) -- [DELETE /admin/payment-collections/{id}](undefined/api/admin#payment-collections_deletepaymentcollectionsid) -- [POST /admin/payment-collections/{id}/mark-as-paid](undefined/api/admin#payment-collections_postpaymentcollectionsidmarkaspaid) -- [GET /admin/payments](undefined/api/admin#payments_getpayments) -- [GET /admin/payments/payment-providers](undefined/api/admin#payments_getpaymentspaymentproviders) -- [GET /admin/payments/{id}](undefined/api/admin#payments_getpaymentsid) -- [POST /admin/payments/{id}/capture](undefined/api/admin#payments_postpaymentsidcapture) -- [POST /admin/payments/{id}/refund](undefined/api/admin#payments_postpaymentsidrefund) -- [GET /admin/price-lists](undefined/api/admin#price-lists_getpricelists) -- [POST /admin/price-lists](undefined/api/admin#price-lists_postpricelists) -- [GET /admin/price-lists/{id}](undefined/api/admin#price-lists_getpricelistsid) -- [POST /admin/price-lists/{id}](undefined/api/admin#price-lists_postpricelistsid) -- [DELETE /admin/price-lists/{id}](undefined/api/admin#price-lists_deletepricelistsid) -- [POST /admin/price-lists/{id}/prices/batch](undefined/api/admin#price-lists_postpricelistsidpricesbatch) -- [POST /admin/price-lists/{id}/products](undefined/api/admin#price-lists_postpricelistsidproducts) -- [GET /admin/price-preferences](undefined/api/admin#price-preferences_getpricepreferences) -- [POST /admin/price-preferences](undefined/api/admin#price-preferences_postpricepreferences) -- [GET /admin/price-preferences/{id}](undefined/api/admin#price-preferences_getpricepreferencesid) -- [POST /admin/price-preferences/{id}](undefined/api/admin#price-preferences_postpricepreferencesid) -- [DELETE /admin/price-preferences/{id}](undefined/api/admin#price-preferences_deletepricepreferencesid) -- [GET /admin/product-categories](undefined/api/admin#product-categories_getproductcategories) -- [POST /admin/product-categories](undefined/api/admin#product-categories_postproductcategories) -- [GET /admin/product-categories/{id}](undefined/api/admin#product-categories_getproductcategoriesid) -- [POST /admin/product-categories/{id}](undefined/api/admin#product-categories_postproductcategoriesid) -- [DELETE /admin/product-categories/{id}](undefined/api/admin#product-categories_deleteproductcategoriesid) -- [POST /admin/product-categories/{id}/products](undefined/api/admin#product-categories_postproductcategoriesidproducts) -- [GET /admin/product-tags](undefined/api/admin#product-tags_getproducttags) -- [POST /admin/product-tags](undefined/api/admin#product-tags_postproducttags) -- [GET /admin/product-tags/{id}](undefined/api/admin#product-tags_getproducttagsid) -- [POST /admin/product-tags/{id}](undefined/api/admin#product-tags_postproducttagsid) -- [DELETE /admin/product-tags/{id}](undefined/api/admin#product-tags_deleteproducttagsid) -- [GET /admin/product-types](undefined/api/admin#product-types_getproducttypes) -- [POST /admin/product-types](undefined/api/admin#product-types_postproducttypes) -- [GET /admin/product-types/{id}](undefined/api/admin#product-types_getproducttypesid) -- [POST /admin/product-types/{id}](undefined/api/admin#product-types_postproducttypesid) -- [DELETE /admin/product-types/{id}](undefined/api/admin#product-types_deleteproducttypesid) -- [GET /admin/product-variants](undefined/api/admin#product-variants_getproductvariants) -- [GET /admin/products](undefined/api/admin#products_getproducts) -- [POST /admin/products](undefined/api/admin#products_postproducts) -- [POST /admin/products/batch](undefined/api/admin#products_postproductsbatch) -- [POST /admin/products/export](undefined/api/admin#products_postproductsexport) -- [POST /admin/products/import](undefined/api/admin#products_postproductsimport) -- [POST /admin/products/import/{transaction_id}/confirm](undefined/api/admin#products_postproductsimporttransaction_idconfirm) -- [GET /admin/products/{id}](undefined/api/admin#products_getproductsid) -- [POST /admin/products/{id}](undefined/api/admin#products_postproductsid) -- [DELETE /admin/products/{id}](undefined/api/admin#products_deleteproductsid) -- [GET /admin/products/{id}/options](undefined/api/admin#products_getproductsidoptions) -- [POST /admin/products/{id}/options](undefined/api/admin#products_postproductsidoptions) -- [GET /admin/products/{id}/options/{option_id}](undefined/api/admin#products_getproductsidoptionsoption_id) -- [POST /admin/products/{id}/options/{option_id}](undefined/api/admin#products_postproductsidoptionsoption_id) -- [DELETE /admin/products/{id}/options/{option_id}](undefined/api/admin#products_deleteproductsidoptionsoption_id) -- [GET /admin/products/{id}/variants](undefined/api/admin#products_getproductsidvariants) -- [POST /admin/products/{id}/variants](undefined/api/admin#products_postproductsidvariants) -- [POST /admin/products/{id}/variants/batch](undefined/api/admin#products_postproductsidvariantsbatch) -- [POST /admin/products/{id}/variants/inventory-items/batch](undefined/api/admin#products_postproductsidvariantsinventoryitemsbatch) -- [GET /admin/products/{id}/variants/{variant_id}](undefined/api/admin#products_getproductsidvariantsvariant_id) -- [POST /admin/products/{id}/variants/{variant_id}](undefined/api/admin#products_postproductsidvariantsvariant_id) -- [DELETE /admin/products/{id}/variants/{variant_id}](undefined/api/admin#products_deleteproductsidvariantsvariant_id) -- [POST /admin/products/{id}/variants/{variant_id}/inventory-items](undefined/api/admin#products_postproductsidvariantsvariant_idinventoryitems) -- [POST /admin/products/{id}/variants/{variant_id}/inventory-items/{inventory_item_id}](undefined/api/admin#products_postproductsidvariantsvariant_idinventoryitemsinventory_item_id) -- [DELETE /admin/products/{id}/variants/{variant_id}/inventory-items/{inventory_item_id}](undefined/api/admin#products_deleteproductsidvariantsvariant_idinventoryitemsinventory_item_id) -- [GET /admin/promotions](undefined/api/admin#promotions_getpromotions) -- [POST /admin/promotions](undefined/api/admin#promotions_postpromotions) -- [GET /admin/promotions/rule-attribute-options/{rule_type}](undefined/api/admin#promotions_getpromotionsruleattributeoptionsrule_type) -- [GET /admin/promotions/rule-value-options/{rule_type}/{rule_attribute_id}](undefined/api/admin#promotions_getpromotionsrulevalueoptionsrule_typerule_attribute_id) -- [GET /admin/promotions/{id}](undefined/api/admin#promotions_getpromotionsid) -- [POST /admin/promotions/{id}](undefined/api/admin#promotions_postpromotionsid) -- [DELETE /admin/promotions/{id}](undefined/api/admin#promotions_deletepromotionsid) -- [POST /admin/promotions/{id}/buy-rules/batch](undefined/api/admin#promotions_postpromotionsidbuyrulesbatch) -- [POST /admin/promotions/{id}/rules/batch](undefined/api/admin#promotions_postpromotionsidrulesbatch) -- [POST /admin/promotions/{id}/target-rules/batch](undefined/api/admin#promotions_postpromotionsidtargetrulesbatch) -- [GET /admin/promotions/{id}/{rule_type}](undefined/api/admin#promotions_getpromotionsidrule_type) -- [GET /admin/refund-reasons](undefined/api/admin#refund-reasons_getrefundreasons) -- [POST /admin/refund-reasons](undefined/api/admin#refund-reasons_postrefundreasons) -- [GET /admin/refund-reasons/{id}](undefined/api/admin#refund-reasons_getrefundreasonsid) -- [POST /admin/refund-reasons/{id}](undefined/api/admin#refund-reasons_postrefundreasonsid) -- [DELETE /admin/refund-reasons/{id}](undefined/api/admin#refund-reasons_deleterefundreasonsid) -- [GET /admin/regions](undefined/api/admin#regions_getregions) -- [POST /admin/regions](undefined/api/admin#regions_postregions) -- [GET /admin/regions/{id}](undefined/api/admin#regions_getregionsid) -- [POST /admin/regions/{id}](undefined/api/admin#regions_postregionsid) -- [DELETE /admin/regions/{id}](undefined/api/admin#regions_deleteregionsid) -- [GET /admin/reservations](undefined/api/admin#reservations_getreservations) -- [POST /admin/reservations](undefined/api/admin#reservations_postreservations) -- [GET /admin/reservations/{id}](undefined/api/admin#reservations_getreservationsid) -- [POST /admin/reservations/{id}](undefined/api/admin#reservations_postreservationsid) -- [DELETE /admin/reservations/{id}](undefined/api/admin#reservations_deletereservationsid) -- [GET /admin/return-reasons](undefined/api/admin#return-reasons_getreturnreasons) -- [POST /admin/return-reasons](undefined/api/admin#return-reasons_postreturnreasons) -- [GET /admin/return-reasons/{id}](undefined/api/admin#return-reasons_getreturnreasonsid) -- [POST /admin/return-reasons/{id}](undefined/api/admin#return-reasons_postreturnreasonsid) -- [DELETE /admin/return-reasons/{id}](undefined/api/admin#return-reasons_deletereturnreasonsid) -- [GET /admin/returns](undefined/api/admin#returns_getreturns) -- [POST /admin/returns](undefined/api/admin#returns_postreturns) -- [GET /admin/returns/{id}](undefined/api/admin#returns_getreturnsid) -- [POST /admin/returns/{id}](undefined/api/admin#returns_postreturnsid) -- [POST /admin/returns/{id}/cancel](undefined/api/admin#returns_postreturnsidcancel) -- [POST /admin/returns/{id}/dismiss-items](undefined/api/admin#returns_postreturnsiddismissitems) -- [POST /admin/returns/{id}/dismiss-items/{action_id}](undefined/api/admin#returns_postreturnsiddismissitemsaction_id) -- [DELETE /admin/returns/{id}/dismiss-items/{action_id}](undefined/api/admin#returns_deletereturnsiddismissitemsaction_id) -- [POST /admin/returns/{id}/receive-items](undefined/api/admin#returns_postreturnsidreceiveitems) -- [POST /admin/returns/{id}/receive-items/{action_id}](undefined/api/admin#returns_postreturnsidreceiveitemsaction_id) -- [DELETE /admin/returns/{id}/receive-items/{action_id}](undefined/api/admin#returns_deletereturnsidreceiveitemsaction_id) -- [POST /admin/returns/{id}/receive](undefined/api/admin#returns_postreturnsidreceive) -- [DELETE /admin/returns/{id}/receive](undefined/api/admin#returns_deletereturnsidreceive) -- [POST /admin/returns/{id}/receive/confirm](undefined/api/admin#returns_postreturnsidreceiveconfirm) -- [POST /admin/returns/{id}/request-items](undefined/api/admin#returns_postreturnsidrequestitems) -- [POST /admin/returns/{id}/request-items/{action_id}](undefined/api/admin#returns_postreturnsidrequestitemsaction_id) -- [DELETE /admin/returns/{id}/request-items/{action_id}](undefined/api/admin#returns_deletereturnsidrequestitemsaction_id) -- [POST /admin/returns/{id}/request](undefined/api/admin#returns_postreturnsidrequest) -- [DELETE /admin/returns/{id}/request](undefined/api/admin#returns_deletereturnsidrequest) -- [POST /admin/returns/{id}/shipping-method](undefined/api/admin#returns_postreturnsidshippingmethod) -- [POST /admin/returns/{id}/shipping-method/{action_id}](undefined/api/admin#returns_postreturnsidshippingmethodaction_id) -- [DELETE /admin/returns/{id}/shipping-method/{action_id}](undefined/api/admin#returns_deletereturnsidshippingmethodaction_id) -- [GET /admin/sales-channels](undefined/api/admin#sales-channels_getsaleschannels) -- [POST /admin/sales-channels](undefined/api/admin#sales-channels_postsaleschannels) -- [GET /admin/sales-channels/{id}](undefined/api/admin#sales-channels_getsaleschannelsid) -- [POST /admin/sales-channels/{id}](undefined/api/admin#sales-channels_postsaleschannelsid) -- [DELETE /admin/sales-channels/{id}](undefined/api/admin#sales-channels_deletesaleschannelsid) -- [POST /admin/sales-channels/{id}/products](undefined/api/admin#sales-channels_postsaleschannelsidproducts) -- [GET /admin/shipping-options](undefined/api/admin#shipping-options_getshippingoptions) -- [POST /admin/shipping-options](undefined/api/admin#shipping-options_postshippingoptions) -- [GET /admin/shipping-options/{id}](undefined/api/admin#shipping-options_getshippingoptionsid) -- [POST /admin/shipping-options/{id}](undefined/api/admin#shipping-options_postshippingoptionsid) -- [DELETE /admin/shipping-options/{id}](undefined/api/admin#shipping-options_deleteshippingoptionsid) -- [POST /admin/shipping-options/{id}/rules/batch](undefined/api/admin#shipping-options_postshippingoptionsidrulesbatch) -- [GET /admin/shipping-profiles](undefined/api/admin#shipping-profiles_getshippingprofiles) -- [POST /admin/shipping-profiles](undefined/api/admin#shipping-profiles_postshippingprofiles) -- [GET /admin/shipping-profiles/{id}](undefined/api/admin#shipping-profiles_getshippingprofilesid) -- [POST /admin/shipping-profiles/{id}](undefined/api/admin#shipping-profiles_postshippingprofilesid) -- [DELETE /admin/shipping-profiles/{id}](undefined/api/admin#shipping-profiles_deleteshippingprofilesid) -- [GET /admin/stock-locations](undefined/api/admin#stock-locations_getstocklocations) -- [POST /admin/stock-locations](undefined/api/admin#stock-locations_poststocklocations) -- [GET /admin/stock-locations/{id}](undefined/api/admin#stock-locations_getstocklocationsid) -- [POST /admin/stock-locations/{id}](undefined/api/admin#stock-locations_poststocklocationsid) -- [DELETE /admin/stock-locations/{id}](undefined/api/admin#stock-locations_deletestocklocationsid) -- [POST /admin/stock-locations/{id}/fulfillment-providers](undefined/api/admin#stock-locations_poststocklocationsidfulfillmentproviders) -- [POST /admin/stock-locations/{id}/fulfillment-sets](undefined/api/admin#stock-locations_poststocklocationsidfulfillmentsets) -- [POST /admin/stock-locations/{id}/sales-channels](undefined/api/admin#stock-locations_poststocklocationsidsaleschannels) -- [GET /admin/stores](undefined/api/admin#stores_getstores) -- [GET /admin/stores/{id}](undefined/api/admin#stores_getstoresid) -- [POST /admin/stores/{id}](undefined/api/admin#stores_poststoresid) -- [GET /admin/tax-rates](undefined/api/admin#tax-rates_gettaxrates) -- [POST /admin/tax-rates](undefined/api/admin#tax-rates_posttaxrates) -- [GET /admin/tax-rates/{id}](undefined/api/admin#tax-rates_gettaxratesid) -- [POST /admin/tax-rates/{id}](undefined/api/admin#tax-rates_posttaxratesid) -- [DELETE /admin/tax-rates/{id}](undefined/api/admin#tax-rates_deletetaxratesid) -- [POST /admin/tax-rates/{id}/rules](undefined/api/admin#tax-rates_posttaxratesidrules) -- [DELETE /admin/tax-rates/{id}/rules/{rule_id}](undefined/api/admin#tax-rates_deletetaxratesidrulesrule_id) -- [GET /admin/tax-regions](undefined/api/admin#tax-regions_gettaxregions) -- [POST /admin/tax-regions](undefined/api/admin#tax-regions_posttaxregions) -- [GET /admin/tax-regions/{id}](undefined/api/admin#tax-regions_gettaxregionsid) -- [POST /admin/tax-regions/{id}](undefined/api/admin#tax-regions_posttaxregionsid) -- [DELETE /admin/tax-regions/{id}](undefined/api/admin#tax-regions_deletetaxregionsid) -- [POST /admin/uploads](undefined/api/admin#uploads_postuploads) -- [GET /admin/uploads/{id}](undefined/api/admin#uploads_getuploadsid) -- [DELETE /admin/uploads/{id}](undefined/api/admin#uploads_deleteuploadsid) -- [GET /admin/users](undefined/api/admin#users_getusers) -- [GET /admin/users/me](undefined/api/admin#users_getusersme) -- [GET /admin/users/{id}](undefined/api/admin#users_getusersid) -- [POST /admin/users/{id}](undefined/api/admin#users_postusersid) -- [DELETE /admin/users/{id}](undefined/api/admin#users_deleteusersid) -- [GET /admin/workflows-executions](undefined/api/admin#workflows-executions_getworkflowsexecutions) -- [GET /admin/workflows-executions/{id}](undefined/api/admin#workflows-executions_getworkflowsexecutionsid) -- [POST /admin/workflows-executions/{workflow_id}/run](undefined/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idrun) -- [POST /admin/workflows-executions/{workflow_id}/steps/failure](undefined/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idstepsfailure) -- [POST /admin/workflows-executions/{workflow_id}/steps/success](undefined/api/admin#workflows-executions_postworkflowsexecutionsworkflow_idstepssuccess) -- [GET /admin/workflows-executions/{workflow_id}/subscribe](undefined/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idsubscribe) -- [GET /admin/workflows-executions/{workflow_id}/{transaction_id}](undefined/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idtransaction_id) -- [GET /admin/workflows-executions/{workflow_id}/{transaction_id}/{step_id}/subscribe](undefined/api/admin#workflows-executions_getworkflowsexecutionsworkflow_idtransaction_idstep_idsubscribe) -- [POST /auth/session](undefined/api/admin#auth_postsession) -- [DELETE /auth/session](undefined/api/admin#auth_deletesession) -- [POST /auth/token/refresh](undefined/api/admin#auth_postadminauthtokenrefresh) -- [POST /auth/user/{auth_provider}](undefined/api/admin#auth_postactor_typeauth_provider) -- [POST /auth/user/{auth_provider}/callback](undefined/api/admin#auth_postactor_typeauth_providercallback) -- [POST /auth/user/{auth_provider}/register](undefined/api/admin#auth_postactor_typeauth_provider_register) -- [POST /auth/user/{auth_provider}/reset-password](undefined/api/admin#auth_postactor_typeauth_providerresetpassword) -- [POST /auth/user/{auth_provider}/update](undefined/api/admin#auth_postactor_typeauth_providerupdate) +```tsx +module.exports = { + presets: [require("@medusajs/ui-preset")], + // ... +} +``` +In order for the styles to be applied correctly to the components, you will also need to ensure that +`@medusajs/ui` is included in the content field of your `tailwind.config.js` file: -## Store API Reference +```tsx +module.exports = { + content: [ + // ... + "./node_modules/@medusajs/ui/dist/**/*.{js,jsx,ts,tsx}", + ], + // ... +} +``` -- [POST /auth/customer/{auth_provider}](undefined/api/store#auth_postactor_typeauth_provider) -- [POST /auth/customer/{auth_provider}/callback](undefined/api/store#auth_postactor_typeauth_providercallback) -- [POST /auth/customer/{auth_provider}/register](undefined/api/store#auth_postactor_typeauth_provider_register) -- [POST /auth/customer/{auth_provider}/reset-password](undefined/api/store#auth_postactor_typeauth_providerresetpassword) -- [POST /auth/customer/{auth_provider}/update](undefined/api/store#auth_postactor_typeauth_providerupdate) -- [POST /auth/session](undefined/api/store#auth_postsession) -- [DELETE /auth/session](undefined/api/store#auth_deletesession) -- [POST /auth/token/refresh](undefined/api/store#auth_postadminauthtokenrefresh) -- [POST /store/carts](undefined/api/store#carts_postcarts) -- [GET /store/carts/{id}](undefined/api/store#carts_getcartsid) -- [POST /store/carts/{id}](undefined/api/store#carts_postcartsid) -- [POST /store/carts/{id}/complete](undefined/api/store#carts_postcartsidcomplete) -- [POST /store/carts/{id}/customer](undefined/api/store#carts_postcartsidcustomer) -- [POST /store/carts/{id}/line-items](undefined/api/store#carts_postcartsidlineitems) -- [POST /store/carts/{id}/line-items/{line_id}](undefined/api/store#carts_postcartsidlineitemsline_id) -- [DELETE /store/carts/{id}/line-items/{line_id}](undefined/api/store#carts_deletecartsidlineitemsline_id) -- [POST /store/carts/{id}/promotions](undefined/api/store#carts_postcartsidpromotions) -- [DELETE /store/carts/{id}/promotions](undefined/api/store#carts_deletecartsidpromotions) -- [POST /store/carts/{id}/shipping-methods](undefined/api/store#carts_postcartsidshippingmethods) -- [POST /store/carts/{id}/taxes](undefined/api/store#carts_postcartsidtaxes) -- [GET /store/collections](undefined/api/store#collections_getcollections) -- [GET /store/collections/{id}](undefined/api/store#collections_getcollectionsid) -- [GET /store/currencies](undefined/api/store#currencies_getcurrencies) -- [GET /store/currencies/{code}](undefined/api/store#currencies_getcurrenciescode) -- [POST /store/customers](undefined/api/store#customers_postcustomers) -- [GET /store/customers/me](undefined/api/store#customers_getcustomersme) -- [POST /store/customers/me](undefined/api/store#customers_postcustomersme) -- [GET /store/customers/me/addresses](undefined/api/store#customers_getcustomersmeaddresses) -- [POST /store/customers/me/addresses](undefined/api/store#customers_postcustomersmeaddresses) -- [GET /store/customers/me/addresses/{address_id}](undefined/api/store#customers_getcustomersmeaddressesaddress_id) -- [POST /store/customers/me/addresses/{address_id}](undefined/api/store#customers_postcustomersmeaddressesaddress_id) -- [DELETE /store/customers/me/addresses/{address_id}](undefined/api/store#customers_deletecustomersmeaddressesaddress_id) -- [GET /store/orders](undefined/api/store#orders_getorders) -- [GET /store/orders/{id}](undefined/api/store#orders_getordersid) -- [POST /store/orders/{id}/transfer/accept](undefined/api/store#orders_postordersidtransferaccept) -- [POST /store/orders/{id}/transfer/cancel](undefined/api/store#orders_postordersidtransfercancel) -- [POST /store/orders/{id}/transfer/decline](undefined/api/store#orders_postordersidtransferdecline) -- [POST /store/orders/{id}/transfer/request](undefined/api/store#orders_postordersidtransferrequest) -- [POST /store/payment-collections](undefined/api/store#payment-collections_postpaymentcollections) -- [POST /store/payment-collections/{id}/payment-sessions](undefined/api/store#payment-collections_postpaymentcollectionsidpaymentsessions) -- [GET /store/payment-providers](undefined/api/store#payment-providers_getpaymentproviders) -- [GET /store/product-categories](undefined/api/store#product-categories_getproductcategories) -- [GET /store/product-categories/{id}](undefined/api/store#product-categories_getproductcategoriesid) -- [GET /store/product-tags](undefined/api/store#product-tags_getproducttags) -- [GET /store/product-tags/{id}](undefined/api/store#product-tags_getproducttagsid) -- [GET /store/product-types](undefined/api/store#product-types_getproducttypes) -- [GET /store/product-types/{id}](undefined/api/store#product-types_getproducttypesid) -- [GET /store/products](undefined/api/store#products_getproducts) -- [GET /store/products/{id}](undefined/api/store#products_getproductsid) -- [GET /store/regions](undefined/api/store#regions_getregions) -- [GET /store/regions/{id}](undefined/api/store#regions_getregionsid) -- [GET /store/return-reasons](undefined/api/store#return-reasons_getreturnreasons) -- [GET /store/return-reasons/{id}](undefined/api/store#return-reasons_getreturnreasonsid) -- [POST /store/return](undefined/api/store#return_postreturn) -- [GET /store/shipping-options](undefined/api/store#shipping-options_getshippingoptions) -- [POST /store/shipping-options/{id}/calculate](undefined/api/store#shipping-options_postshippingoptionsidcalculate) +If you are working within a monorepo, you may need to add the path to the `@medusajs/ui` package in your `tailwind.config.js` like so: +```tsx +const path = require("path") -# Introduction +const uiPath = path.resolve( + require.resolve("@medusajs/ui"), + "../..", + "\*_/_.{js,jsx,ts,tsx}" +) -Primitives for building Medusa applications. +module.exports = { + content: [ + // ... + uiPath, + ], + // ... +} -Welcome to Medusa UI, a React implementation of the Medusa design system. -It is a collection of components, hooks, utility functions, icons, and [Tailwind CSS](https://tailwindcss.com/) classes that can be used to build -a consistent user interface across the Medusa Admin and other Medusa applications. +``` -## Packages +## Start building *** -Medusa UI is split into multiple packages. Each package is published to npm -and can be installed separately. +You are now ready to start building your application with Medusa UI. You can import the components like so: -- `@medusajs/ui` - React components, hooks, and utility functions used - in Medusa UI. -- `@medusajs/ui-preset` - Tailwind CSS preset containing all the classes - used in Medusa UI. -- `@medusajs/icons` - Icons used in Medusa UI. +```tsx +import { Button, Drawer } from "@medusajs/ui" +``` -## About +## Updating UI Packages *** -At its core, Medusa UI is a styled and slightly opinionated implementation of [Radix Primitives](https://www.radix-ui.com/primitives). -Our team have also referenced the fantastic [shadcn/ui](https://ui.shadcn.com/) for inspiration in certain implementations. - -Our team strongly believe in keeping the components simple and -composable, much like Medusa's foundation. This allows you to build whatever you need. Our team have tried to avoid overloading -the component API and, instead, leveraged the native HTML API, which gets implemented -and respected accordingly, and passed to the underlying elements. - - -# Installation +Medusa's design-system packages, including `@medusajs/ui`, `@medusajs/ui-preset`, and `@medusajs/ui-icons`, are versioned independently. However, they're still part of the latest Medusa release. So, you can browse the [release notes](https://github.com/medusajs/medusa/releases) to see if there are any breaking changes to these packages. -How to install and setup Medusa UI. +To update these packages, update their version in your `package.json` file and re-install dependencies. For example: +```bash +npm install @medusajs/ui +``` # Alert @@ -32764,7 +32380,7 @@ This component is based on the \`div\` element and supports all of its props *** -If you're using the `CodeBlock` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `CodeBlock.Header` component uses a [Tooltip](undefined/components/tooltip#usage-outside-medusa-admin): +If you're using the `CodeBlock` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `CodeBlock.Header` component uses a [Tooltip](https://docs.medusajs.com/components/tooltip#usage-outside-medusa-admin/index.html.md): ```tsx @@ -33048,7 +32664,7 @@ This component is based on the div element and supports all of its props *** -If you're using the `Command` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `Command.Copy` component uses a [Tooltip](undefined/components/tooltip#usage-outside-medusa-admin): +If you're using the `Command` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `Command.Copy` component uses a [Tooltip](https://docs.medusajs.com/components/tooltip#usage-outside-medusa-admin/index.html.md): ```tsx @@ -33173,7 +32789,7 @@ This component is based on the \`button\` element and supports all of its props *** -If you're using the `Copy` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `Copy` component uses a [Tooltip](undefined/components/tooltip#usage-outside-medusa-admin): +If you're using the `Copy` component in a project other than the Medusa Admin, make sure to include the `TooltipProvider` somewhere up in your component tree, as the `Copy` component uses a [Tooltip](https://docs.medusajs.com/components/tooltip#usage-outside-medusa-admin/index.html.md): ```tsx @@ -38299,125 +37915,6 @@ If you're using the `Tooltip` component in a project other than the Medusa Admin - disableHoverableContent: (boolean) When \`true\`, trying to hover the content will result in the tooltip closing as the pointer leaves the trigger. -# Medusa Admin Extension - -How to install and use Medusa UI for building Admin extensions. - -## Installation - -*** - -The `@medusajs/ui` package is a already installed as a dependency of the `@medusajs/admin` package. Due to this you can simply import the package and use it in your local Admin extensions. - -If you are building a Admin extension as part of a Medusa plugin, you can install the package as a dependency of your plugin. - -```bash -npm install @medusajs/ui -``` - -## Configuration - -*** - -The configuration of the UI package is handled by the `@medusajs/admin` package. Therefore, you do not need to any additional configuration to use the UI package in your Admin extensions. - - -# Standalone Project - -How to install and use Medusa UI in a standalone project. - -## Installation - -*** - -Medusa UI is a React UI library and while it's intended for usage within Medusa projects, it can also be used in any React project. - -### Install Medusa UI - -Install the React UI library with the following command: - -```bash -npm install @medusajs/ui -``` - -### Configuring Tailwind CSS - -The components are styled using Tailwind CSS, and in order to use them, you will need to install Tailwind CSS in your project as well. -For more information on how to install Tailwind CSS, please refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs/installation). - -All of the classes used for Medusa UI are shipped as a Tailwind CSS customization. -You can install it with the following command: - -```bash -npm install @medusajs/ui-preset -``` - -After you have installed Tailwind CSS and the Medusa UI preset, you need to add the following to your `tailwind.config.js`file: - -```tsx -module.exports = { - presets: [require("@medusajs/ui-preset")], - // ... -} -``` - -In order for the styles to be applied correctly to the components, you will also need to ensure that -`@medusajs/ui` is included in the content field of your `tailwind.config.js` file: - -```tsx -module.exports = { - content: [ - // ... - "./node_modules/@medusajs/ui/dist/**/*.{js,jsx,ts,tsx}", - ], - // ... -} -``` - -If you are working within a monorepo, you may need to add the path to the `@medusajs/ui` package in your `tailwind.config.js` like so: - -```tsx -const path = require("path") - -const uiPath = path.resolve( - require.resolve("@medusajs/ui"), - "../..", - "\*_/_.{js,jsx,ts,tsx}" -) - -module.exports = { - content: [ - // ... - uiPath, - ], - // ... -} - -``` - -## Start building - -*** - -You are now ready to start building your application with Medusa UI. You can import the components like so: - -```tsx -import { Button, Drawer } from "@medusajs/ui" -``` - -## Updating UI Packages - -*** - -Medusa's design-system packages, including `@medusajs/ui`, `@medusajs/ui-preset`, and `@medusajs/ui-icons`, are versioned independently. However, they're still part of the latest Medusa release. So, you can browse the [release notes](https://github.com/medusajs/medusa/releases) to see if there are any breaking changes to these packages. - -To update these packages, update their version in your `package.json` file and re-install dependencies. For example: - -```bash -npm install @medusajs/ui -``` - - # clx Utility function for working with classNames.