4.2.3. Extend Create Product API Route

Example ChapterThis chapter covers how to extend the Create Product API route to link a product to a brand as a step of the "Extend Models" chapter .

Additional Data in API Routes#

Some API routes, including the Create Product API route, accept an additional_data request body parameter.

It's useful when you want to pass custom data, such as the brand ID, then perform an action based on this data, such as link the brand to the product.


1. Allow Passing the Brand ID in Additional Data#

Before passing custom properties in the additional_data parameter, you add the property to additional_data's validation rules.

Create the file src/api/middlewares.ts, which is a special file that defines middlewares or validation rules of custom properties passed in the additional_data parameter:

src/api/middlewares.ts
1import { defineMiddlewares } from "@medusajs/medusa"2import { z } from "zod"3
4export default defineMiddlewares({5  routes: [6    {7      matcher: "/admin/products",8      method: ["POST"],9      additionalDataValidator: {10        brand_id: z.string().optional(),11      },12    },13  ],14})

You use Zod to add a validation rule to the additional_data parameter indicating that it can include a brand_id property of type string.

defineMiddleware Parameters#

The defineMiddlewares function accepts an object having a routes property. Its value is an array of middleware route objects, each having the following properties:

  • matcher: a string or regular expression indicating the API route path to apply the middleware on. It must be compatible with path-to-regexp.
  • method: An array of HTTP method to apply the middleware or additional data validation to. If not supplied, it's applied to all HTTP methods.
  • additionalDataValidator: An object of key-value pairs defining the validation rules for custom properties using Zod.

A workflow hook is a point in a workflow where you can inject a step to perform a custom functionality. This is useful to perform custom action in an API route's workflow.

The createProductsWorkflow used in the Create Product API route has a productsCreated hook that runs after the product is created.

So, to consume the productsCreated hook, create the file src/workflows/hooks/created-product.ts with the following content:

src/workflows/hooks/created-product.ts
1import { createProductsWorkflow } from "@medusajs/medusa/core-flows"2import { StepResponse } from "@medusajs/framework/workflows-sdk"3import { Modules, ContainerRegistrationKeys } from "@medusajs/framework/utils"4import { BRAND_MODULE } from "../../modules/brand"5import BrandModuleService from "../../modules/brand/service"6
7createProductsWorkflow.hooks.productsCreated(8  (async ({ products, additional_data }, { container }) => {9    if (!additional_data?.brand_id) {10      return new StepResponse([], [])11    }12
13    // check that brand exists14    const brandModuleService: BrandModuleService = container.resolve(15      BRAND_MODULE16    )17    // if the brand doesn't exist, an error is thrown.18    await brandModuleService.retrieveBrand(additional_data.brand_id as string)19
20    const remoteLink = container.resolve(21      ContainerRegistrationKeys.REMOTE_LINK22    )23    const logger = container.resolve(24      ContainerRegistrationKeys.LOGGER25    )26
27    const links = []28
29    // link products to brands30    for (const product of products) {31      links.push({32        [Modules.PRODUCT]: {33          product_id: product.id,34        },35        [BRAND_MODULE]: {36          brand_id: additional_data.brand_id,37        },38      })39    }40
41    await remoteLink.create(links)42
43    logger.info("Linked brand to products")44
45    return new StepResponse(links, links)46  })47)

Workflows have a special hooks property to access its hooks and consume them. Each hook, such as productCreated, accept a step function as a parameter.

In the step, if a brand ID is passed in additional_data and the brand exists, you create a link between each product and the brand.

You can pass as a second parameter of the hook a compensation function that undoes what the step did.

Add the following compensation function as a second parameter:

src/workflows/hooks/created-product.ts
1createProductsWorkflow.hooks.productsCreated(2  // ...3  (async (links, { container }) => {4    if (!links.length) {5      return6    }7
8    const remoteLink = container.resolve(9      ContainerRegistrationKeys.REMOTE_LINK10    )11
12    await remoteLink.dismiss(links)13  })14)

In the compensation function, you dismiss the links created by the step using the dismiss method of the remote link.


Test it Out#

To test it out, first, retrieve the authentication token of your admin user by sending a POST request to /auth/user/emailpass:

Code
1curl -X POST 'http://localhost:9000/auth/user/emailpass' \2-H 'Content-Type: application/json' \3--data-raw '{4    "email": "admin@medusa-test.com",5    "password": "supersecret"6}'

Make sure to replace the email and password with your user's credentials.

Then, send a POST request to /admin/products to create a product, and pass in the additional_data parameter a brand's ID:

Code
1curl -X POST 'http://localhost:9000/admin/products' \2-H 'Content-Type: application/json' \3-H 'Authorization: Bearer {token}' \4--data '{5    "title": "Product 1",6    "options": [7      {8        "title": "Default option",9        "values": ["Default option value"]10      }11    ],12    "additional_data": {13        "brand_id": "01J7AX9ES4X113HKY6C681KDZ2J"14    }15}'
TipMake sure to replace the {token} in the Authorization header with the token received from the previous request.

In the request body, you pass in the additional_data parameter a brand_id.

The request creates a product and returns it.

In the Medusa application's logs, you'll find the message Linked brand to products, indicating that the workflow hook handler ran and linked the brand to the products.


Workflows and API Routes References#

Medusa exposes hooks in many of its workflows that you can consume to add custom logic.

The Store and Admin API references indicate what workflows are used in each API routes. By clicking on the workflow, you access the workflow's reference where you can see the hooks available in the workflow.


Next Steps: Query Linked Records#

In the next chapter, you'll learn how to query the brand linked to a product.

Was this chapter helpful?
Edit this page