Skip to main content

Middlewares

In this document, you’ll learn how to add a middleware to an existing or custom route in Medusa.

Overview

As the Medusa backend is built on top of Express, Express’s features can be utilized during your development with Medusa.

One feature in particular is adding a middleware. A middleware is a function that has access to the request and response objects.

A middleware can be used to perform an action when an endpoint is called or modify the response, among other usages.

You can add a middleware to an existing route in the Medusa backend, a route in a plugin, or your custom routes.


How to Add a Middleware

Step 1: Create the Middleware File

You can organize your middlewares as you see fit, but it's recommended to create Middlewares in the src/api/middlewaresCopy to Clipboard directory. It's recommended to create each middleware in a different file.

Each file should export a middleware function that accepts three parameters:

  1. The first one is an Express request object. It can be used to get details related to the request or resolve resources from the dependency container.
  2. The second one is an Express response object. It can be used to modify the response, or in some cases return a response without executing the associated endpoint.
  3. The third one is a next middleware function that ensures that other middlewares and the associated endpoint are executed.

You can learn more about Middlewares and their capabilities in Express’s documentation.

Here's an example of a middleware:

src/api/middlewares/custom-middleware.ts
export function customMiddleware(req, res, next) {
// TODO perform an action

next()
}
Report Incorrect CodeCopy to Clipboard

Step 2: Apply Middleware on an Endpoint

To apply a middleware on any endpoint, you can use the same router defined in src/api/index.tsCopy to Clipboard or any other router that is used or exported by src/api/index.tsCopy to Clipboard. For example:

The examples used here don't apply Cross-Origin Resource Origin (CORS) options for simplicity. Make sure to apply them, especially for core routes, as explained in the Create Endpoint documentation.

src/api/index.ts
import { Router } from "express"
import {
customMiddleware,
} from "./middlewares/custom-middleware"

export default (rootDirectory, pluginOptions) => {
const router = Router()

// custom route
router.get("/hello", (req, res) => {
res.json({
message: "Welcome to My Store!",
})
})

// middleware for the custom route
router.use("/hello", customMiddleware)

// middleware for core route
router.use("/store/products", customMiddleware)

return router
}
Report Incorrect CodeCopy to Clipboard

Step 3: Building Files

Similar to custom endpoints, you must transpile the files under srcCopy to Clipboard into the distCopy to Clipboard directory for the backend to load them.

To do that, run the following command before running the Medusa backend:

yarn build
Report Incorrect CodeCopy to Clipboard

You can then test that the middleware is working by running the backend.


Registering 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 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 req.scope.registerCopy to Clipboard method:

src/api/middlewares/custom-middleware.ts
export function customMiddleware(req, res, next) {
// TODO perform an action

req.scope.register({
customResource: {
resolve: () => "my custom resource",
},
})

next()
}
Report Incorrect CodeCopy to Clipboard

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
Report Incorrect CodeCopy to Clipboard

Notice that you have 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 is not 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.SCOPEDCopy to Clipboard or Lifetime.TRANSIENTCopy to Clipboard. Services that have a Lifetime.SINGLETONCopy to Clipboard 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.SCOPEDCopy to Clipboard. However, if you extend a core service, you must change the lifetime since the default lifetime for core services is Lifetime.SINGLETONCopy to Clipboard.

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
Report Incorrect CodeCopy to Clipboard

Troubleshooting

AwilixResolutionError: Could Not Resolve X

If you're registering a custom resource within a middleware, for example a logged-in user, then make sure that all services that are using it have their LIFE_TIMECopy to Clipboard static property either set to Lifetime.SCOPEDCopy to Clipboard or Lifetime.TRANSIENTCopy to Clipboard. This mainly applies for services in the core Medusa package, as, by default, their lifetime is Lifetime.SINGLETONCopy to Clipboard.

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

// ...
}

export default ProductService
Report Incorrect CodeCopy to Clipboard

This may require you to extend a service as explained in this documentation if necessary.

If you're unsure which service you need to change its LIFE_TIMECopy to Clipboard property, it should be mentioned along with the AwilixResolutionErrorCopy to Clipboard message. For example:

AwilixResolutionError: Could not resolve 'loggedInUser'.

Resolution path: cartService -> productService -> loggedInUser

As shown in the resolution path, you must change the LIFE_TIMECopy to Clipboard property of both cartServiceCopy to Clipboard and productServiceCopy to Clipboard to Lifetime.SCOPEDCopy to Clipboard or Lifetime.TRANSIENTCopy to Clipboard.

You can learn about the service lifetime in the Create a Service documentation.

AwilixResolutionError: Could Not Resolve X (Custom Registration)

When you register a custom resource using a middleware, make sure that when you use it in a service's constructor you wrap it in a try-catch block. This can cause an error when the Medusa backend first runs, especially if the service is used within a subscriber. Subscribers are built the first time the Medusa backend runs, meaning that their dependencies are registered at that point. Since your custom resource hasn't been registered at this point, it will cause an AwilixResolutionErrorCopy to Clipboard when the backend tries to resolve it.

For that reason, and to avoid other similar situations, make sure to always wrap your custom resources in a try-catch block when you use them inside the constructor of a service. For example:

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
Report Incorrect CodeCopy to Clipboard

You can learn more about this in the Middlewares documentation.


See Also

Was this page helpful?