4.6.12. Allowed Fields in API Routes

In this chapter, you'll learn about allowed fields in API routes, how to set them on custom API routes, and how to override them for Medusa's API routes.

What are Allowed Fields?#

An API route's fields query parameter accepts any field or relation name by default, including custom linked data models that you add after the route is created.

To restrict which fields and relations a route can retrieve, pass an allowed array to the route's validateAndTransformQuery configuration. Once you set allowed, a client can only request a field whose full path appears in that array. A relation in allowed doesn't grant access to the fields nested under it. Medusa silently removes every other field from the query before it executes.

Tip: Set allowed even on your custom API routes, not only on Medusa's routes. Since the fields query parameter is open by default, a route without allowed exposes every relation you link to its underlying data model, including relations you add later without realizing they're now reachable through that route.

allowed is different than disallowed fields:

  • allowed is an opt-in list. Without it, a route retrieves any field a client requests.
  • disallowed is a deny list that Medusa always enforces, even for fields in the allowed list. Medusa's API routes don't set it, so use it on your own routes to block a segment that the allowed list would otherwise let through.

How to Set Allowed Fields in Custom API Routes?#

To restrict the fields and relations retrievable by a custom API route, pass an allowed array to the validateAndTransformQuery configuration:

src/api/middlewares.ts
1import {2  validateAndTransformQuery,3  defineMiddlewares,4} from "@medusajs/framework/http"5import { createFindParams } from "@medusajs/medusa/api/utils/validators"6
7export default defineMiddlewares({8  routes: [9    {10      matcher: "/store/custom",11      method: "GET",12      middlewares: [13        validateAndTransformQuery(14          createFindParams(),15          {16            defaults: ["id", "title"],17            isList: true,18            allowed: ["id", "title", "brand"],19          }20        ),21      ],22    },23  ],24})

In this example, a request to GET /store/custom?fields=*owner doesn't retrieve the owner field, since it's not in the allowed array, even though the underlying data model has an owner relation.

An entry in allowed only matches a requested field equal to it. Medusa normalizes the * and .* prefixes and suffixes before it compares the paths, so variants in allowed also allows *variants. It rejects variants.options and variants.prices, unless you add those exact paths to the array as well:

src/api/middlewares.ts
1import {2  validateAndTransformQuery,3  defineMiddlewares,4} from "@medusajs/framework/http"5import { createFindParams } from "@medusajs/medusa/api/utils/validators"6
7export default defineMiddlewares({8  routes: [9    {10      matcher: "/store/custom",11      method: "GET",12      middlewares: [13        validateAndTransformQuery(14          createFindParams(),15          {16            defaults: ["id", "title"],17            isList: true,18            allowed: [19              "variants",20              "variants.options",21              "variants.prices",22            ],23          }24        ),25      ],26    },27  ],28})

Medusa strips a rejected field silently. The request still succeeds, but the response doesn't include the field.

Note: The order query parameter is the exception. If you sort by a field that isn't in allowed, the request fails with a 400 error.

Allowed Fields and the Relations Limit#

An allowed field must also comply with the http.storeRelationsLimit configuration, which caps how many relations a Store API route expands in one request. The two restrictions are separate:

  • allowed controls which paths a client can request.
  • http.storeRelationsLimit controls how deep those paths can go.

So, adding a path to allowed doesn't exempt it from the limit. Medusa counts every segment of a path that selects a whole relation, such as *items.variant.options, and every segment but the last of a path that selects a property, such as items.variant.title. Both count as three relations.

Important: A path that exceeds the limit fails the request with a 400 error, unlike a path that isn't in allowed, which Medusa strips silently. A route can also set a lower limit than the application-wide configuration, in which case the route's limit applies and raising http.storeRelationsLimit has no effect on it.

API Routes that Restrict Retrievable Fields#

Some of Medusa's API routes already set allowed, which means you can't pass your custom linked data models in the fields query parameter of these routes. Medusa restricts these routes to keep them performant and secure.

Every Store API route sets allowed. Each route allows its default fields and a small set of extra fields and relations, and rejects everything else. Refer to the API reference of an API route for its full list of allowed fields.

The Admin API routes that restrict the fields and relations you can retrieve are:


How to Override Allowed Fields of Medusa's API Routes?#

For the routes mentioned above, you need to override the allowed fields and relations to be retrieved. You can do this by applying a global middleware to those routes.

Since every Store API route restricts its retrievable fields, retrieving a custom linked data model through a Store API route always requires this override.

Warning: Only add the specific fields and relations that the client needs. A broad entry, such as a relation that pulls in multiple relations, or a sensitive relation like an order, a payment, or another customer, exposes that data to anyone who calls the route. Store API routes are public, so treat every path you add to allowed as data you're willing to publish.

For example, to allow retrieving the b2b_company of a customer using the Get Customer Store API Route, create the file src/api/middlewares.ts with the following content:

src/api/middlewares.ts
1import {2  allowFields,3  defineMiddlewares,4} from "@medusajs/framework/http"5
6export default defineMiddlewares({7  routes: [8    {9      matcher: "/store/customers/me",10      middlewares: [allowFields("b2b_company")],11    },12  ],13})

In this example, you apply the allowFields middleware to the Get Customer Store API Route. The middleware adds b2b_company to the fields and relations that the route already allows.

Note: allowFields is available since Medusa v2.21.0. In earlier versions, write the middleware yourself and push the fields to the request's allowed property:`import { allowFields, defineMiddlewares, } from "@medusajs/framework/http" export default defineMiddlewares({ routes: [ { matcher: "/store/customers/me", middlewares: [ (req, res, next) => { req.allowed.push("b2b_company") next() }, ], }, ], }) `

allowFields accepts the field paths as separate parameters, an array, or a mix of both:

Code
1allowFields("b2b_company", "b2b_company.name")2allowFields(["b2b_company", "b2b_company.name"])

Pass the normalized field path, without a *, +, or .* prefix or suffix.

Each allowFields middleware adds to what the route and the other middlewares allow, so several plugins or middlewares can each expose their own fields on the same route.

Note: Learn how to create a middleware in the Middlewares chapter.

You can now retrieve the b2b_company field using the fields query parameter of the Get Customer Store API Route:

Code
1curl 'http://localhost:9000/store/customers/me?fields=*b2b_company' \2-H 'x-publishable-api-key: {your_publishable_api_key}' \3-H 'Authorization: Bearer {jwt_token}'

In this example, you retrieve the b2b_company relation of the customer using the fields query parameter.

Important: This approach only works using a global middleware. It doesn't work in a route middleware.
Was this chapter helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break