Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: AwilixResolutionError: Could not resolve 'product' in custom module loader. #10946

Closed
jgatto1 opened this issue Jan 13, 2025 · 5 comments

Comments

@jgatto1
Copy link

jgatto1 commented Jan 13, 2025

Package.json file

{
  "name": "medusa-starter-default",
  "version": "0.0.1",
  "description": "A starter for Medusa projects.",
  "author": "Medusa (https://medusajs.com)",
  "license": "MIT",
  "keywords": [
    "sqlite",
    "postgres",
    "typescript",
    "ecommerce",
    "headless",
    "medusa"
  ],
  "scripts": {
    "build": "medusa build",
    "seed": "medusa exec ./src/scripts/seed.ts",
    "start": "medusa start",
    "dev": "medusa develop",
    "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
    "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
    "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
    "migrate": "npx medusa db:migrate"
  },
  "dependencies": {
    "@hello-pangea/dnd": "^16.6.0",
    "@medusajs/admin-sdk": "2.2.0",
    "@medusajs/cli": "2.2.0",
    "@medusajs/framework": "2.2.0",
    "@medusajs/icons": "^1.2.1",
    "@medusajs/js-sdk": "^2.2.0",
    "@medusajs/medusa": "2.2.0",
    "@medusajs/types": "^2.2.0",
    "@mikro-orm/core": "5.9.7",
    "@mikro-orm/knex": "5.9.7",
    "@mikro-orm/migrations": "5.9.7",
    "@mikro-orm/postgresql": "5.9.7",
    "@opentelemetry/instrumentation": "^0.55.0",
    "@opentelemetry/instrumentation-pg": "^0.47.1",
    "@opentelemetry/resources": "^1.28.0",
    "@opentelemetry/sdk-node": "^0.55.0",
    "@opentelemetry/sdk-trace-node": "^1.28.0",
    "@sentry/node": "^8.38.0",
    "@sentry/opentelemetry-node": "^7.114.0",
    "@sentry/profiling-node": "^8.38.0",
    "@tanstack/react-table": "^8.17.3",
    "@uppy/box": "^3.1.2",
    "@uppy/core": "^4.3.0",
    "@uppy/dashboard": "^4.1.3",
    "@uppy/drag-drop": "^4.0.5",
    "@uppy/dropbox": "^4.1.2",
    "@uppy/file-input": "^4.0.4",
    "@uppy/google-drive": "^4.2.0",
    "@uppy/onedrive": "^4.1.2",
    "@uppy/progress-bar": "^4.0.2",
    "@uppy/react": "^4.0.4",
    "@uppy/transloadit": "^4.1.4",
    "@uppy/tus": "^4.1.5",
    "awilix": "^8.0.1",
    "axios": "^1.7.2",
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "16.3.1",
    "express": "^4.17.2",
    "ioredis": "^5.4.1",
    "jose": "^5.9.6",
    "jwt-decode": "^4.0.0",
    "moment": "^2.30.1",
    "pg": "^8.13.0",
    "prism-react-renderer": "^2.0.4",
    "quill": "^2.0.2",
    "react-hook-form": "^7.52.0",
    "react-hot-toast": "^2.4.1",
    "react-quilljs": "^2.0.3",
    "react-select": "^5.8.1",
    "tailwind-merge": "^2.4.0",
    "tus-js-client": "^4.1.0"
  },
  "devDependencies": {
    "@babel/cli": "^7.14.3",
    "@babel/core": "^7.14.3",
    "@babel/preset-typescript": "^7.21.4",
    "@medusajs/test-utils": "2.2.0",
    "@mikro-orm/cli": "5.9.7",
    "@stdlib/number-float64-base-normalize": "0.0.8",
    "@swc/core": "1.5.7",
    "@swc/jest": "^0.2.36",
    "@types/express": "^4.17.13",
    "@types/jest": "^29.5.13",
    "@types/mime": "1.3.5",
    "@types/node": "^20.0.0",
    "@types/quill": "^2.0.14",
    "@types/react": "^18.3.2",
    "@types/react-dom": "^18.2.25",
    "cross-env": "^7.0.3",
    "eslint": "^6.8.0",
    "jest": "^29.7.0",
    "prop-types": "^15.8.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "rimraf": "^3.0.2",
    "ts-jest": "^27.0.7",
    "ts-loader": "^9.2.6",
    "ts-node": "^10.9.2",
    "typescript": "^5.6.2",
    "vite": "^5.2.11"
  },
  "jest": {
    "globals": {
      "ts-jest": {
        "tsconfig": "tsconfig.spec.json"
      }
    },
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "testPathIgnorePatterns": [
      "/node_modules/",
      "<rootDir>/node_modules/"
    ],
    "rootDir": "src",
    "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|js)$",
    "transform": {
      ".ts": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "./coverage",
    "testEnvironment": "node"
  },
  "resolutions": {
    "glob": "^7.1.6"
  },
  "engines": {
    "node": ">=20"
  }
}

Node.js version

v20.17.0

Database and its version

PostgreSQL 13.16

Operating system name and version

MacOS 14.0 (23A344)

Browser name

No response

What happended?

I can't load the product module on my custom module loader.

The file is in: src/modules/lms/loaders/lms.ts with content:

import { Modules } from "@medusajs/framework/utils";
import { LoaderOptions } from "@medusajs/types";

const queueName = process.env.LMS_QUEUE_NAME || "lms-queue";

export default async ({ container }: LoaderOptions) => {
  const productService = container.resolve(Modules.PRODUCT);
};

I'm adding it to my src/modules/lms/index.ts file.

import { Module } from "@medusajs/framework/utils";
import MedusaModuleService from './service'
import lmsLoader from "./loaders/lms";

export default Module("lms", {
  service: MedusaModuleService,
  loaders: [lmsLoader],
});

But when I use the same in a route for example it works.

import { MedusaResponse } from "@medusajs/framework/http";
import { Modules } from "@medusajs/framework/utils";
import { MedusaLogToRequest } from "../middlewares";

export async function GET(
  req: MedusaLogToRequest<{}>,
  res: MedusaResponse
): Promise<void> {
  const productModuleService = req.scope.resolve(Modules.PRODUCT);
  const products = await productModuleService.listProducts();

  res.json(products);
}

Btw, when I resolve container.resolve(ContainerRegistrationKeys.LOGGER); it works in my custom loader.

Expected behavior

It should resolve product module.

Actual behavior

Is not resolving the product module in my custom module loader.

Link to reproduction repo

not apply

@jgatto1 jgatto1 changed the title [Bug]: AwilixResolutionError: Could not resolve 'product'. [Bug]: AwilixResolutionError: Could not resolve 'product' in custom module loader. Jan 13, 2025
@riqwan riqwan self-assigned this Jan 14, 2025
@riqwan
Copy link
Contributor

riqwan commented Jan 14, 2025

Hey @jgatto1,

This is expected. The module loader inside will only load dependencies that it specifically has access to. You are trying to load an external module here within a module which isn't supported. This is keeping in mind the module isolation principles.

If you're looking to run cross module logic, you'd have to do that once all modules are loaded - for example, as a script or in the http layer. This is why it works within the http endpoint, but not the module loader.

@jgatto1
Copy link
Author

jgatto1 commented Jan 14, 2025

@riqwan I see. Thanks for your explanation! I'm trying to integrate Medusa with an external system mapping external products with Medusa products, so the idea is to subscribe to an event bus and there create the association between both products. I put the subscriber in the loader, but I'm not sure if there is an alternative to this. What do you think? Thanks!

Copy link
Contributor

riqwan commented Jan 15, 2025

If you mean by this subscriber, then yes that sounds like a good approach. If not, a bit more details on what the setup looks like would help.

@jgatto1
Copy link
Author

jgatto1 commented Jan 15, 2025

@riqwan I meant a loader. Our idea was to subscribe in the loader to our internal emitted events. The loader is waiting for new events and then links Medusa Products with our custom products. But we can't call Product's module. I think using a loader is a better idea, instead of for example making a call from our custom app to create the products in Medusa.

I really appreciate your help!

@carlos-r-l-rodrigues
Copy link
Contributor

carlos-r-l-rodrigues commented Jan 17, 2025

You can create a workflow on your custom module, and in your loader you can subscribe to the events, and run this workflow when you have a new event.
This workflow will have access to all the loaded modules.

// modules/your_custom/workflows/test.ts
import { Modules } from "@medusajs/framework/utils";
import { createStep, createWorkflow } from "@medusajs/framework/workflows-sdk";

const step1 = createStep("step-1", async (_, context) => {
  const prod = context.container.resolve(Modules.PRODUCT);

  console.log(await prod.listProductCategories());
});

const myWorkflow = createWorkflow("sync-stuff", function (input) {
  step1();
});

export default myWorkflow;
// modules/your_custom/loaders/test.ts
import { LoaderOptions } from "@medusajs/framework/types";
import myWorkflow from "../workflows/test";

export default async function yourLoader({ container }: LoaderOptions) {
  // subscribe to the events
  setTimeout(async () => {
    await myWorkflow.run();
  }, 2000);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants