Files
medusa-store/www/apps/book/app/basics/workflows/page.mdx
Damien Thiesson adb3a8246a docs: fix import path in the workflow basic documentation (#9481)
The import is three levels up, but, the `src/api/workflow/route.ts` file is only two levels down from `src/workflow`.

![Screenshot 2024-10-06 at 6 09 38 PM](https://github.com/user-attachments/assets/0d94338f-a8db-406b-80fe-a64ffe500ca3)
2024-10-07 07:22:15 +00:00

271 lines
7.1 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { CodeTabs, CodeTab } from "docs-ui"
export const metadata = {
title: `${pageNumber} Workflows`,
}
# {metadata.title}
In this chapter, youll 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.
By using a workflow, you can track its execution's progress, provide roll-back logic for each step to mitigate data inconsistency when errors occur, automatically retry failing steps, and do much more, as explained in later chapters.
---
## 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/framework/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/framework/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,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
// ...
const myWorkflow = createWorkflow(
"hello-world",
function (input: WorkflowInput) {
const str1 = step1()
// to pass input
const str2 = step2(input)
return new WorkflowResponse({
message: str1,
})
}
)
export default myWorkflow
```
This creates a `hello-world` workflow. When you create a workflow, its constructed but not executed yet.
The workflow must return an instance of `WorkflowResponse`, whose first parameter is returned to workflow executors.
### 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:
<CodeTabs group="resource-types">
<CodeTab label="API Route" value="api-route">
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"], ["15"], ["16"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import myWorkflow from "../../workflows/hello-world"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await myWorkflow(req.scope)
.run({
input: {
name: req.query.name as string,
},
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscriber">
```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/framework"
import myWorkflow from "../workflows/hello-world"
import { Modules } from "@medusajs/framework/utils"
import { IUserModuleService } from "@medusajs/framework/types"
export default async function handleCustomerCreate({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const userId = data.id
const userModuleService: IUserModuleService = container.resolve(
Modules.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",
}
```
</CodeTab>
<CodeTab label="Scheduled Job" value="scheduled-job">
```ts title="src/jobs/message-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"], ["11"], ["12"]]}
import { MedusaContainer } from "@medusajs/framework/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 * * *`,
};
```
</CodeTab>
</CodeTabs>
### 4. Test Workflow
To test out your workflow, start your Medusa application:
```bash npm2yarn
npm run dev
```
Then, send a `GET` request to `/workflow`:
```bash
curl http://localhost:9000/workflow
```
Youll receive the following response:
```json
{
"message": "Hello from step one!"
}
```
---
## When to Use Workflows
<Note title="Use workflows when" type="success">
You're implementing a custom feature exposed by an API route, or used in subscribers or scheduled jobs.
</Note>
---
## 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 = [
["12", "resolve", "Resolve the Product Module's main service."],
[
"12",
"Modules.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"
import {
createStep,
StepResponse,
createWorkflow,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { IProductModuleService } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, context) => {
const productModuleService: IProductModuleService =
context.container.resolve(Modules.PRODUCT)
const [, count] = await productModuleService.listAndCountProducts()
return new StepResponse(count)
})
const myWorkflow = createWorkflow(
"product-count",
function () {
const count = step1()
return new WorkflowResponse({
count,
})
}
)
export default myWorkflow
```
In the step, you resolve the Product Module's main service and use it to retrieve the product count.