diff --git a/www/apps/book/app/learn/advanced-development/admin/ui-routes/page.mdx b/www/apps/book/app/learn/advanced-development/admin/ui-routes/page.mdx index fa8d32303eed9..0f41118e524e8 100644 --- a/www/apps/book/app/learn/advanced-development/admin/ui-routes/page.mdx +++ b/www/apps/book/app/learn/advanced-development/admin/ui-routes/page.mdx @@ -29,6 +29,8 @@ You create a UI route in a `page.tsx` file under a sub-directory of `src/admin/r For example, create the file `src/admin/routes/custom/page.tsx` with the following content: +![Example of UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) + ```tsx title="src/admin/routes/custom/page.tsx" import { Container, Heading } from "@medusajs/ui" @@ -110,6 +112,8 @@ The above example adds a new sidebar item with the label `Custom Route` and an i Consider that along the UI route above at `src/admin/routes/custom/page.tsx` you create a nested UI route at `src/admin/routes/custom/nested/page.tsx` that also exports route configurations: +![Example of nested UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867243/Medusa%20Book/ui-route-dir-overview_tgju25.jpg) + ```tsx title="src/admin/routes/custom/nested/page.tsx" import { defineRouteConfig } from "@medusajs/admin-sdk" import { Container, Heading } from "@medusajs/ui" @@ -149,6 +153,8 @@ To create a page under the settings section of the admin dashboard, create a UI For example, create a UI route at `src/admin/routes/settings/custom/page.tsx`: +![Example of settings UI route file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867435/Medusa%20Book/setting-ui-route-dir-overview_kytbh8.jpg) + ```tsx title="src/admin/routes/settings/custom/page.tsx" import { defineRouteConfig } from "@medusajs/admin-sdk" import { Container, Heading } from "@medusajs/ui" @@ -180,6 +186,8 @@ A UI route can accept path parameters if the name of any of the directories in i For example, create the file `src/admin/routes/custom/[id]/page.tsx` with the following content: +![Example of UI route file with path parameters in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867748/Medusa%20Book/path-param-ui-route-dir-overview_kcfbev.jpg) + ```tsx title="src/admin/routes/custom/[id]/page.tsx" highlights={[["5", "", "Retrieve the path parameter."], ["10", "{id}", "Show the path parameter."]]} import { useParams } from "react-router-dom" import { Container, Heading } from "@medusajs/ui" diff --git a/www/apps/book/app/learn/advanced-development/admin/widgets/page.mdx b/www/apps/book/app/learn/advanced-development/admin/widgets/page.mdx index 9dd205b1efdf1..7ff2a5d55ca10 100644 --- a/www/apps/book/app/learn/advanced-development/admin/widgets/page.mdx +++ b/www/apps/book/app/learn/advanced-development/admin/widgets/page.mdx @@ -29,6 +29,8 @@ You create a widget in a `.tsx` file under the `src/admin/widgets` directory. Th For example, create the file `src/admin/widgets/product-widget.tsx` with the following content: +![Example of widget file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732867137/Medusa%20Book/widget-dir-overview_dqsbct.jpg) + export const widgetHighlights = [ ["5", "ProductWidget", "The React component of the product widget."], ["17", "zone", "The zone to inject the widget to."] diff --git a/www/apps/book/app/learn/basics/api-routes/page.mdx b/www/apps/book/app/learn/basics/api-routes/page.mdx index 76a5d0847c1f7..cb75d18035500 100644 --- a/www/apps/book/app/learn/basics/api-routes/page.mdx +++ b/www/apps/book/app/learn/basics/api-routes/page.mdx @@ -8,7 +8,7 @@ In this chapter, you’ll learn what API Routes are and how to create them. ## What is an API Route? -An API Route is a REST API endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems. +An API Route is an endpoint. It exposes commerce features to external applications, such as storefronts, the admin dashboard, or third-party systems. The Medusa core application provides a set of admin and store API routes out-of-the-box. You can also create custom API routes to expose your custom functionalities. @@ -18,6 +18,8 @@ The Medusa core application provides a set of admin and store API routes out-of- An API Route is created in a TypeScript or JavaScript file under the `src/api` directory of your Medusa application. The file’s name must be `route.ts` or `route.js`. +![Example of API route in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808645/Medusa%20Book/route-dir-overview_dqgzmk.jpg) + Each file exports API Route handler functions for at least one HTTP method (`GET`, `POST`, `DELETE`, etc…). For example, to create a `GET` API Route at `/hello-world`, create the file `src/api/hello-world/route.ts` with the following content: diff --git a/www/apps/book/app/learn/basics/commerce-modules/page.mdx b/www/apps/book/app/learn/basics/commerce-modules/page.mdx index 2ebd129429685..4c4922b5d04ec 100644 --- a/www/apps/book/app/learn/basics/commerce-modules/page.mdx +++ b/www/apps/book/app/learn/basics/commerce-modules/page.mdx @@ -8,11 +8,9 @@ In this chapter, you'll learn about Medusa's commerce modules. ## What is a Commerce Module? -A commerce module is a package built by Medusa that provides business logic and data models specific for a single commerce domain, such as the Product and Order modules. Commerce modules are available out-of-the-box in your application. +Commerce modules are built-in [modules](../modules/page.mdx) of Medusa that provide core commerce logic specific to domains like Products, Orders, Customers, Fulfillment, and much more. -Medusa implements core commerce flows in workflows that use the commerce modules. Then, it exposes admin and storefront API routes that, under the hood, execute these workflows. - -For example, the workflow to add a product to the cart uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart. +Medusa's commerce modules are used to form Medusa's default [workflows](!resources!/medusa-workflows-reference) and [APIs](!api!/store). For example, when you call the add to cart endpoint. the add to cart workflow runs which uses the Product Module to check if the product exists, the Inventory Module to ensure the product is available in the inventory, and the Cart Module to finally add the product to the cart. @@ -20,6 +18,8 @@ You'll find the details and steps of the add-to-cart workflow in [this workflow +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](!resources!/commerce-modules) for a full list of commerce modules in Medusa. @@ -33,18 +33,17 @@ Similar to your [custom modules](../modules/page.mdx), the Medusa application re For example, consider you have a [workflow](../workflows/page.mdx) (a special function that performs a task in a series of steps with rollback mechanism) that needs a step to retrieve the total number of products. You can create a step in the workflow that resolves the Product Module's service from the container to use its methods: export const highlights = [ - ["7", "Modules.PRODUCT", "Resolve the Product Module's service from the container."], - ["9", "listAndCountProducts", "Use the service's method to get the products count."] + ["6", `"product"`, "Resolve the Product Module's service from the container."], + ["8", "listAndCountProducts", "Use the service's method to get the products count."] ] ```ts highlights={highlights} import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" export const countProductsStep = createStep( "count-products", async ({ }, { container }) => { - const productModuleService = container.resolve(Modules.PRODUCT) + const productModuleService = container.resolve("product") const [,count] = await productModuleService.listAndCountProducts() diff --git a/www/apps/book/app/learn/basics/events-and-subscribers/page.mdx b/www/apps/book/app/learn/basics/events-and-subscribers/page.mdx index 9e8373e4d7fa4..61e08861a8e71 100644 --- a/www/apps/book/app/learn/basics/events-and-subscribers/page.mdx +++ b/www/apps/book/app/learn/basics/events-and-subscribers/page.mdx @@ -8,11 +8,11 @@ In this chapter, you’ll learn about Medusa's event system, and how to handle e ## Handle Core Commerce Flows with Events -When building commerce digital applications, you'll often need to perform an action after a commerce operation is performed. For example, sending an order confirmation email when the customer places an order. +When building commerce digital applications, you'll often need to perform an action after a commerce operation is performed. For example, sending an order confirmation email when the customer places an order, or syncing data that's updated in Medusa to a third-party system. -In other commerce platforms, it can be tricky to implement this when the commerce operation is performed in the platform's core. You resort to hacky workarounds or extend core services to perform your custom action, which becomes difficult to maintain in the long run and as your application grows. +Medusa emits events when core commerce features are performed, and you can listen to and handle these events in asynchronous functions. You can think of Medusa's events like you'd think about webhooks in other commerce platforms, but instead of having to setup separate applications to handle webhooks, your efforts only go into writing the logic right in your Medusa codebase. -Medusa's event system removes this complexity by emitting events when core commerce features are performed. Your efforts only go into handling these events in subscribers, which are functions executed asynchronously when an event is emitted. +You listen to an event in a subscriber, which is an asynchronous function that's executed when its associated event is emitted. ![A diagram showcasing an example of how an event is emitted when an order is placed.](https://res.cloudinary.com/dza7lstvk/image/upload/v1732277948/Medusa%20Book/order-placed-event-example_e4e4kw.jpg) @@ -36,18 +36,17 @@ You create a subscriber in a TypeScript or JavaScript file under the `src/subscr For example, create the file `src/subscribers/order-placed.ts` with the following content: +![Example of subscriber file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866244/Medusa%20Book/subscriber-dir-overview_pusyeu.jpg) + ```ts title="src/subscribers/product-created.ts" import { SubscriberArgs, type SubscriberConfig } from "@medusajs/framework" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" import { sendOrderConfirmationWorkflow } from "../workflows/send-order-confirmation" export default async function orderPlacedHandler({ event: { data }, container, }: SubscriberArgs<{ id: string }>) { - const logger = container.resolve( - ContainerRegistrationKeys.LOGGER - ) + const logger = container.resolve("logger") logger.info("Sending confirmation email...") diff --git a/www/apps/book/app/learn/basics/loaders/page.mdx b/www/apps/book/app/learn/basics/loaders/page.mdx index a70a3adbc39c8..a4c060160907f 100644 --- a/www/apps/book/app/learn/basics/loaders/page.mdx +++ b/www/apps/book/app/learn/basics/loaders/page.mdx @@ -10,7 +10,7 @@ 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 you're integrating a non-supported database such as MongoDB, you want to establish the connection when the application starts and re-use it in your customizations. +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](../modules/page.mdx), which is a package of business logic for a single domain. When the Medusa application starts, it executes all loaders exported by configured modules. @@ -32,6 +32,8 @@ You create a loader function in a TypeScript or JavaScript file under a module's 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](../modules/page.mdx). @@ -42,14 +44,11 @@ Learn how to create a module in [this chapter](../modules/page.mdx). 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) + const logger = container.resolve("logger") logger.info("[helloWorldLoader]: Hello, World!") } @@ -123,17 +122,16 @@ Consider your have a MongoDB module that allows you to perform operations on a M To connect to the database, you create the following loader in your module: export const loaderHighlights = [ - ["6", "ModuleOptions", "Define a type for expected options."], - ["14", "ModuleOptions", "Pass the option type as a type argument to `LoaderOptions`."], - ["24", "clientDb", "Create a client instance that connects to the specified database."], - ["30", "register", "Register custom resource in the container."], - ["31", `"mongoClient"`, "The resource's key in the container."], - ["32", "asValue(clientDb)", "The resource to register."] + ["5", "ModuleOptions", "Define a type for expected options."], + ["13", "ModuleOptions", "Pass the option type as a type argument to `LoaderOptions`."], + ["23", "clientDb", "Create a client instance that connects to the specified database."], + ["29", "register", "Register custom resource in the container."], + ["30", `"mongoClient"`, "The resource's key in the container."], + ["31", "asValue(clientDb)", "The resource to register."] ] ```ts title="src/modules/mongo/loaders/connection.ts" highlights={loaderHighlights} import { LoaderOptions } from "@medusajs/framework/types" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" import { asValue } from "awilix" import { MongoClient } from "mongodb" @@ -152,7 +150,7 @@ export default async function mongoConnectionLoader({ if (!options.db_name) { throw new Error(`[MONGO MDOULE]: db_name option is required.`) } - const logger = container.resolve(ContainerRegistrationKeys.LOGGER) + const logger = container.resolve("logger") try { const clientDb = ( diff --git a/www/apps/book/app/learn/basics/medusa-container/page.mdx b/www/apps/book/app/learn/basics/medusa-container/page.mdx index 88cc3129e20e5..e1c883efe2d0a 100644 --- a/www/apps/book/app/learn/basics/medusa-container/page.mdx +++ b/www/apps/book/app/learn/basics/medusa-container/page.mdx @@ -19,8 +19,8 @@ For example, consider you're creating an API route that retrieves products based export const highlights = [ ["8", "resolve", "A method that resolves resources from the container."], [ - "9", - "ContainerRegistrationKeys.QUERY", + "8", + `"query"`, "Query's registration key in the container.", ], ] @@ -33,9 +33,7 @@ export async function GET( req: MedusaRequest, res: MedusaResponse ) { - const query = req.scope.resolve( - ContainerRegistrationKeys.QUERY - ) + const query = req.scope.resolve("query") const { data: products } = await query.graph({ entity: "product", @@ -53,8 +51,6 @@ 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. -To resolve Query, you use the `ContainerRegistrationKeys` enum imported from `@medusajs/framework/utils`, which has the registration keys for all framework tools and resources. - You can learn more about how Query works in [this chapter](../../advanced-development/module-links/query/page.mdx). @@ -148,7 +144,7 @@ export const config = { ### Workflow Step -A [step in a workflow](../workflows/page.mdx), which is a special function where you build reliable business logic, 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](../workflows/page.mdx), 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: export const workflowStepsHighlight = [ ["7", "container", "Receive the Medusa container as a parameter"], diff --git a/www/apps/book/app/learn/basics/modules/page.mdx b/www/apps/book/app/learn/basics/modules/page.mdx index 616ae5104d70e..47933135b4fff 100644 --- a/www/apps/book/app/learn/basics/modules/page.mdx +++ b/www/apps/book/app/learn/basics/modules/page.mdx @@ -28,10 +28,12 @@ Modules are created in a sub-directory of `src/modules`. So, start by creating t ### 1. Create Data Model -A data model represents a table in the database. You create data models using Medusa's data modeling utility, which is built to improve readability and provide an intuitive developer experience. It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations. +A data model represents a table in the database. You create data models using Medusa's data modeling utility. It simplifies defining a table's columns, relations, and indexes with straightforward methods and configurations. You create a data model in a TypeScript or JavaScript file under the `models` directory of a module. So, to create a `Post` data model in the Blog Module, create the file `src/modules/blog/models/post.ts` with the following content: +![Updated directory overview after adding the data model](https://res.cloudinary.com/dza7lstvk/image/upload/v1732806790/Medusa%20Book/blog-dir-overview-1_jfvovj.jpg) + ```ts title="src/modules/blog/models/post.ts" import { model } from "@medusajs/framework/utils" @@ -61,12 +63,10 @@ The code snippet above defines a `Post` data model with `id` and `title` propert 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](../medusa-container/page.mdx), allowing you to resolve and use it when building custom commerce flows. -In other commerce platforms, you have to write the methods to manage each data model, such as to create or retrieve a post. This process is inefficient and wastes your time that can be spent on building custom business logic. - -Medusa saves your time by generating these methods for you. Your service can extend a `MedusaService` utility, which is a function that generates a class with read and write methods for every data model in your module. Your efforts only go into building custom business logic. - 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: +![Updated directory overview after adding the service](https://res.cloudinary.com/dza7lstvk/image/upload/v1732807230/Medusa%20Book/blog-dir-overview-2_avzb9l.jpg) + export const highlights = [ ["4", "MedusaService", "The service factory function."], ["5", "MyCustom", "The data models to generate data-management methods for."] @@ -84,7 +84,9 @@ class BlogModuleService extends MedusaService({ export default BlogModuleService ``` -Your module's service extends a class returned by the `MedusaService` utility function. The `MedusaService` function accepts an object of data models, and returns a class with generated methods for data-management Create, Read, Update, and Delete (CRUD) operations on those data models. You can pass all data models in your module in this object. +Your module's service extends a class generated by the `MedusaService` utility function. This class comes with generated methods for data-management Create, Read, Update, and Delete (CRUD) operations on each of your modules, saving your time that can be spent on building custom business logic. + +The `MedusaService` function accepts an object of data models to generate methods for. You can pass all data models in your module in this object. 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. @@ -102,6 +104,8 @@ The final piece to a module is its definition, which is exported in an `index.ts So, to export the definition of the Blog Module, create the file `src/modules/blog/index.ts` with the following content: +![Updated directory overview after adding the module definition](https://res.cloudinary.com/dza7lstvk/image/upload/v1732808511/Medusa%20Book/blog-dir-overview-3_dcgjaa.jpg) + export const moduleDefinitionHighlights = [ ["4", "BLOG_MODULE", "Export the module's name to reference it in other customizations."], ["6", "BLOG_MODULE", "Specify the module's name."], diff --git a/www/apps/book/app/learn/basics/project-directories-files/page.mdx b/www/apps/book/app/learn/basics/project-directories-files/page.mdx index 10fb80714b42d..d883a574f69cc 100644 --- a/www/apps/book/app/learn/basics/project-directories-files/page.mdx +++ b/www/apps/book/app/learn/basics/project-directories-files/page.mdx @@ -6,18 +6,21 @@ export const metadata = { In this chapter, you’ll learn about important directories and files in your Medusa application's project. +![A diagram of the directories overview](https://res.cloudinary.com/dza7lstvk/image/upload/v1732803813/Medusa%20Book/medusa-dir-overview_v7ks0j.jpg) + ## src This directory is the central place for your custom development. It includes the following sub-directories: -- `admin`: Holds your admin dashboard's custom components and pages. -- `api`: Holds your custom API routes that are added as endpoints in your Medusa application. -- `jobs`: Holds your scheduled jobs that run at a specified interval during your Medusa application's runtime. -- `modules`: Holds your custom modules that implement custom business logic. -- `scripts`: Holds your custom scripts to be executed using Medusa's CLI tool. -- `subscribers`: Holds your event listeners that are executed asynchronously whenever an event is emitted. -- `workflows`: Holds your custom flows that can be executed from anywhere in your application. +- `admin`: Holds your admin dashboard's custom [widgets](../../advanced-development/admin/widgets/page.mdx) and [UI routes](../../advanced-development/admin/ui-routes/page.mdx). +- `api`: Holds your custom [API routes](../api-routes/page.mdx) that are added as endpoints in your Medusa application. +- `jobs`: Holds your [scheduled jobs](../scheduled-jobs/page.mdx) that run at a specified interval during your Medusa application's runtime. +- `links`: Holds you [module links](../../advanced-development/module-links/page.mdx) that build associations between data models of different modules. +- `modules`: Holds your custom [modules](../modules/page.mdx) that implement custom business logic. +- `scripts`: Holds your custom [scripts](../../advanced-development/custom-cli-scripts/page.mdx) to be executed using Medusa's CLI tool. +- `subscribers`: Holds your [event listeners](../events-and-subscribers/page.mdx) that are executed asynchronously whenever an event is emitted. +- `workflows`: Holds your custom [flows](../workflows/page.mdx) that can be executed from anywhere in your application. ## medusa-config.ts -This file holds your Medusa configurations, such as your PostgreSQL database configurations. +This file holds your [Medusa configurations](!resources!/references/medusa-config), such as your PostgreSQL database configurations. diff --git a/www/apps/book/app/learn/basics/scheduled-jobs/page.mdx b/www/apps/book/app/learn/basics/scheduled-jobs/page.mdx index e7d254ea5ec1a..21119d5386584 100644 --- a/www/apps/book/app/learn/basics/scheduled-jobs/page.mdx +++ b/www/apps/book/app/learn/basics/scheduled-jobs/page.mdx @@ -10,7 +10,7 @@ In this chapter, you’ll learn about scheduled jobs and how to use them. When building your commerce application, you may need to automate tasks and run them repeatedly at a specific schedule. For example, you need to automatically sync products to a third-party service once a day. -In other commerce platforms, this feature isn't natively supported. Instead, you need to use the operating system's cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling. +In other commerce platforms, this feature isn't natively supported. Instead, you have to setup a separate application to execute cron jobs, which adds complexity as to how you expose this task to be executed in a cron job, or how do you debug it when it's not running within the platform's tooling. Medusa removes this overhead by supporting this feature natively with scheduled jobs. A scheduled job is an asynchronous function that the Medusa application runs at the interval you specify during the Medusa application's runtime. Your efforts are only spent on implementing the functionality performed by the job, such as syncing products to an ERP. @@ -30,21 +30,22 @@ You create a scheduled job in a TypeScript or JavaScript file under the `src/job For example, create the file `src/jobs/hello-world.ts` with the following content: +![Example of scheduled job file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866423/Medusa%20Book/scheduled-job-dir-overview_ediqgm.jpg) + export const highlights = [ - ["4", "greetingJob", "The scheduled job function to execute at a specified interval."] - ["4", "container", "Receive the Medusa container as a parameter"], - ["5", "logger", "Resolve the logger from the container"], - ["10", "config", "The scheduled job's configurations."], - ["11", "name", "The job's unique name"], - ["12", "schedule", "The schedule to run the job on."] + ["3", "greetingJob", "The scheduled job function to execute at a specified interval."] + ["3", "container", "Receive the Medusa container as a parameter"], + ["4", "logger", "Resolve the logger from the container"], + ["9", "config", "The scheduled job's configurations."], + ["10", "name", "The job's unique name"], + ["11", "schedule", "The schedule to run the job on."] ] ```ts title="src/jobs/hello-world.ts" highlights={highlights} import { MedusaContainer } from "@medusajs/framework/types" -import { ContainerRegistrationKeys } from "@medusajs/framework/utils" export default async function greetingJob(container: MedusaContainer) { - const logger = container.resolve(ContainerRegistrationKeys.LOGGER) + const logger = container.resolve("logger") logger.info("Greeting!") } diff --git a/www/apps/book/app/learn/basics/workflows/page.mdx b/www/apps/book/app/learn/basics/workflows/page.mdx index 66912e11db321..66f65f7601eb4 100644 --- a/www/apps/book/app/learn/basics/workflows/page.mdx +++ b/www/apps/book/app/learn/basics/workflows/page.mdx @@ -10,9 +10,11 @@ In this chapter, you’ll learn about workflows and how to define and execute th ## What is a Workflow? -In other commerce platforms, it's difficult to implement commerce features where a single task performs operations on core and custom data models, or connects to third-party systems. It's also almost impossible to maintain their execution, handle errors gracefully, and have more control over how these tasks are executed. +In digital commerce you typically have many systems involved in your operations. For example, you may have an ERP system that holds product master data and accounting reports, a CMS system for content, a CRM system for managing customer campaigns, a payment service to process credit cards, and so on. Sometimes you may even have custom built applications that need to participate in the commerce stack. One of the biggest challenges when operating a stack like this is ensuring consistency in the data spread across systems. -Medusa solves this major pain point with workflows. A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow similar to how you create a JavaScript function. +Medusa has a built-in durable execution engine to help complete tasks that span multiple systems. You orchestrate your operations across systems in Medusa instead of having to manage it yourself. Other commerce platforms don't have this capability, which makes them a bottleneck to building customizations and iterating quickly. + +A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow similar to how you create a JavaScript function. However, unlike regular functions, workflows: @@ -32,6 +34,8 @@ A workflow is made of a series of steps. A step is created using the `createStep Create the file `src/workflows/hello-world.ts` with the following content: +![Example of workflow file in the application's directory structure](https://res.cloudinary.com/dza7lstvk/image/upload/v1732866980/Medusa%20Book/workflow-dir-overview_xklukj.jpg) + export const step1Highlights = [ ["4", `"step-1"`, "The step's unique name."], ["6", "`Hello from step one!`", "The step's returned data."] @@ -236,28 +240,27 @@ A step receives an object as a second parameter with configurations and context- 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: export const highlights = [ - ["11", "container", "Receive the Medusa container as a parameter"], - ["12", "resolve", "Resolve the Product Module's main service."], + ["10", "container", "Receive the Medusa container as a parameter"], + ["11", "resolve", "Resolve the Product Module's main service."], [ - "12", - "Modules.PRODUCT", + "11", + `"product"`, "The resource registration name imported from `@medusajs/framework/utils`.", ], ] -```ts title="src/workflows/product-count.ts" highlights={highlights} collapsibleLines="1-9" expandButtonLabel="Show Imports" +```ts title="src/workflows/product-count.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports" import { createStep, StepResponse, createWorkflow, WorkflowResponse, } from "@medusajs/framework/workflows-sdk" -import { Modules } from "@medusajs/framework/utils" const getProductCountStep = createStep( "get-product-count", async (_, { container }) => { - const productModuleService = container.resolve(Modules.PRODUCT) + const productModuleService = container.resolve("product") const [, count] = await productModuleService.listAndCountProducts() diff --git a/www/apps/book/app/learn/installation/page.mdx b/www/apps/book/app/learn/installation/page.mdx index 38ab0088349b5..6976ebe1cfbf7 100644 --- a/www/apps/book/app/learn/installation/page.mdx +++ b/www/apps/book/app/learn/installation/page.mdx @@ -6,11 +6,11 @@ export const metadata = { # {metadata.title} -In this documentation, you'll learn how to install and run a Medusa application. +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 headless Node.js server and an admin dashboard. You can optionally install a separate [Next.js storefront](../storefront-development/nextjs-starter/page.mdx) 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](../storefront-development/nextjs-starter/page.mdx) either while installing the Medusa application or at a later point. { return (

{ return (

{ return { allStringChildren, - stringChildren: stringChildren.join(" "), + stringChildren: stringChildren.join(""), } }, [children])