215 lines
6.3 KiB
Plaintext
215 lines
6.3 KiB
Plaintext
---
|
||
addHowToData: true
|
||
---
|
||
|
||
import DetailsList from '@site/src/components/DetailsList'
|
||
import ServiceLifetimeSection from '../../troubleshooting/awilix-resolution-error/_service-lifetime.md'
|
||
import CustomRegistrationSection from '../../troubleshooting/awilix-resolution-error/_custom-registration.md'
|
||
|
||
# Middlewares
|
||
|
||
In this document, you’ll learn how to add a middleware to existing or custom API Routes in Medusa.
|
||
|
||
:::tip
|
||
|
||
v1.17.2 of `@medusajs/medusa` introduced a new approach to creating middlewares using a single `middlewares.ts` file. You can still use the [Express Router Approach](./add-middleware-express-route.mdx), however, it's highly recommended that you start using this new approach.
|
||
|
||
:::
|
||
|
||
## Basic Implementation
|
||
|
||
```ts title=src/api/middlewares.ts
|
||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||
import type {
|
||
MedusaNextFunction,
|
||
MedusaRequest,
|
||
MedusaResponse,
|
||
} from "@medusajs/medusa"
|
||
|
||
const storeMiddleware = (
|
||
req: MedusaRequest,
|
||
res: MedusaResponse,
|
||
next: MedusaNextFunction
|
||
) => {
|
||
// do something
|
||
next()
|
||
}
|
||
|
||
export const config: MiddlewaresConfig = {
|
||
routes: [
|
||
{
|
||
matcher: "/store/*",
|
||
middlewares: [storeMiddleware],
|
||
},
|
||
],
|
||
}
|
||
```
|
||
|
||
A middleware is a function that has access to the `MedusaRequest` and `MedusaResponse` objects that are passed to API Route method handlers.
|
||
|
||
Middlewares are used to perform an action when a request is sent to an API Route, or modify the response of an API route, among other usages.
|
||
|
||
### middlewares.ts File
|
||
|
||
Middlewares are defined in the `src/api/middlewares.ts` file. This file must expose a config object of type `MiddlewaresConfig` imported from `@medusajs/medusa`.
|
||
|
||
This object accepts a parameter `routes`, whose value is an array of middleware route objects. Each middleware route object accepts the following properties:
|
||
|
||
- The `matcher` property accepts a string or a regular expression that will be used to check whether the middlewares should be applied on a API Routes.
|
||
- The `middlewares` property is an array of middlewares that should be applied on API Routes matching the pattern specified in `matcher`.
|
||
|
||
---
|
||
|
||
## Build Files
|
||
|
||
Similar to custom API Routes, you must transpile the files under `src` into the `dist` directory for the backend to load them.
|
||
|
||
To do that, run the following command before running the Medusa backend:
|
||
|
||
```bash npm2yarn
|
||
npm run build
|
||
```
|
||
|
||
You can then test that the middleware is working by running the backend.
|
||
|
||
---
|
||
|
||
## Register New Resources in Dependency Container
|
||
|
||
In some cases, you may need to register a resource to use within your commerce application. For example, you may want to register the logged-in user to access it in other resources, such as services. You can do that in your middleware.
|
||
|
||
:::tip
|
||
|
||
If you want to register a logged-in user and access it in your resources, you can check out [this example guide](./example-logged-in-user.mdx).
|
||
|
||
:::
|
||
|
||
To register a new resource in the dependency container, use the `MedusaRequest` object's `scope.register` method. It accepts an object, where each key is the name to be registered in the dependency container, and its value is an object that has a `resolve` property.
|
||
|
||
The `resolve`'s value is a function that returns the resource to be registered in the dependency container.
|
||
|
||
For example:
|
||
|
||
```ts title=src/api/middlewares.ts
|
||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||
import type {
|
||
MedusaNextFunction,
|
||
MedusaRequest,
|
||
MedusaResponse,
|
||
} from "@medusajs/medusa"
|
||
|
||
const customResource = (
|
||
req: MedusaRequest,
|
||
res: MedusaResponse,
|
||
next: MedusaNextFunction) => {
|
||
req.scope.register({
|
||
customResource: {
|
||
resolve: () => "my custom resource",
|
||
},
|
||
})
|
||
|
||
next()
|
||
}
|
||
|
||
export const config: MiddlewaresConfig = {
|
||
routes: [
|
||
{
|
||
matcher: "/store/*",
|
||
middlewares: [customResource],
|
||
},
|
||
],
|
||
}
|
||
```
|
||
|
||
You can then load this new resource within other resources. For example, to load it in a service:
|
||
|
||
<!-- eslint-disable prefer-rest-params -->
|
||
|
||
```ts title=src/services/custom-service.ts
|
||
import { TransactionBaseService } from "@medusajs/medusa"
|
||
|
||
class CustomService extends TransactionBaseService {
|
||
|
||
constructor(container, options) {
|
||
super(...arguments)
|
||
|
||
// use the registered resource.
|
||
try {
|
||
container.customResource
|
||
} catch (e) {
|
||
// avoid errors when the backend first loads
|
||
}
|
||
}
|
||
}
|
||
|
||
export default CustomService
|
||
```
|
||
|
||
:::note
|
||
|
||
Make sure to wrap your usage of the new resource in a try-catch block when you use it in a constructor. This is to avoid errors that can arise when the backend first loads, as the resource isn't registered yet.
|
||
|
||
:::
|
||
|
||
### Note About Services Lifetime
|
||
|
||
If you want to access new registrations in the dependency container within a service, you must set the lifetime of the service either to `Lifetime.SCOPED` or `Lifetime.TRANSIENT`. Services that have a `Lifetime.SINGLETON` lifetime can't access new registrations since they're resolved and cached in the root dependency container beforehand. You can learn more in the [Create Services documentation](../services/create-service.mdx#service-life-time).
|
||
|
||
For custom services, no additional action is required as the default lifetime is `Lifetime.SCOPED`. However, if you extend a core service, you must change the lifetime since the default lifetime for core services is `Lifetime.SINGLETON`.
|
||
|
||
For example:
|
||
|
||
<!-- eslint-disable prefer-rest-params -->
|
||
|
||
```ts
|
||
import { Lifetime } from "awilix"
|
||
import {
|
||
ProductService as MedusaProductService,
|
||
} from "@medusajs/medusa"
|
||
|
||
// extending ProductService from the core
|
||
class ProductService extends MedusaProductService {
|
||
// The default life time for a core service is SINGLETON
|
||
static LIFE_TIME = Lifetime.SCOPED
|
||
|
||
constructor(container, options) {
|
||
super(...arguments)
|
||
|
||
// use the registered resource.
|
||
try {
|
||
container.customResource
|
||
} catch (e) {
|
||
// avoid errors when the backend first loads
|
||
}
|
||
}
|
||
|
||
// ...
|
||
}
|
||
|
||
export default ProductService
|
||
```
|
||
|
||
---
|
||
|
||
## Troubleshooting
|
||
|
||
<DetailsList
|
||
sections={[
|
||
{
|
||
title: 'AwilixResolutionError: Could Not Resolve X',
|
||
content: <ServiceLifetimeSection />
|
||
},
|
||
{
|
||
title: 'AwilixResolutionError: Could Not Resolve X (Custom Registration)',
|
||
content: <CustomRegistrationSection />
|
||
}
|
||
]}
|
||
/>
|
||
|
||
---
|
||
|
||
## See Also
|
||
|
||
- [Store API reference](https://docs.medusajs.com/api/store)
|
||
- [Admin API reference](https://docs.medusajs.com/api/admin)
|