Documentation

5.8.4. Compensation Function

In this chapter, you'll learn what a compensation function is and how to add it to a step.

What is a Compensation Function#

A compensation function rolls back or undoes changes made by a step when an error occurs in the workflow.

For example, if a step creates a record, the compensation function deletes the record when an error occurs later in the workflow.

By using compensation functions, you provide a mechanism that guarantees data consistency in your application and across systems.


How to add a Compensation Function?#

A compensation function is passed as a second parameter to the createStep function.

For example, create the file src/workflows/hello-world.ts with the following content:

src/workflows/hello-world.ts
4} from "@medusajs/framework/workflows-sdk"5
6const step1 = createStep(7  "step-1",8  async () => {9    const message = `Hello from step one!`10
11    console.log(message)12
13    return new StepResponse(message)14  },15  async () => {16    console.log("Oops! Rolling back my changes...")17  }18)

Each step can have a compensation function. The compensation function only runs if an error occurs throughout the workflow.


Test the Compensation Function#

Create a step in the same src/workflows/hello-world.ts file that throws an error:

src/workflows/hello-world.ts
1const step2 = createStep(2  "step-2",3  async () => {4    throw new Error("Throwing an error...")5  }6)

Then, create a workflow that uses the steps:

src/workflows/hello-world.ts
7// steps...8
9const myWorkflow = createWorkflow(10  "hello-world", 11  function (input) {12  const str1 = step1()13  step2()14
15  return new WorkflowResponse({16    message: str1,17  })18})19
20export default myWorkflow

Finally, execute the workflow from an API route:

src/api/workflow/route.ts
5import myWorkflow from "../../../workflows/hello-world"6
7export async function GET(8  req: MedusaRequest,9  res: MedusaResponse10) {11  const { result } = await myWorkflow(req.scope)12    .run()13
14  res.send(result)15}

Run the Medusa application and send a GET request to /workflow:

Code
curl http://localhost:9000/workflow

In the console, you'll see:

  • Hello from step one! logged in the terminal, indicating that the first step ran successfully.
  • Oops! Rolling back my changes... logged in the terminal, indicating that the second step failed and the compensation function of the first step ran consequently.

Pass Input to Compensation Function#

If a step creates a record, the compensation function must receive the ID of the record to remove it.

To pass input to the compensation function, pass a second parameter in the StepResponse returned by the step.

For example:

Code
1import { 2  createStep,3  StepResponse,4} from "@medusajs/framework/workflows-sdk"5
6const step1 = createStep(7  "step-1",8  async () => {9    return new StepResponse(10      `Hello from step one!`, 11      { message: "Oops! Rolling back my changes..." }12    )13  },14  async ({ message }) => {15    console.log(message)16  }17)

In this example, the step passes an object as a second parameter to StepResponse.

The compensation function receives the object and uses its message property to log a message.


Resolve Resources from the Medusa Container#

The compensation function receives an object second parameter. The object has a container property that you use to resolve resources from the Medusa container.

For example:

Code
1import { 2  createStep,3  StepResponse,4} from "@medusajs/framework/workflows-sdk"5import { ContainerRegistrationKeys } from "@medusajs/framework/utils"6
7const step1 = createStep(8  "step-1",9  async () => {10    return new StepResponse(11      `Hello from step one!`, 12      { message: "Oops! Rolling back my changes..." }13    )14  },15  async ({ message }, { container }) => {16    const logger = container.resolve(17      ContainerRegistrationKeys.LOGGER18    )19
20    logger.info(message)21  }22)

In this example, you use the container property in the second object parameter of the compensation function to resolve the logger.

You then use the logger to log a message.

Was this chapter helpful?
Edit this page