How to Access Request Parameters and Body in Medusa API Routes
In Medusa API routes, access URL path parameters via req.params, query filters via req.filterableFields, and request body data via req.body (raw) or req.validatedBody (type-checked after validation middleware).
The medusajs/medusa repository provides a robust HTTP layer through the @medusajs/framework/http package. Every API route receives a strongly-typed request object that exposes multiple properties for accessing request parameters and body data. Understanding how to properly extract this data ensures type-safe handlers that integrate cleanly with Medusa’s workflow and query systems.
Understanding MedusaRequest Types
Every route handler in Medusa receives a request object whose type derives from either AuthenticatedMedusaRequest (for routes requiring authentication) or the generic MedusaRequest (for public routes). These types extend standard Express request objects with Medusa-specific fields.
As defined in packages/framework/src/http/types.ts, these interfaces provide the foundation for req.params, req.body, req.queryConfig, and other context-aware properties. When you import types like RequestWithContext or StoreRequestWithContext, you gain access to additional fields such as locale, pricingContext, and queryConfig that the middleware stack automatically populates.
Extracting URL Path Parameters with req.params
Path parameters captured from route definitions (e.g., /:id) are available on req.params as plain strings. You can destructure them directly for immediate use in database queries or workflow inputs.
In packages/medusa/src/api/store/products/[id]/route.ts, the handler extracts the product ID to perform a targeted lookup:
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse<HttpTypes.StoreProductResponse>
) => {
const { id } = req.params
const product = await query.graph({
entity: "product",
filters: { id },
fields: req.queryConfig.fields,
pagination: req.queryConfig.pagination,
})
res.json({ product })
}
The req.params object contains string values from the URL segments. When multiple parameters exist (e.g., /:id/line-items/:line_id), destructure them together: const { id, line_id } = req.params.
Parsing Query String Filters via req.filterableFields
For list endpoints that accept query-string filters (e.g., GET /store/products), Medusa automatically parses filterable fields into req.filterableFields. This property contains validated and typed query parameters specific to the entity being listed.
In packages/medusa/src/api/store/products/route.ts, the handler uses req.filterableFields to apply user-provided filters to the product query:
export const GET = async (
req: RequestWithContext<HttpTypes.StoreProductListParams>,
res: MedusaResponse<HttpTypes.StoreProductListResponse>
) => {
const filterableFields = req.filterableFields
const { data: products, metadata } = await query.graph({
entity: "product",
filters: filterableFields,
fields: req.queryConfig.fields,
pagination: req.queryConfig.pagination,
})
res.json({ products, count: metadata!.count })
}
The filterableFields property returns a type-safe object (in this case HttpTypes.StoreProductListParams) that you can pass directly to the query layer without manual parsing.
Handling Request Body Data
Medusa provides two properties for accessing incoming JSON payloads: req.body for raw data and req.validatedBody for validated, type-checked data.
Accessing Raw Body Data with req.body
Use req.body when you need the raw JSON payload, such as when forwarding data directly to a workflow. In packages/medusa/src/api/store/payment-collections/route.ts, the handler extracts the cart ID to create a payment collection:
export const POST = async (
req: AuthenticatedMedusaRequest<
HttpTypes.StoreCreatePaymentCollection,
HttpTypes.SelectParams
>,
res: MedusaResponse<HttpTypes.StorePaymentCollectionResponse>
) => {
const { cart_id } = req.body
await we.run(createPaymentCollectionForCartWorkflowId, {
input: req.body,
})
res.status(200).json({ payment_collection: paymentCollection })
}
Using Validated Body Data with req.validatedBody
After the built-in body-validation middleware runs (defined in packages/core/framework/src/http/utils/validate-body.ts), a type-checked copy of the payload is stored in req.validatedBody. Use this property when you need guaranteed type safety after schema validation.
In packages/medusa/src/api/store/carts/[id]/line-items/[line_id]/route.ts, the handler accesses the validated quantity update:
export const POST = async (
req: AuthenticatedMedusaRequest<HttpTypes.StoreUpdateLineItem>,
res: MedusaResponse<HttpTypes.StoreLineItemResponse>
) => {
const { quantity } = req.validatedBody
const lineId = req.params.line_id
// Process the validated update...
}
The validated body ensures that quantity conforms to the expected schema, reducing runtime errors and eliminating the need for manual type guards.
Leveraging Typed Request Helpers
Medusa extends base request types with context-aware helpers that expose additional request metadata. The RequestWithContext and StoreRequestWithContext types provide access to:
- queryConfig: Pre-computed fields and pagination settings
- locale: Current localization context
- pricingContext: Pricing-specific context for the request
These helpers are automatically populated by Medusa’s middleware stack, allowing you to write cleaner route handlers without manually parsing configuration or context data.
Summary
- Access URL path parameters via req.params, which contains plain strings from route definitions like
/:id. - Retrieve parsed query filters via req.filterableFields in list endpoints, which provides type-safe filter objects.
- Read raw JSON payloads using req.body when forwarding data to workflows or services.
- Prefer req.validatedBody after validation middleware runs for type-safe, schema-validated data.
- Use AuthenticatedMedusaRequest or MedusaRequest as base types, and leverage RequestWithContext for additional fields like
queryConfigandlocale.
Frequently Asked Questions
What is the difference between req.body and req.validatedBody in Medusa?
req.body contains the raw JSON payload sent by the client, while req.validatedBody contains the same data after it has passed through Medusa's body-validation middleware. According to the implementation in packages/core/framework/src/http/utils/validate-body.ts, the middleware validates the payload against the route's schema and attaches the result to req.validatedBody. Use req.body for raw forwarding to workflows, and req.validatedBody when you need guaranteed type safety in your handler logic.
How do I access query parameters in a Medusa API route?
For standard query strings used as filters, access req.filterableFields, which Medusa automatically parses and types based on the endpoint's expected parameters. This property is available on routes using types like RequestWithContext<HttpTypes.StoreProductListParams>. For custom query parameters that are not part of the filterable schema, you can access them via the standard Express req.query object, though Medusa's typed helpers are preferred for consistency.
What type should I use for authenticated routes in Medusa?
Use AuthenticatedMedusaRequest for routes that require authentication. This type extends the base MedusaRequest with authentication-specific properties and ensures the route is properly protected. For public store routes, use the generic MedusaRequest or StoreRequestWithContext when you need access to store-specific context like locale or pricingContext.
Where is the request validation middleware defined in Medusa?
The body validation logic that populates req.validatedBody is implemented in packages/core/framework/src/http/utils/validate-body.ts. This utility runs automatically on routes that have validation schemas defined, ensuring incoming payloads match expected types before your handler executes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →