docs: updates to middlewares and protected API routes + new chapter (#12419)
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { Table, CodeTabs, CodeTab } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Middlewares`,
|
||||
}
|
||||
@@ -12,6 +14,8 @@ A middleware is a function executed when a request is sent to an API Route. It's
|
||||
|
||||
Middlewares are used to guard API routes, parse request content types other than `application/json`, manipulate request data, and more.
|
||||
|
||||

|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
As Medusa's server is based on Express, you can use any [Express middleware](https://expressjs.com/en/resources/middleware.html).
|
||||
@@ -22,19 +26,59 @@ As Medusa's server is based on Express, you can use any [Express middleware](htt
|
||||
|
||||
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).
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>
|
||||
Type
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
Description
|
||||
</Table.HeaderCell>
|
||||
<Table.HeaderCell>
|
||||
Example
|
||||
</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
Global Middleware
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
A middleware that applies to all routes matching a specified pattern.
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
`/custom*` applies to all routes starting with `/custom`
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
Route Middleware
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
A middleware that applies to routes matching a specified pattern and HTTP method(s).
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
A middleware that applies to all `POST` requests to routes starting with `/custom`.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
||||
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?
|
||||
## 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:
|
||||
|
||||
<CodeTabs group="middleware-type">
|
||||
<CodeTab label="Global Middleware" value="global-middleware">
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import {
|
||||
defineMiddlewares,
|
||||
@@ -63,46 +107,17 @@ export default defineMiddlewares({
|
||||
})
|
||||
```
|
||||
|
||||
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](https://github.com/pillarjs/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:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. Send a request to any API route starting with `/custom`.
|
||||
3. See the following message in the terminal:
|
||||
|
||||
```bash
|
||||
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:
|
||||
</CodeTab>
|
||||
<CodeTab label="Route Middleware" value="route-middleware">
|
||||
|
||||
export const highlights = [["12", "method", "Apply the middleware only on `POST` requests"]]
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights}
|
||||
import {
|
||||
defineMiddlewares,
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
defineMiddlewares,
|
||||
} from "@medusajs/framework/http"
|
||||
|
||||
export default defineMiddlewares({
|
||||
@@ -126,9 +141,16 @@ export default defineMiddlewares({
|
||||
})
|
||||
```
|
||||
|
||||
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.
|
||||
</CodeTab>
|
||||
</CodeTabs>
|
||||
|
||||
### Test the Route Middleware
|
||||
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](https://github.com/pillarjs/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:
|
||||
|
||||
@@ -138,7 +160,7 @@ To test the middleware:
|
||||
npm run dev
|
||||
```
|
||||
|
||||
2. Send a `POST` request to any API route starting with `/custom`.
|
||||
2. 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.
|
||||
3. See the following message in the terminal:
|
||||
|
||||
```bash
|
||||
@@ -149,12 +171,12 @@ Received a request!
|
||||
|
||||
## When to Use Middlewares
|
||||
|
||||
<Note type="success" title="Use middlewares when">
|
||||
Middlewares are useful for:
|
||||
|
||||
- You want to protect API routes by a custom condition.
|
||||
- You're modifying the request body.
|
||||
|
||||
</Note>
|
||||
- [Protecting API routes](../protected-routes/page.mdx) to ensure that only authenticated users can access them.
|
||||
- [Validating](../validation/page.mdx) request query and body parameters.
|
||||
- [Parsing](../parse-body/page.mdx) request content types other than `application/json`.
|
||||
- [Applying CORS](../cors/page.mdx) configurations to custom API routes.
|
||||
|
||||
---
|
||||
|
||||
@@ -172,12 +194,50 @@ You must call the `next` function in the middleware. Otherwise, other middleware
|
||||
|
||||
</Note>
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import {
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
defineMiddlewares,
|
||||
} from "@medusajs/framework/http"
|
||||
|
||||
export default defineMiddlewares({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom*",
|
||||
middlewares: [
|
||||
(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
console.log("Received a request!", req.body)
|
||||
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
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}`.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
A middleware applied on a route with path parameters is a route middleware.
|
||||
|
||||
</Note>
|
||||
|
||||
For example:
|
||||
|
||||
export const pathParamHighlights = [["11", ":id", "Indicates that the API route accepts an `id` path parameter."]]
|
||||
@@ -248,15 +308,19 @@ In general, avoid adding trailing backslashes when sending requests to API route
|
||||
|
||||
---
|
||||
|
||||
## Middlewares and Route Ordering
|
||||
## How Are Middlewares Ordered and Applied?
|
||||
|
||||
<Note>
|
||||
|
||||
The ordering explained in this section was added in [Medusa v2.6](https://github.com/medusajs/medusa/releases/tag/v2.6)
|
||||
The information explained in this section is applicable starting from [Medusa v2.6](https://github.com/medusajs/medusa/releases/tag/v2.6).
|
||||
|
||||
</Note>
|
||||
|
||||
The Medusa application registers middlewares and API route handlers in the following order:
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
1. Global middlewares in the following order:
|
||||
1. Global middleware defined in the Medusa's core.
|
||||
@@ -271,6 +335,48 @@ The Medusa application registers middlewares and API route handlers in the follo
|
||||
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.
|
||||
|
||||

|
||||
|
||||
For example, consider you have the following middlewares:
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
export default defineMiddlewares({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom",
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Global middleware")
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
matcher: "/custom",
|
||||
method: ["GET"],
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Route middleware")
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
When you send a request to `/custom` route, the following messages are logged in the terminal:
|
||||
|
||||
```bash
|
||||
Global middleware
|
||||
Route middleware
|
||||
Hello 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:
|
||||
@@ -317,50 +423,10 @@ And the route middlewares are sorted into the following order before they're reg
|
||||
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:
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
export default defineMiddlewares({
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom",
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Global middleware")
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
matcher: "/custom",
|
||||
method: ["GET"],
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Route middleware")
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
When you send a request to `/custom` route, the following messages are logged in the terminal:
|
||||
|
||||
```bash
|
||||
Global middleware
|
||||
Route middleware
|
||||
Hello 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
|
||||
@@ -368,3 +434,11 @@ The global middleware runs first, then the route middleware, and finally the rou
|
||||
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](../protected-routes/page.mdx#protect-custom-api-routes) 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](../page.mdx) that executes the same functionality as the original route, but with the middlewares you want.
|
||||
|
||||
Learn more in the [Override API Routes](../override/page.mdx) chapter.
|
||||
Reference in New Issue
Block a user