Skip to main content
Skip to main content

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, however, it's highly recommended that you start using this new approach.

Basic Implementation

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

  • routes: an array of middleware route objects. Each middleware route object accepts the following properties:
    • matcher: a string or a regular expression that will be used to check whether the middlewares should be applied on a API Routes.
    • middlewares: an array of middlewares that should be applied on API Routes matching the pattern specified in matcher.
    • method: a string or an array of strings, where each value is the name of an HTTP method. This allows you to apply the middlewares only on the specified HTTP methods in this property. If not specified, the middlewares are applied on all HTTP methods.
    • bodyParser: This property can have two types of values:
      • false: If this value is used, the default json body parser middleware is disabled. This allows you to override the body parser with your own middleware. For example, it's useful when changing the default parser for webhooks.
      • object: You can pass an object with configurations for the default json body parser middleware. It accepts the following configurations:
        • sizeLimit: A number indicating the maximum body size allowed in bytes. By default, it's 1000 bytes. If a request body is greater than that, an error will be thrown. So, you can set this configuration if you expect the request body size of an API route to be greater than 1000 bytes.
  • errorHandler: This property can have two types of values:
    • false: If this value is used, the default error handler is disabled. Learn more about its behavior here.
    • function: A middleware function used to define a new error handler. Learn more here.

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:

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.

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:

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:

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.

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:

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

AwilixResolutionError: Could Not Resolve X
AwilixResolutionError: Could Not Resolve X (Custom Registration)

See Also

Was this section helpful?