import { Details } from "docs-ui" export const metadata = { title: `${pageNumber} Pass Additional Data to Medusa's API Route`, } # {metadata.title} In this chapter, you'll learn how to pass additional data in requests to Medusa's API Route. ## Why Pass Additional Data? 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](!api!/admin#products_postproducts) 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. ### API Routes Accepting Additional Data
- Campaigns - [Create Campaign](!api!/admin#campaigns_postcampaigns) - [Update Campaign](!api!/admin#campaigns_postcampaignsid) - Cart - [Create Cart](!api!/store#carts_postcarts) - [Update Cart](!api!/store#carts_postcartsid) - Customers - [Create Customer](!api!/admin#customers_postcustomers) - [Update Customer](!api!/admin#customers_postcustomersid) - [Create Address](!api!/admin#customers_postcustomersidaddresses) - [Update Address](!api!/admin#customers_postcustomersidaddressesaddress_id) - Draft Orders - [Create Draft Order](!api!/admin#draft-orders_postdraftorders) - Orders - [Complete Orders](!api!/admin#orders_postordersidcomplete) - [Cancel Order's Fulfillment](!api!/admin#orders_postordersidfulfillmentsfulfillment_idcancel) - [Create Shipment](!api!/admin#orders_postordersidfulfillmentsfulfillment_idshipments) - [Create Fulfillment](!api!/admin#orders_postordersidfulfillments) - Products - [Create Product](!api!/admin#products_postproducts) - [Update Product](!api!/admin#products_postproductsid) - [Create Product Variant](!api!/admin#products_postproductsidvariants) - [Update Product Variant](!api!/admin#products_postproductsidvariantsvariant_id) - [Create Product Option](!api!/admin#products_postproductsidoptions) - [Update Product Option](!api!/admin#products_postproductsidoptionsoption_id) - Promotions - [Create Promotion](!api!/admin#promotions_postpromotions) - [Update Promotion](!api!/admin#promotions_postpromotionsid)
--- ## How to Pass Additional Data ### 1. Specify Validation of Additional Data 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: ```ts title="src/api/middlewares.ts" import { defineMiddlewares } from "@medusajs/medusa" import { z } from "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](https://zod.dev/) 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](https://zod.dev) for all available validation rules. ### 2. Pass the Additional Data in a Request You can now pass a `brand` property in the `additional_data` parameter of a request to the Create Product API Route. For example: ```bash curl -X POST 'http://localhost:9000/admin/products' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer {token}' \ --data '{ "title": "Product 1", "additional_data": { "brand": "Acme" } }' ``` Make sure to replace the `{token}` in the authorization header with an admin user's authentication token. 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. --- ## Use Additional Data in a Hook Learn about workflow hooks in [this guide](../../workflows/workflow-hooks/page.mdx). 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: ```ts title="src/workflows/hooks/product-created.ts" 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. ### Compensation Function 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: ```ts title="src/workflows/hooks/product-created.ts" 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 product to their original state before adding the brand to their `metadata` property.