Middlewares
In this document, you’ll learn how to add a middleware to existing or custom API Routes in Medusa.
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
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 inmatcher
.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 defaultjson
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 defaultjson
body parser middleware. It accepts the following configurations:sizeLimit
: A number indicating the maximum body size allowed in bytes. By default, it's1000
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 than1000
bytes.
errorHandler
: This property can have two types of values:
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:
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.
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:
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:
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
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