3.6.4. 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.

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

Diagram showcasing how a middleware is executed when a request is sent to an API route.

Tip: As Medusa's server is based on Express, you can use any Express middleware.

Middleware Types#

There are two types of middlewares:

Type

Description

Example

Global Middleware

A middleware that applies to all routes matching a specified pattern.

/custom* applies to all routes starting with /custom

Route Middleware

A middleware that applies to routes matching a specified pattern and HTTP method(s).

A middleware that applies to all POST requests to routes starting with /custom.

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 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:

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.
  • method: (optional) By default, a middleware is applied on all HTTP methods for a route. You can specify one or more HTTP methods to apply the middleware to in this option, making it a route middleware.

Test the Middleware#

To test the middleware:

  1. Start the application:
  1. Send a request to any API route starting with /custom. If you specified an HTTP method in the method property, make sure to use that method.
  2. See the following message in the terminal:
Terminal
Received a request!

When to Use Middlewares#

Middlewares are useful for:


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.
Important: You must call the next function in the middleware. Otherwise, other middlewares and the API route handler won’t execute.

For example:

src/api/middlewares.ts
1import { 2  MedusaNextFunction, 3  MedusaRequest, 4  MedusaResponse, 5  defineMiddlewares,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!", req.body)19
20          next()21        },22      ],23    },24  ],25})

This middleware logs the request body to the terminal, then calls the next function to execute the next middleware in the stack.


Middleware for Routes with Path Parameters#

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

Tip: A middleware applied on a route with path parameters is a route middleware.

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.


How Are Middlewares Ordered and Applied?#

Note: The information explained in this section is applicable starting from Medusa v2.6.

Middleware and Routes Execution Order#

The Medusa application registers middlewares and API route handlers in the following order, stacking them on top of each other:

Diagram showcasing the order in which middlewares and route handlers are registered.

  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.

Then, when a request is sent to an API route, the stack is executed in order: global middlewares are executed first, then the route middlewares, and finally the route handlers.

Diagram showcasing the order in which middlewares and route handlers are executed when a request is sent to an API route.

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!.

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.

Diagram showcasing the order in which middlewares are sorted before being registered.

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


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.

Similarly, if you add an authenticate middleware to an existing route, both the original and the custom authentication middleware will run. So, you can't override the original authentication middleware.

Alternative Solution to Overriding Middlewares#

If you need to change the middlewares applied to a route, you can create a custom API route that executes the same functionality as the original route, but with the middlewares you want.

Learn more in the Override API Routes chapter.

Was this chapter helpful?
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