3.7.6. Workflow Hooks

In this chapter, you'll learn what workflow hooks are and how to use them.

What is a Workflow Hook?#

A workflow hook is a specific point in a workflow where you can inject custom functionality. This custom functionality is called a hook handler.

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.

Tip: Refer to the Workflows Reference to view all workflows and their hooks.
Consume workflow hooks when: You want to perform a custom action during a workflow's execution, such as when a product is created.

How to Consume a Hook?#

A workflow has a special hooks property. This property is an object that contains all available hooks.

So, in a TypeScript or JavaScript file created under the src/workflows/hooks directory:

  1. Import the workflow.
  2. Access the hook using the hooks property.
  3. Pass a step function as a parameter to the hook.

For example, to consume the productsCreated hook of Medusa's createProductsWorkflow, create the file src/workflows/hooks/product-created.ts with the following content:

src/workflows/hooks/product-created.ts
1import { createProductsWorkflow } from "@medusajs/medusa/core-flows"2
3createProductsWorkflow.hooks.productsCreated(4  async ({ products }, { container }) => {5    // TODO perform an action6  }7)

The productsCreated hook is available in the workflow's hooks property.

You call the hook and pass a step function (the hook handler) as a parameter.

Now, when a product is created using the Create Product API route, your hook handler runs after the product is created.

Note: A hook can have only one handler. So, you can't consume the same hook multiple times.
Tip: Refer to the createProductsWorkflow reference to see at which point the hook handler is executed.

Hook Handler Parameter#

Since a hook handler is essentially a step function, it receives the hook's input as a first parameter, and an object holding a container property as a second parameter.

Each hook has different input. For example, the productsCreated hook receives an object with a products property that contains the created product.

You can find the input for each workflow's hooks in the Core Workflows Reference.

Hook Handler Compensation#

Since the hook handler is a step function, you can set its compensation function as a second parameter of the hook.

For example:

src/workflows/hooks/product-created.ts
1import { createProductsWorkflow } from "@medusajs/medusa/core-flows"2
3createProductsWorkflow.hooks.productsCreated(4  async ({ products }, { container }) => {5    // TODO perform an action6
7    return new StepResponse(undefined, { ids })8  },9  async ({ ids }, { container }) => {10    // undo the performed action11  }12)

The compensation function runs if an error occurs in the workflow. It undoes the actions performed by the hook handler.

The compensation function receives the second parameter passed to the StepResponse returned by the step function as input.

It also accepts an object with a container property as a second parameter. This allows you to resolve resources from the Medusa container.

Additional Data Property#

Medusa's workflows include an additional_data property in the hook's input:

src/workflows/hooks/product-created.ts
1import { createProductsWorkflow } from "@medusajs/medusa/core-flows"2
3createProductsWorkflow.hooks.productsCreated(4  async ({ products, additional_data }, { container }) => {5    // TODO perform an action6  }7)

This property is an object that contains additional data passed to the workflow through the request sent to the API route.

Learn how to pass additional_data in requests to API routes in the Additional Data chapter.

Pass Additional Data to Workflow#

You can also pass additional data when running the workflow. Pass it as a parameter to the workflow's .run method:

src/workflows/hooks/product-created.ts
1import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"2import { createProductsWorkflow } from "@medusajs/medusa/core-flows"3
4export async function POST(req: MedusaRequest, res: MedusaResponse) {5  await createProductsWorkflow(req.scope).run({6    input: { 7      products: [8        // ...9      ], 10      additional_data: {11        custom_field: "test",12      },13    },14  })15}

Your hook handler then receives the passed data in the additional_data object.

Was this chapter helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break