on GitHub" data-tooltip-id=":Rblcldtb:">v2.6·
In this chapter, you’ll learn about middlewares and how to create them.
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.
There are two types of middlewares:
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.
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:
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
.
To test the middleware:
/custom
.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:
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.
To test the middleware:
POST
request to any API route starting with /custom
.The middleware function accepts three parameters:
MedusaRequest
.MedusaResponse
.MedusaNextFunction
that executes the next middleware in the stack.next
function in the middleware. Otherwise, other middlewares and the API route handler won’t execute.To indicate a path parameter in a middleware's matcher
pattern, use the format :{param-name}
.
For example:
This applies a middleware to the routes defined in the file src/api/custom/[id]/route.ts
.
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:
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.
The Medusa application registers middlewares and API route handlers in the following order:
On top of the previous ordering, Medusa sorts global and route middlewares based on their matcher pattern in the following order:
/custom*
./custom/(products|collections)
./custom
./custom/:id
.For example, if you have the following middlewares:
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:
/custom
./custom/:id
.And the route middlewares are sorted into the following order before they're registered:
/custom*
./custom/:id
.Then, the middlwares are registered in the order mentioned earlier, with global middlewares first, then the route middlewares.
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:
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:
The global middleware runs first, then the route middleware, and finally the route handler, assuming that it logs the message Hello from custom!
.
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.