docs: added workflow hooks docs + changed workflow response (#8364)
* docs: added workflow hooks docs + changed workflow response * Update page.mdx
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Workflow Hooks`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn what a workflow hook is, how to expose it in your workflow, and how to consume the hook with a handler.
|
||||
|
||||
## What is a Workflow Hook?
|
||||
|
||||
A workflow hook is a point in a workflow where you can inject custom functionality as a step function, called a hook handler.
|
||||
|
||||
Hook handlers receive input from the workflow to perform custom actions during the workflow's execution.
|
||||
|
||||
<Note title="Use workflow hooks when" type="success">
|
||||
|
||||
- Your workflow is reusable in other applications, and you allow performing an external action at some point in your workflow.
|
||||
|
||||
</Note>
|
||||
|
||||
<Note title="Don't use workflow hooks if" type="error">
|
||||
|
||||
- Your workflow isn't reusable by other applications. Use a step that performs what a hook handler would instead.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## How to Expose a Hook in a Workflow?
|
||||
|
||||
To expose a hook in your workflow, use the `createHook` function imported from `@medusajs/workflows-sdk`.
|
||||
|
||||
For example:
|
||||
|
||||
export const hookHighlights = [
|
||||
["13", "createHook", "Add a hook to the workflow."],
|
||||
["14", `"productCreated"`, "The hook's name."],
|
||||
["15", "productId", "The data to pass to the hook handler."],
|
||||
["19", "hooks", "Return the list of hooks in the workflow."]
|
||||
]
|
||||
|
||||
```ts title="src/workflows/my-workflow/index.ts" highlights={hookHighlights}
|
||||
import {
|
||||
createStep,
|
||||
createHook,
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/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. This is used to add a hook handler 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 a Hook?
|
||||
|
||||
You consume a hook by registering a hook handler on the workflow. A hook handler is registered in a TypeScript or JavaScript file created in the `src/workflows/hooks` directory.
|
||||
|
||||
You'll find a workflow's exposed hooks in its `hooks` property.
|
||||
|
||||
For example, to consume the hook of the workflow in the previous example, create the file `src/workflows/hooks/my-workflow.ts` with the following content:
|
||||
|
||||
export const handlerHighlights = [
|
||||
["3", "productCreated", "Invoke the hook, passing it a step function as a parameter."],
|
||||
]
|
||||
|
||||
```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
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
<Note>
|
||||
|
||||
A hook can have only one handler.
|
||||
|
||||
</Note>
|
||||
|
||||
The hook is available on the workflow's `hooks` property using its name `productCreated`. You invoke the hook, passing the handler as a parameter, which is a step function.
|
||||
|
||||
Similar to a step, the handler receives the hook's input as a first parameter, and the container in the object as a second parameter.
|
||||
|
||||
### Hook Handler Compensation
|
||||
|
||||
You can also pass a compensation function as a second parameter:
|
||||
|
||||
```ts
|
||||
import { myWorkflow } from "../my-workflow"
|
||||
|
||||
myWorkflow.hooks.productCreated(
|
||||
async ({ productId }, { container }) => {
|
||||
// TODO perform an action
|
||||
},
|
||||
async () => {
|
||||
// undo the performed action
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
The compensation function is executed if an error occurs in the workflow to undo the actions performed by the hook handler.
|
||||
@@ -205,8 +205,11 @@ With the steps ready, you'll create the workflow that runs these steps to update
|
||||
|
||||
Change the content of `src/workflows/update-product-erp/index.ts` to the following:
|
||||
|
||||
```ts title="src/workflows/update-product-erp/index.ts" collapsibleLines="1-5" expandButtonLabel="Show Imports"
|
||||
import { createWorkflow } from "@medusajs/workflows-sdk"
|
||||
```ts title="src/workflows/update-product-erp/index.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { UpsertProductDTO } from "@medusajs/types"
|
||||
import updateProduct from "./steps/update-product"
|
||||
import updateErp from "./steps/update-erp"
|
||||
@@ -219,10 +222,10 @@ const updateProductAndErpWorkflow = createWorkflow(
|
||||
const product = updateProduct(input)
|
||||
const erpData = updateErp(input)
|
||||
|
||||
return {
|
||||
return new WorkflowResponse({
|
||||
product,
|
||||
erpData,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default updateProductAndErpWorkflow
|
||||
|
||||
@@ -54,10 +54,11 @@ const step2 = createStep(
|
||||
|
||||
2. Use the steps in a workflow. For example:
|
||||
|
||||
```ts title="src/workflows/hello-world.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
||||
```ts title="src/workflows/hello-world.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
// other imports...
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
// ...
|
||||
@@ -68,9 +69,9 @@ const myWorkflow = createWorkflow(
|
||||
const str1 = step1()
|
||||
step2()
|
||||
|
||||
return {
|
||||
return new WorkflowResponse({
|
||||
message: str1,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default myWorkflow
|
||||
|
||||
@@ -19,14 +19,15 @@ Since if-conditions aren't allowed in the workflow constructor function, use the
|
||||
For example:
|
||||
|
||||
export const highlights = [
|
||||
["15", "input", "The data to pass as a parameter to the function in the second parameter"],
|
||||
["17", "return", "The function must return a boolean value indicating whether\nthe callback function passed to `then` should be executed."],
|
||||
["19", "() => {", "The function to execute if `when`'s second parameter returns a `true` value."]
|
||||
["16", "input", "The data to pass as a parameter to the function in the second parameter"],
|
||||
["18", "return", "The function must return a boolean value indicating whether\nthe callback function passed to `then` should be executed."],
|
||||
["20", "() => {", "The function to execute if `when`'s second parameter returns a `true` value."]
|
||||
]
|
||||
|
||||
```ts highlights={highlights}
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
when,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
// step imports...
|
||||
@@ -49,7 +50,11 @@ const workflow = createWorkflow(
|
||||
})
|
||||
|
||||
// executed without condition
|
||||
return anotherStep(result)
|
||||
const anotherStepResult = anotherStep(result)
|
||||
|
||||
return new WorkflowResponse(
|
||||
anotherStepResult
|
||||
)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
@@ -22,10 +22,11 @@ A workflow is considered long-running if at least one step has its `async` confi
|
||||
|
||||
For example, consider the following workflow and steps:
|
||||
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["14"]]} collapsibleLines="1-10" expandButtonLabel="Show More"
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["15"]]} collapsibleLines="1-11" expandButtonLabel="Show More"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
StepResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
@@ -54,9 +55,9 @@ const myWorkflow = createWorkflow(
|
||||
step2()
|
||||
const message = step3()
|
||||
|
||||
return {
|
||||
return new WorkflowResponse({
|
||||
message,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default myWorkflow
|
||||
|
||||
@@ -15,13 +15,14 @@ The workflow waits until all steps passed to the `parallelize` function finish e
|
||||
For example:
|
||||
|
||||
export const highlights = [
|
||||
["21", "[prices, productSalesChannel]", "The result of the steps. `prices` is the result of `createPricesStep`, and `productSalesChannel` is the result of `attachProductToSalesChannelStep`."],
|
||||
["21", "parallelize", "Run the steps passed as parameters in parallel."],
|
||||
["22", "[prices, productSalesChannel]", "The result of the steps. `prices` is the result of `createPricesStep`, and `productSalesChannel` is the result of `attachProductToSalesChannelStep`."],
|
||||
["22", "parallelize", "Run the steps passed as parameters in parallel."],
|
||||
]
|
||||
|
||||
```ts highlights={highlights} collapsibleLines="1-12" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
parallelize,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
@@ -46,7 +47,9 @@ const myWorkflow = createWorkflow(
|
||||
)
|
||||
|
||||
const id = product.id
|
||||
return getProductStep(product.id)
|
||||
const refetchedProduct = getProductStep(product.id)
|
||||
|
||||
return new WorkflowResponse(refetchedProduct)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
@@ -14,10 +14,11 @@ You can configure the step to retry on failure. The `createStep` function can ac
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["9"]]} collapsibleLines="1-5" expandButtonLabel="Show Imports"
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["10"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
@@ -37,9 +38,9 @@ const myWorkflow = createWorkflow(
|
||||
function () {
|
||||
const str1 = step1()
|
||||
|
||||
return {
|
||||
return new WorkflowResponse({
|
||||
message: str1,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default myWorkflow
|
||||
|
||||
@@ -20,10 +20,11 @@ Timeout doesn't stop the execution of a running step. The timeout only affects t
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["15"]]} collapsibleLines="1-12" expandButtonLabel="Show More"
|
||||
```ts title="src/workflows/hello-world.ts" highlights={[["16"]]} collapsibleLines="1-13" expandButtonLabel="Show More"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
@@ -39,9 +40,9 @@ const myWorkflow = createWorkflow({
|
||||
}, function () {
|
||||
const str1 = step1()
|
||||
|
||||
return {
|
||||
return new WorkflowResponse({
|
||||
message: str1,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
export default myWorkflow
|
||||
|
||||
Reference in New Issue
Block a user