Skip to main content

Example: Access Logged-In User

In this document, you’ll see an example of how you can use middlewares and endpoints to register the logged-in user in the dependency container of your commerce application. You can then access the logged-in user in other resources, such as services.

This guide showcases how to register the logged-in admin user, but you can apply the same steps if you want to register the current customer.

This documentation does not explain the basics of middlewares and endpoints. You can refer to their respective guides for more details about each.

Step 1: Create the Middleware

Create the file src/api/middlewares/logged-in-user.ts with the following content:

src/api/middlewares/logged-in-user.ts
import { User, UserService } from "@medusajs/medusa"

export async function registerLoggedInUser(req, res, next) {
let loggedInUser: User | null = null

if (req.user && req.user.userId) {
const userService =
req.scope.resolve("userService") as UserService
loggedInUser = await userService.retrieve(req.user.userId)
}

req.scope.register({
loggedInUser: {
resolve: () => loggedInUser,
},
})

next()
}

This retrieves the ID of the current user to retrieve an instance of it, then registers it in the scope under the name loggedInUser.


Step 2: Apply Middleware on Endpoint

If you don't have the cors package installed, make sure to install it first:

npm install cors

Then, create the file src/api/routes/create-product.ts with the following content:

src/api/routes/create-product.ts
import cors from "cors"
import { Router } from "express"
import {
registerLoggedInUser,
} from "../middlewares/logged-in-user"
import
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate"

const router = Router()

export default function (adminCorsOptions) {
// This router will be applied before the core routes.
// Therefore, the middleware will be executed
// before the create product handler is hit
router.use(
"/admin/products",
cors(adminCorsOptions),
authenticate(),
registerLoggedInUser
)
return router
}

In the example above, the middleware is applied on the /admin/products core endpoint. However, you can apply it on any other endpoint. You can also apply it to custom endpoints.

For endpoints that require Cross-Origin Resource Origin (CORS) options, such as core endpoints, you must pass the CORS options to the middleware as well since it will be executed before the underlying endpoint.

In the above code snippet, the authenticate middleware imported from @medusajs/medusa is used to ensure that the user is logged in first. If you're implementing this for middleware to register the logged-in customer, make sure to use the customer's authenticate middleware.


Step 3: Register Endpoint in the API

Create the file src/api/index.ts with the following content:

src/api/index.ts
import configLoader from "@medusajs/medusa/dist/loaders/config"
import createProductRouter from "./routes/create-product"

export default function (rootDirectory: string) {
const config = configLoader(rootDirectory)

const adminCors = {
origin: config.projectConfig.admin_cors.split(","),
credentials: true,
}

const productRouters = [
createProductRouter(adminCors),
]

return [...productRouters]
}

This exports an array of endpoints, one of them being the product endpoint that you applied the middleware on in the second step. You can export more endpoints as well.


Step 4: Use in a Service

You can now access the logged-in user in a service. For example, to access it in a custom service:

import { Lifetime } from "awilix"
import {
TransactionBaseService,
User,
} from "@medusajs/medusa"

class HelloService extends TransactionBaseService {

protected readonly loggedInUser_: User | null

constructor(container, options) {
super(...arguments)

try {
this.loggedInUser_ = container.loggedInUser
} catch (e) {
// avoid errors when backend first runs
}
}

// ...
}

export default HelloService

If you're accessing it in an extended core service, it’s important to change the lifetime of the service to Lifetime.SCOPED. For example:

import { Lifetime } from "awilix"
import {
ProductService as MedusaProductService,
User,
} from "@medusajs/medusa"

// extend core product service
class ProductService extends MedusaProductService {
// The default life time for a core service is SINGLETON
static LIFE_TIME = Lifetime.SCOPED

protected readonly loggedInUser_: User | null

constructor(container, options) {
super(...arguments)

this.loggedInUser_ = container.loggedInUser
}
}

export default ProductService

You can learn more about the importance of changing the service lifetime in the Middlewares documentation.


Step 5: Test it Out

To test out your implementation, run the following command in the root directory of the Medusa backend to transpile your changes:

npm run build

Then, run your backend with the following command:

npx @medusajs/medusa-cli develop

If you try accessing the endpoints you added the middleware to, you should see your implementation working as expected.


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_TIME static property either set to Lifetime.SCOPED or Lifetime.TRANSIENT. This mainly applies for services in the core Medusa package, as, by default, their lifetime 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

// ...
}

export default ProductService

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_TIME property, it should be mentioned along with the AwilixResolutionError message. For example:

AwilixResolutionError: Could not resolve 'loggedInUser'.

Resolution path: cartService -> productService -> loggedInUser

As shown in the resolution path, you must change the LIFE_TIME property of both cartService and productService to Lifetime.SCOPED or Lifetime.TRANSIENT.

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 AwilixResolutionError 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

You can learn more about this in the Middlewares documentation.

Was this page helpful?