docs: updates to middlewares and protected API routes + new chapter (#12419)

This commit is contained in:
Shahed Nasser
2025-05-09 12:06:34 +03:00
committed by GitHub
parent 7c7e44d9fe
commit 28285f309a
10 changed files with 24651 additions and 24032 deletions
@@ -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.
![Diagram showcasing how a middleware is executed when a request is sent to an API route.](https://res.cloudinary.com/dza7lstvk/image/upload/v1746775148/Medusa%20Book/middleware-overview_wc2ws5.jpg)
<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:
![Diagram showcasing the order in which middlewares and route handlers are registered.](https://res.cloudinary.com/dza7lstvk/image/upload/v1746776911/Medusa%20Book/middleware-registration-overview_spc02f.jpg)
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.
![Diagram showcasing the order in which middlewares and route handlers are executed when a request is sent to an API route.](https://res.cloudinary.com/dza7lstvk/image/upload/v1746776172/Medusa%20Book/middleware-order-overview_h7kzfl.jpg)
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`.
![Diagram showcasing the order in which middlewares are sorted before being registered.](https://res.cloudinary.com/dza7lstvk/image/upload/v1746777297/Medusa%20Book/middleware-registration-sorting_oyfqhw.jpg)
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.
@@ -0,0 +1,135 @@
import { Table } from "docs-ui"
export const metadata = {
title: `${pageNumber} Override API Routes`,
}
# {metadata.title}
In this chapter, you'll learn the approach recommended when you need to override an existing API route in Medusa.
## Approaches to Consider Before Overriding API Routes
While building customizations in your Medusa application, you may need to make changes to existing API routes for your business use case.
Medusa provides the following approaches to customize API routes:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Approach
</Table.HeaderCell>
<Table.HeaderCell>
Description
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
[Pass Additional Data](../additional-data/page.mdx)
</Table.Cell>
<Table.Cell>
Pass custom data to the API route with custom validation.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
[Perform Custom Logic within an Existing Flows](../../workflows/workflow-hooks/page.mdx)
</Table.Cell>
<Table.Cell>
API routes execute workflows to perform business logic, which may have hooks that allow you to perform custom logic.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
[Use Custom Middlewares](../middlewares/page.mdx)
</Table.Cell>
<Table.Cell>
Use custom middlewares to perform custom logic before the API route is executed. However, you cannot remove or replace middlewares applied to existing API routes.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
[Listen to Events in Subscribers](../../events-and-subscribers/page.mdx)
</Table.Cell>
<Table.Cell>
Functionalities in API routes may trigger events that you can handle in subscribers. This is useful if you're performing an action that isn't integral to the API route's core functionality or response.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
If the above approaches do not meet your needs, you can consider the approaches mentioned in the rest of this chapter.
---
## Replicate, Don't Override API Routes
If the approaches mentioned in the [section above](#approaches-to-consider-before-overriding-api-routes) do not meet your needs, you can replicate an existing API route and modify it to suit your requirements.
By replicating instead of overriding, the original API route remains intact, allowing you to easily revert to the original functionality if needed. You can also update your Medusa version without worrying about breaking changes in the original API route.
---
## How to Replicate an API Route?
Medusa's API routes are generally slim and use logic contained in [workflows](../../workflows/page.mdx). So, creating a custom route based on the original route is straightforward.
You can view the source code for Medusa's API routes in the [Medusa GitHub repository](https://github.com/medusajs/medusa/tree/develop/packages/medusa/src/api).
For example, if you need to allow vendors to access the `POST /admin/products` API route, you can create an API route in your Medusa project at `src/api/vendor/products/route.ts` with the [same code as the original route](https://github.com/medusajs/medusa/blob/develop/packages/medusa/src/api/admin/products/route.ts#L88). Then, you can make changes to it or its middlewares.
---
## When to Replicate an API Route?
Some examples of when you might want to replicate an API route include:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Use Case
</Table.HeaderCell>
<Table.HeaderCell>
Description
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
Custom Validation
</Table.Cell>
<Table.Cell>
You want to change the validation logic for a specific API route, and the [Additional Data](../additional-data/page.mdx) isn't sufficient or applicable.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Change Authentication
</Table.Cell>
<Table.Cell>
You want to remove required authentication for a specific API route, or you want to allow custom [actor types](!resources!/commerce-modules/auth/auth-identity-and-actor-types) to access existing protected API routes.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Custom Response
</Table.Cell>
<Table.Cell>
You want to change the response format of an existing API route.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
Override Middleware
</Table.Cell>
<Table.Cell>
You want to override the middleware applied on existing API routes. Because of [how middlewares are ordered and applied](../middlewares/page.mdx#how-are-middlewares-ordered-and-applied), you can't remove or replace middlewares applied to existing API routes.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
@@ -1,36 +1,102 @@
import { Table } from "docs-ui"
export const metadata = {
title: `${pageNumber} Protected Routes`,
title: `${pageNumber} Protected API Routes`,
}
# {metadata.title}
In this chapter, youll learn how to create protected routes.
In this chapter, youll learn how to create protected API routes.
## What is a Protected Route?
## What is a Protected API Route?
A protected route is a route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access.
By default, an API route is publicly accessible, meaning that any user can access it without authentication. This is useful for public API routes that allow users to browse products, view collections, and so on.
---
A protected API route is an API route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access.
## Default Protected Routes
Medusa applies an authentication guard on routes starting with `/admin`, including custom API routes.
Requests to `/admin` must be user-authenticated to access the route.
Protected API routes are useful for routes that require user authentication, such as creating a product or managing an order. These routes must only be accessed by authenticated admin users.
<Note title="Tip">
Refer to the API Reference for [Admin](!api!/admin#authentication) and [Store](!api!/store#authentication) authentication methods.
Refer to the API Reference for [Admin](!api!/admin#authentication) and [Store](!api!/store#authentication) to learn how to send authenticated requests.
</Note>
---
## Default Protected Routes
Any API route, including your custom API routes, are protected if they start with the following prefixes:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>
Route Prefix
</Table.HeaderCell>
<Table.HeaderCell>
Access
</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`/admin`
</Table.Cell>
<Table.Cell>
Only authenticated admin users can access.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`/store/customers/me`
</Table.Cell>
<Table.Cell>
Only authenticated customers can access.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
Refer to the API Reference for [Admin](!api!/admin#authentication) and [Store](!api!/store#authentication) to learn how to send authenticated requests.
### Opt-Out of Default Authentication Requirement
If you create a custom API route under a prefix that is protected by default, you can opt-out of the authentication requirement by exporting an `AUTHENTICATE` variable in the route file with its value set to `false`.
For example, to disable authentication requirement for a custom API route created at `/admin/custom`, you can export an `AUTHENTICATE` variable in the route file:
```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]}
import type {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
res.json({
message: "Hello",
})
}
export const AUTHENTICATE = false
```
Now, any request sent to the `/admin/custom` API route is allowed, regardless if the admin user is authenticated.
---
## Protect Custom API Routes
To protect custom API Routes to only allow authenticated customer or admin users, use the `authenticate` middleware from the Medusa Framework.
You can protect API routes using the `authenticate` [middleware](../middlewares/page.mdx) from the Medusa Framework. When applied to a route, the middleware checks that:
For example:
- The correct actor type (for example, `user`, `customer`, or a custom actor type) is authenticated.
- The correct authentication method is used (for example, `session`, `bearer`, or `api-key`).
For example, you can add the `authenticate` middleware in the `src/api/middlewares.ts` file to protect a custom API route:
export const highlights = [
[
@@ -119,111 +185,148 @@ export default defineMiddlewares({
})
```
### Override Authentication for Medusa's API Routes
In some cases, you may want to override the authentication requirement for Medusa's API routes. For example, you may want to allow custom actor types to access existing protected API routes.
It's not possible to change the [authentication middleware](../middlewares/page.mdx) applied to an existing API route. Instead, you need to replicate the API route and apply the authentication middleware to it.
Learn more in the [Override API Routes](../override/page.mdx) chapter.
---
## Authentication Opt-Out
## Access Authentication Details in API Routes
To disable the authentication guard on custom routes under the `/admin` path prefix, export an `AUTHENTICATE` variable in the route file with its value set to `false`.
To access the authentication details in an API route, such as the logged-in user's ID, set the type of the first request parameter to `AuthenticatedMedusaRequest`. It extends `MedusaRequest`:
For example:
```ts title="src/api/admin/custom/route.ts" highlights={[["15"]]}
import type {
AuthenticatedMedusaRequest,
```ts highlights={[["7", "AuthenticatedMedusaRequest"]]}
import type {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: AuthenticatedMedusaRequest,
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
res.json({
message: "Hello",
})
// ...
}
export const AUTHENTICATE = false
```
Now, any request sent to the `/admin/custom` API route is allowed, regardless if the admin user is authenticated.
---
## Authenticated Request Type
To access the authentication details in an API route, such as the logged-in user's ID, set the type of the first request parameter to `AuthenticatedMedusaRequest`. It extends `MedusaRequest`.
The `auth_context.actor_id` property of `AuthenticatedMedusaRequest` holds the ID of the authenticated user or customer. If there isn't any authenticated user or customer, `auth_context` is `undefined`.
For example:
```ts title="src/api/store/custom/route.ts" highlights={[["10", "actor_id"]]}
import type {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const id = req.auth_context?.actor_id
// ...
}
```
In this example, you retrieve the ID of the authenticated user, customer, or custom actor type from the `auth_context` property of the `AuthenticatedMedusaRequest` object.
<Note>
If you opt-out of authentication in a route as mentioned in the [previous section](#authentication-opt-out), you can't access the authenticated user or customer anymore. Use the [authenticate middleware](#protect-custom-api-routes) instead.
If you opt-out of authentication in a route as mentioned in the [Opt-Out section](#opt-out-of-default-authentication-requirement), you can't access the authenticated user or customer anymore. Use the [authenticate middleware](#protect-custom-api-routes) instead to protect the route.
</Note>
### Retrieve Logged-In Customer's Details
You can access the logged-in customers ID in all API routes starting with `/store` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object.
You can access the logged-in customers ID in all API routes starting with `/store` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. You can then use [Query](../../module-links/query/page.mdx) to retrieve the customer details, or pass the ID to a [workflow](../../workflows/page.mdx) that performs business logic.
For example:
```ts title="src/api/store/custom/route.ts" highlights={[["19", "req.auth_context.actor_id", "Access the logged-in customer's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
export const customerHighlights = [
["10", "customerId", "Retrieve the logged-in customer's ID."],
["13", "query", "Retrieve the customer details."],
["20", "throwIfKeyNotFound", "Throw an error if the customer with\nthe specified ID is not found."],
]
```ts title="src/api/store/custom/route.ts" highlights={customerHighlights}
import type {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
import { ICustomerModuleService } from "@medusajs/framework/types"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
if (req.auth_context?.actor_id) {
// retrieve customer
const customerModuleService: ICustomerModuleService = req.scope.resolve(
Modules.CUSTOMER
)
const customerId = req.auth_context?.actor_id
const query = req.scope.resolve("query")
const customer = await customerModuleService.retrieveCustomer(
req.auth_context.actor_id
)
}
const { data: [customer] } = await query.graph({
entity: "customer",
fields: ["*"],
filters: {
id: customerId,
},
}, {
throwIfKeyNotFound: true,
})
// ...
// do something with the customer data...
}
```
In this example, you resolve the Customer Module's main service, then use it to retrieve the logged-in customer, if available.
In this example, you retrieve the customer's ID and resolve Query from the [Medusa container](../../medusa-container/page.mdx).
Then, you use Query to retrieve the customer details. The `throwIfKeyNotFound` option throws an error if the customer with the specified ID is not found.
After that, you can use the customer's details in your API route.
### Retrieve Logged-In Admin User's Details
You can access the logged-in admin users ID in all API Routes starting with `/admin` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object.
You can access the logged-in admin users ID in all API routes starting with `/admin` using the `auth_context.actor_id` property of the `AuthenticatedMedusaRequest` object. You can then use [Query](../../module-links/query/page.mdx) to retrieve the user details, or pass the ID to a [workflow](../../workflows/page.mdx) that performs business logic.
For example:
```ts title="src/api/admin/custom/route.ts" highlights={[["17", "req.auth_context.actor_id", "Access the logged-in admin user's ID."]]} collapsibleLines="1-7" expandButtonLabel="Show Imports"
export const adminHighlights = [
["10", "userId", "Retrieve the logged-in admin user's ID."],
["13", "query", "Retrieve the user details."],
["20", "throwIfKeyNotFound", "Throw an error if the user with\nthe specified ID is not found."],
]
```ts title="src/api/admin/custom/route.ts" highlights={adminHighlights}
import type {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
import { IUserModuleService } from "@medusajs/framework/types"
export const GET = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const userModuleService: IUserModuleService = req.scope.resolve(
Modules.USER
)
const userId = req.auth_context?.actor_id
const query = req.scope.resolve("query")
const user = await userModuleService.retrieveUser(
req.auth_context.actor_id
)
const { data: [user] } = await query.graph({
entity: "user",
fields: ["*"],
filters: {
id: userId,
},
}, {
throwIfKeyNotFound: true,
})
// ...
// do something with the user data...
}
```
In the route handler, you resolve the User Module's main service, then use it to retrieve the logged-in admin user.
In this example, you retrieve the admin user's ID and resolve Query from the [Medusa container](../../medusa-container/page.mdx).
Then, you use Query to retrieve the user details. The `throwIfKeyNotFound` option throws an error if the user with the specified ID is not found.
After that, you can use the user's details in your API route.
+4 -3
View File
@@ -22,7 +22,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/admin/widgets/page.mdx": "2024-12-09T16:43:24.260Z",
"app/learn/fundamentals/data-models/page.mdx": "2025-03-18T07:55:56.252Z",
"app/learn/fundamentals/modules/remote-link/page.mdx": "2024-09-30T08:43:53.127Z",
"app/learn/fundamentals/api-routes/protected-routes/page.mdx": "2025-03-17T11:47:10.101Z",
"app/learn/fundamentals/api-routes/protected-routes/page.mdx": "2025-05-09T07:57:32.929Z",
"app/learn/fundamentals/workflows/add-workflow-hook/page.mdx": "2024-12-09T14:42:39.693Z",
"app/learn/fundamentals/events-and-subscribers/data-payload/page.mdx": "2025-05-01T15:30:08.421Z",
"app/learn/fundamentals/workflows/advanced-example/page.mdx": "2024-09-11T10:46:59.975Z",
@@ -46,7 +46,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/admin/tips/page.mdx": "2025-03-11T08:54:12.028Z",
"app/learn/fundamentals/api-routes/cors/page.mdx": "2025-03-11T08:54:26.281Z",
"app/learn/fundamentals/admin/ui-routes/page.mdx": "2025-02-24T09:35:11.752Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2025-03-24T06:43:02.362Z",
"app/learn/fundamentals/api-routes/middlewares/page.mdx": "2025-05-09T07:56:04.125Z",
"app/learn/fundamentals/modules/isolation/page.mdx": "2024-12-09T11:02:38.087Z",
"app/learn/fundamentals/data-models/index/page.mdx": "2025-03-18T07:59:07.798Z",
"app/learn/fundamentals/custom-cli-scripts/page.mdx": "2024-10-23T07:08:55.898Z",
@@ -119,5 +119,6 @@ export const generatedEditDates = {
"app/learn/fundamentals/data-models/properties/page.mdx": "2025-03-18T07:57:17.826Z",
"app/learn/fundamentals/framework/page.mdx": "2025-04-25T14:26:25.000Z",
"app/learn/fundamentals/api-routes/retrieve-custom-links/page.mdx": "2025-04-25T14:26:25.000Z",
"app/learn/fundamentals/workflows/errors/page.mdx": "2025-04-25T14:26:25.000Z"
"app/learn/fundamentals/workflows/errors/page.mdx": "2025-04-25T14:26:25.000Z",
"app/learn/fundamentals/api-routes/override/page.mdx": "2025-05-09T08:01:24.493Z"
}
+10
View File
@@ -642,6 +642,16 @@ export const generatedSidebars = [
"children": [],
"chapterTitle": "3.6.11. Retrieve Custom Links",
"number": "3.6.11."
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/api-routes/override",
"title": "Override API Routes",
"children": [],
"chapterTitle": "3.6.12. Override API Routes",
"number": "3.6.12."
}
],
"chapterTitle": "3.6. API Routes",
File diff suppressed because it is too large Load Diff
+5
View File
@@ -339,6 +339,11 @@ export const sidebars = [
path: "/learn/fundamentals/api-routes/retrieve-custom-links",
title: "Retrieve Custom Links",
},
{
type: "link",
path: "/learn/fundamentals/api-routes/override",
title: "Override API Routes",
},
],
},
{
@@ -85,7 +85,7 @@ const createManagerStep = createStep(
}: Pick<CreateManagerWorkflowInput, "manager">,
{ container }) => {
const managerModuleService: ManagerModuleService =
container.resolve("managerModuleService")
container.resolve("manager")
const manager = await managerModuleService.createManager(
managerData
@@ -243,12 +243,18 @@ export async function GET(
req: AuthenticatedMedusaRequest,
res: MedusaResponse
): Promise<void> {
const managerModuleService: ManagerModuleService =
req.scope.resolve("managerModuleService")
const query = req.scope.resolve("query")
const managerId = req.auth_context?.actor_id
const manager = await managerModuleService.retrieveManager(
req.auth_context.actor_id
)
const { data: [manager] } = await query.graph({
entity: "manager",
fields: ["*"],
filters: {
id: managerId,
},
}, {
throwIfKeyNotFound: true,
})
res.json({ manager })
}
@@ -335,7 +341,7 @@ const deleteManagerStep = createStep(
{ id }: DeleteManagerWorkflow,
{ container }) => {
const managerModuleService: ManagerModuleService =
container.resolve("managerModuleService")
container.resolve("manager")
const manager = await managerModuleService.retrieve(id)
@@ -345,7 +351,7 @@ const deleteManagerStep = createStep(
},
async ({ manager }, { container }) => {
const managerModuleService: ManagerModuleService =
container.resolve("managerModuleService")
container.resolve("manager")
await managerModuleService.createManagers(manager)
}
+1 -1
View File
@@ -193,7 +193,7 @@ export const generatedEditDates = {
"app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00",
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2025-01-07T09:02:27.235Z",
"app/commerce-modules/api-key/page.mdx": "2025-04-17T08:48:16.605Z",
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-25T13:26:27.176Z",
"app/commerce-modules/auth/create-actor-type/page.mdx": "2025-05-09T07:09:41.615Z",
"app/infrastructure-modules/page.mdx": "2025-04-17T08:29:00.388Z",
"app/infrastructure-modules/workflow-engine/redis/page.mdx": "2024-11-19T16:37:47.262Z",
"app/infrastructure-modules/notification/sendgrid/page.mdx": "2025-03-27T17:39:10.840Z",
@@ -1301,6 +1301,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/deleteLineItemsWorkflow",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "processPaymentWorkflow",
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/processPaymentWorkflow",
"children": []
},
{
"loaded": true,
"isPathHref": true,
@@ -5327,6 +5335,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/orderExchangeAddNewItemWorkflow",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "processPaymentWorkflow",
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/processPaymentWorkflow",
"children": []
},
{
"loaded": true,
"isPathHref": true,