4.8.5. Workflow Hooks

In this chapter, you'll learn what a workflow hook is and how to consume them.

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.

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.

TipRefer to the Workflows Reference to view all workflows and their hooks.
Consume workflow hooks whenYou 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 which is an object that holds its hooks.

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

  • Import the workflow.
  • Access its hook using the hooks property.
  • Pass the hook a step function as a parameter to consume it.

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 on the workflow's hooks property by its name.

You invoke the hook, passing a step function (the hook handler) as a parameter.

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

NoteA hook can have only one handler.
TipRefer 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 having a products property holding the created product.

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.productCreated(4  async ({ productId }, { 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 is executed if an error occurs in the workflow to undo the actions performed by the hook handler.

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

It also accepts as a second parameter an object holding a container property to resolve resources from the Medusa container.

Additional Data Property#

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

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 holds additional data passed to the workflow through the request sent to the API route using the workflow.

NoteLearn how to pass additional_data in requests to API routes in this chapter .

Pass Additional Data to Workflow#

You can also pass that additional data when executing the workflow. Pass it as a parameter to the .run method of the workflow:

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 that passed data in the additional_data object.

Was this chapter helpful?
Edit this page