www/apps/book/app/learn/fundamentals/api-routes/additional-data/page.mdx
import { Details } from "docs-ui"
export const metadata = {
title: ${pageNumber} Pass Additional Data to Medusa's API Route,
}
In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route.
Some of Medusa's API Routes accept an additional_data parameter whose type is an object. The API Route passes the additional_data to the workflow, which in turn passes it to its hooks.
This is useful when you have a link from your custom module to a Commerce Module, and you want to perform an additional action when a request is sent to an existing API route.
For example, the Create Product API Route accepts an additional_data parameter. If you have a data model linked to it, you consume the productsCreated hook to create a record of the data model using the custom data and link it to the product.
Before passing custom data in the additional_data object parameter, you must specify validation rules for the allowed properties in the object.
To do that, use the middleware route object defined in src/api/middlewares.ts.
For example, create the file src/api/middlewares.ts with the following content:
As of Medusa v2.13.0, Zod should be imported from @medusajs/framework/zod.
import { defineMiddlewares } from "@medusajs/framework/http"
import { z } from "@medusajs/framework/zod"
export default defineMiddlewares({
routes: [
{
method: "POST",
matcher: "/admin/products",
additionalDataValidator: {
brand: z.string().optional(),
},
},
],
})
The middleware route object accepts an optional parameter additionalDataValidator whose value is an object of key-value pairs. The keys indicate the name of accepted properties in the additional_data parameter, and the value is Zod validation rules of the property.
In this example, you indicate that the additional_data parameter accepts a brand property whose value is an optional string.
Refer to Zod's documentation for all available validation rules.
</Note>You can now pass a brand property in the additional_data parameter of a request to the Create Product API Route.
For example:
curl -X POST 'http://localhost:9000/admin/products' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer {token}' \
--data '{
"title": "Product 1",
"options": [
{
"title": "Default option",
"values": ["Default option value"]
}
],
"shipping_profile_id": "{shipping_profile_id}",
"additional_data": {
"brand": "Acme"
}
}'
Make sure to replace the {token} in the authorization header with an admin user's authentication token, and {shipping_profile_id} with an existing shipping profile's ID.
In this request, you pass in the additional_data parameter a brand property and set its value to Acme.
The additional_data is then passed to hooks in the createProductsWorkflow used by the API route.
Learn about workflow hooks in this guide.
</Note>Step functions consuming the workflow hook can access the additional_data in the first parameter.
For example, consider you want to store the data passed in additional_data in the product's metadata property.
To do that, create the file src/workflows/hooks/product-created.ts with the following content:
import { StepResponse } from "@medusajs/framework/workflows-sdk"
import { createProductsWorkflow } from "@medusajs/medusa/core-flows"
import { Modules } from "@medusajs/framework/utils"
createProductsWorkflow.hooks.productsCreated(
async ({ products, additional_data }, { container }) => {
if (!additional_data?.brand) {
return
}
const productModuleService = container.resolve(
Modules.PRODUCT
)
await productModuleService.upsertProducts(
products.map((product) => ({
...product,
metadata: {
...product.metadata,
brand: additional_data.brand,
},
}))
)
return new StepResponse(products, {
products,
additional_data,
})
}
)
This consumes the productsCreated hook, which runs after the products are created.
If brand is passed in additional_data, you resolve the Product Module's main service and use its upsertProducts method to update the products, adding the brand to the metadata property.
Hooks also accept a compensation function as a second parameter to undo the actions made by the step function.
For example, pass the following second parameter to the productsCreated hook:
createProductsWorkflow.hooks.productsCreated(
async ({ products, additional_data }, { container }) => {
// ...
},
async ({ products, additional_data }, { container }) => {
if (!additional_data.brand) {
return
}
const productModuleService = container.resolve(
Modules.PRODUCT
)
await productModuleService.upsertProducts(
products
)
}
)
This updates the products to their original state before adding the brand to their metadata property.