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:
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:
Then, create the file src/api/routes/create-product.ts
with the following content:
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:
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:
Then, run your backend with the following command:
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.