Middlewares

In this chapter, you’ll learn about middlewares and how to create them.

What is a Middleware?#

A middleware is a function executed when a request is sent to an API Route. It's executed before the route handler function.

Middlwares are used to guard API routes, parse request content types other than application/json, manipulate request data, and more.

TipAs Medusa's server is based on Express, you can use any Express middleware.

Middleware Types#

There are two types of middlewares:

  1. Global Middleware: A middleware that applies to all routes matching a specified pattern.
  2. Route Middleware: A middleware that applies to routes matching a specified pattern and HTTP method(s).

These middlewares generally have the same definition and usage, but they differ in the routes they apply to. You'll learn how to create both types in the following sections.


How to Create a Global Middleware?#

Middlewares of all types are defined in the special file src/api/middlewares.ts. Use the defineMiddlewares function from the Medusa Framework to define the middlewares, and export its value.

For example:

src/api/middlewares.ts
1import { 2  defineMiddlewares,3  MedusaNextFunction, 4  MedusaRequest, 5  MedusaResponse, 6} from "@medusajs/framework/http"7
8export default defineMiddlewares({9  routes: [10    {11      matcher: "/custom*",12      middlewares: [13        (14          req: MedusaRequest, 15          res: MedusaResponse, 16          next: MedusaNextFunction17        ) => {18          console.log("Received a request!")19
20          next()21        },22      ],23    },24  ],25})

The defineMiddlewares function accepts a middleware configurations object that has the property routes. routes's 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. The regular expression must be compatible with path-to-regexp.
  • middlewares: An array of global and route middleware functions.

In the example above, you define a global middleware that logs the message Received a request! whenever a request is sent to an API route path starting with /custom.

Test the Global Middleware#

To test the middleware:

  1. Start the application:
  1. Send a request to any API route starting with /custom.
  2. See the following message in the terminal:
Terminal
Received a request!

How to Create a Route Middleware?#

In the previous section, you learned how to create a global middleware. You define the route middleware in the same way in src/api/middlewares.ts, but you specify an additional property method in the middleware route object. Its value is one or more HTTP methods to apply the middleware to.

For example:

src/api/middlewares.ts
6} from "@medusajs/framework/http"7
8export default defineMiddlewares({9  routes: [10    {11      matcher: "/custom*",12      method: ["POST", "PUT"],13      middlewares: [14        (15          req: MedusaRequest, 16          res: MedusaResponse, 17          next: MedusaNextFunction18        ) => {19          console.log("Received a request!")20
21          next()22        },23      ],24    },25  ],26})

This example applies the middleware only when a POST or PUT request is sent to an API route path starting with /custom, changing the middleware from a global middleware to a route middleware.

Test the Route Middleware#

To test the middleware:

  1. Start the application:
  1. Send a POST request to any API route starting with /custom.
  2. See the following message in the terminal:
Terminal
Received a request!

When to Use Middlewares#

Use middlewares when
  • You want to protect API routes by a custom condition.
  • You're modifying the request body.

Middleware Function Parameters#

The middleware function accepts three parameters:

  1. A request object of type MedusaRequest.
  2. A response object of type MedusaResponse.
  3. A function of type MedusaNextFunction that executes the next middleware in the stack.
ImportantYou must call the next function in the middleware. Otherwise, other middlewares and the API route handler won’t execute.

Middleware for Routes with Path Parameters#

To indicate a path parameter in a middleware's matcher pattern, use the format :{param-name}.

For example:

src/api/middlewares.ts
6} from "@medusajs/framework/http"7
8export default defineMiddlewares({9  routes: [10    {11      matcher: "/custom/:id",12      middlewares: [13        // ...14      ],15    },16  ],17})

This applies a middleware to the routes defined in the file src/api/custom/[id]/route.ts.


Request URLs with Trailing Backslashes#

A middleware whose matcher pattern doesn't end with a backslash won't be applied for requests to URLs with a trailing backslash.

For example, consider you have the following middleware:

src/api/middlewares.ts
6} from "@medusajs/framework/http"7
8export default defineMiddlewares({9  routes: [10    {11      matcher: "/custom",12      middlewares: [13        (14          req: MedusaRequest, 15          res: MedusaResponse, 16          next: MedusaNextFunction17        ) => {18          console.log("Received a request!")19
20          next()21        },22      ],23    },24  ],25})

If you send a request to http://localhost:9000/custom, the middleware will run.

However, if you send a request to http://localhost:9000/custom/, the middleware won't run.

In general, avoid adding trailing backslashes when sending requests to API routes.


Middlewares and Route Ordering#

NoteThe ordering explained in this section was added in Medusa v2.6

The Medusa application registers middlewares and API route handlers in the following order:

  1. Global middlewares in the following order:
    1. Global middleware defined in the Medusa's core.
    2. Global middleware defined in the plugins (in the order the plugins are registered in).
    3. Global middleware you define in the application.
  2. Route middlewares in the following order:
    1. Route middleware defined in the Medusa's core.
    2. Route middleware defined in the plugins (in the order the plugins are registered in).
    3. Route middleware you define in the application.
  3. API routes in the following order:
    1. API routes defined in the Medusa's core.
    2. API routes defined in the plugins (in the order the plugins are registered in).
    3. API routes you define in the application.

Middlewares Sorting#

On top of the previous ordering, Medusa sorts global and route middlewares based on their matcher pattern in the following order:

  1. Wildcard matchers. For example, /custom*.
  2. Regex matchers. For example, /custom/(products|collections).
  3. Static matchers without parameters. For example, /custom.
  4. Static matchers with parameters. For example, /custom/:id.

For example, if you have the following middlewares:

src/api/middlewares.ts
1export default defineMiddlewares({2  routes: [3    {4      matcher: "/custom/:id",5      middlewares: [/* ... */],6    },7    {8      matcher: "/custom",9      middlewares: [/* ... */],10    },11    {12      matcher: "/custom*",13      method: ["GET"],14      middlewares: [/* ... */],15    },16    {17      matcher: "/custom/:id",18      method: ["GET"],19      middlewares: [/* ... */],20    },21  ],22})

The global middlewares are sorted into the following order before they're registered:

  1. Global middleware /custom.
  2. Global middleware /custom/:id.

And the route middlewares are sorted into the following order before they're registered:

  1. Route middleware /custom*.
  2. Route middleware /custom/:id.

Then, the middlwares are registered in the order mentioned earlier, with global middlewares first, then the route middlewares.

Middlewares and Route Execution Order#

When a request is sent to an API route, the global middlewares are executed first, then the route middlewares, and finally the route handler.

For example, consider you have the following middlewares:

src/api/middlewares.ts
1export default defineMiddlewares({2  routes: [3    {4      matcher: "/custom",5      middlewares: [6        (req, res, next) => {7          console.log("Global middleware")8          next()9        },10      ],11    },12    {13      matcher: "/custom",14      method: ["GET"],15      middlewares: [16        (req, res, next) => {17          console.log("Route middleware")18          next()19        },20      ],21    },22  ],23})

When you send a request to /custom route, the following messages are logged in the terminal:

Terminal
Global middlewareRoute middlewareHello from custom! # message logged from API route handler

The global middleware runs first, then the route middleware, and finally the route handler, assuming that it logs the message Hello from custom!.


Overriding Middlewares#

A middleware can not override an existing middleware. Instead, middlewares are added to the end of the middleware stack.

For example, if you define a custom validation middleware, such as validateAndTransformBody, on an existing route, then both the original and the custom validation middleware will run.

Was this chapter helpful?
Edited Mar 4·Edit this page
Ask Anything
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