import { CodeTabs, CodeTab } from "docs-ui" export const metadata = { title: `${pageNumber} Workflows`, } # {metadata.title} In this chapter, you’ll learn about workflows and how to define and execute them. ## What is a Workflow? A workflow is a series of queries and actions that complete a task. You construct a workflow similar to how you create a JavaScript function, but unlike regular functions, a workflow creates an internal representation of your steps. This makes it possible to keep track of your workflow’s progress, automatically retry failing steps, and roll back steps. --- ## How to Create and Execute a Workflow? ### 1. Create the Steps A workflow is made of a series of steps. A step is created using the `createStep` utility function imported from `@medusajs/workflows-sdk`. Create the file `src/workflows/hello-world.ts` with the following content: ```ts title="src/workflows/hello-world.ts" import { createStep, StepResponse } from "@medusajs/workflows-sdk" const step1 = createStep("step-1", async () => { return new StepResponse(`Hello from step one!`) }) ``` This creates one step that returns a hello message. Steps can accept input parameters. For example, add the following to `src/workflows/hello-world.ts`: ```ts title="src/workflows/hello-world.ts" type WorkflowInput = { name: string } const step2 = createStep("step-2", async ({ name }: WorkflowInput) => { return new StepResponse(`Hello ${name} from step two!`) }) ``` ### 2. Create a Workflow Next, add the following to the same file to create the workflow using the `createWorkflow` function: ```ts title="src/workflows/hello-world.ts" import { // other imports... createWorkflow, } from "@medusajs/workflows-sdk" // ... type WorkflowOutput = { message: string } const myWorkflow = createWorkflow( "hello-world", function (input) { const str1 = step1() // to pass input const str2 = step2(input) return { message: str1, } } ) export default myWorkflow ``` This creates a `hello-world` workflow. When you create a workflow, it’s constructed but not executed yet. ### 3. Execute the Workflow You can execute a workflow from different resources within Medusa. - Use API routes to execute the workflow in response to an API request or a webhook. - Use subscribers to execute a workflow when an event is triggered. - Use scheduled jobs to execute a workflow on a regular schedule. To execute the workflow, invoke it passing the Medusa container as a parameter. Then, use its `run` method: ```ts title="src/api/store/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"], ["15"], ["16"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports" import type { MedusaRequest, MedusaResponse, } from "@medusajs/medusa" import myWorkflow from "../../../workflows/hello-world" export async function GET( req: MedusaRequest, res: MedusaResponse ) { const { result } = await myWorkflow(req.scope) .run({ input: { name: req.query.name as string, }, }) res.send(result) } ``` ```ts title="src/subscribers/customer-created.ts" highlights={[["20"], ["21"], ["22"], ["23"], ["24"], ["25"]]} collapsibleLines="1-9" expandButtonLabel="Show Imports" import { type SubscriberConfig, type SubscriberArgs, } from "@medusajs/medusa" import myWorkflow from "../workflows/hello-world" import { ModuleRegistrationName } from "@medusajs/utils" import { IUserModuleService } from "@medusajs/types" export default async function handleCustomerCreate({ data, container, }: SubscriberArgs<{ id: string }>) { const userId = "data" in data ? data.data.id : data.id const userModuleService: IUserModuleService = container.resolve( ModuleRegistrationName.USER ) const user = await userModuleService.retrieveUser(userId) const { result } = await myWorkflow(container) .run({ input: { name: user.first_name, }, }) console.log(result) } export const config: SubscriberConfig = { event: "user.created", } ``` ```ts title="src/jobs/message-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"], ["11"], ["12"]]} import { MedusaContainer } from "@medusajs/types" import myWorkflow from "../workflows/hello-world" export default async function myCustomJob( container: MedusaContainer ) { const { result } = await myWorkflow(container) .run({ input: { name: "John", }, }) console.log(result.message) } export const config = { name: "run-once-a-day", schedule: `0 0 * * *`, }; ``` ### 4. Test Workflow To test out your workflow, start your Medusa application: ```bash npm2yarn npm run dev ``` Then, send a `GET` request to `/store/workflow`: ```bash apiTesting testApiMethod="GET" testApiUrl="http://localhost:9000/store/workflow" curl http://localhost:9000/store/workflow ``` You’ll receive the following response: ```json { "message": "Hello from step one!" } ``` --- ## When to Use Workflows - You're defining a flow with interactions across multiple systems and services. - You're defining flows to be used across different resources. For example, if you want to invoke the flow manually through an API Router, but also want to automate its running through a scheduled job. - You want to define configurations related to errors and how to roll-back steps. This is explained more in later chapters. --- ## Resolve Resources Each step in the workflow receives as a second parameter a `context` object. The object holds a `container` property which is the Medusa container. Use it to resolve other resources, such as services, of your Medusa application. For example: export const highlights = [ ["15", "resolve", "Resolve the Product Module's main service."], [ "15", "ModuleRegistrationName.PRODUCT", "The resource registration name imported from `@medusajs/modules-sdk`.", ], ] ```ts title="src/workflows/product-count.ts" highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports" import { createStep, StepResponse, createWorkflow, } from "@medusajs/workflows-sdk" import { IProductModuleService } from "@medusajs/types" import { ModuleRegistrationName } from "@medusajs/utils" type WorkflowOutput = { count: number } const step1 = createStep("step-1", async (_, context) => { const productModuleService: IProductModuleService = context.container.resolve(ModuleRegistrationName.PRODUCT) const [, count] = await productModuleService.listAndCountProducts() return new StepResponse(count) }) const myWorkflow = createWorkflow( "product-count", function () { const count = step1() return { count, } } ) export default myWorkflow ``` In the step, you resolve the Product Module's main service and use it to retrieve the product count.