#Need an understandable explanation of best practices for a NestJS monorepo

1 messages · Page 1 of 1 (latest)

golden jackal
#

Hello,

I have exactly the same problem as this ticket: #1161017385755611237 message

My project is a monorepo turbo.build (using pnpm) with nestjs microservices.

|-- node_modules
|-- package.json
|-- apps
     |-- api-gateway
      |-- node_modules
      |-- package.json
     |-- svc-common
      |-- node_modules
      |-- package.json
     |-- svc-notification
      |-- node_modules
      |-- package.json
|-- packages
     |-- schemas
      |-- node_modules
      |-- package.json
     |-- types
      |-- package.json

As mentioned in the ticket previously quoted, in connection with the installation of @golevelup/nestjs-rabbitmq. My problem being:

 [Nest] 41040  - 28.12.2023, 11:36:09   ERROR [ExceptionHandler] Nest can't resolve dependencies of the DiscoveryService (?, MetadataScanner). Please make sure that the argument ModulesContainer at index [0] is available in the DiscoveryModule context.
  1. I don't understand this story about having several @nestjs/core. In my project, I hope that "api-gateway" uses its node_modules folder only. There is no need for multiple @nestjs/core.
  2. It says that @nestjs/core should be installed at the root of monorepo. In my opinion, this makes no sense, as my packages have no need for it (and it's a disaster to deploy with PNPM and its symbolic links). It might be easier for me to have one node_module per project.

How to fix it ? What are the best practices? What do I have to do?

Thanks for helping

golden jackal
#

Without changing anything, some days no error is reported and I can use my service without any problem. Then suddenly, like today, this error occurs again. I use GIT and I can assure you that there are no code modifications.

Some days Stripe from @golevelup/nestjs-stripe report this error or (like the initial post above) some days it is @golevelup/nestjs-rabbitmq.

ERROR [ExceptionHandler] Nest can't resolve dependencies of the StripeModule (DiscoveryService, ?, Symbol(STRIPE_MODULE_CONFIG_TOKEN)). Please make sure that the argument ExternalContextCreator at index [1] is available in the StripeModule context.
oak lintel
#

It says that @nestjs/core should be installed at the root of monorepo.
What is "It" meant here? Where is this stated?

golden jackal
#

It's about a response from a ticket very similar to mine. Here it is : #1161017385755611237 message

oak lintel
#

In my experience, this is caused because one or more libraries pulling in a dependency the main application also pulls in. So, two versions of the same dependency get put into node_modules and the "right one" isn't used or is available to Nest, thus Nest fails to wire itself up properly (i.e. your error).

I think the the better way to put it than "in the root of the monorepo" might be "at the highest level of your applications dependencies".

This explanation of what might be happening from the Rush monorepo manager docs might clarify this phenomena better for you (it's a complicated subject): https://rushjs.io/pages/advanced/npm_doppelgangers/

#

If you note too, it says PNPMs installation model solves this for you, but I've found this isn't always the case, when peer-deps aren't done correctly.

golden jackal
#

Thank you for your explanations, I understand much better. In order to find a solution to correct my problem, I have found a ticket in which you are involved: https://github.com/microsoft/rushstack/issues/3447

If I've understood correctly, the method explained is to manually delete the doppelganger from the lockfile. But... Isn't there anything else we can do to make it stable?

oak lintel
#

The better solution is to find what is causing the bad dependency to be put in the lockfile in the first place and resolve it.

#

In my situations of finding this, it was me putting dependencies in the wrong place i.e. in dev deps instead of peer deps, for instance. ( I was learning all this too at the time.)

golden jackal
#

(Unfortunately I can't use lockfile-explorer in non-rush project https://github.com/microsoft/rushstack/issues/4211)

Honestly, I can't find the anomaly. How should I go about it? What I've done so far:

  • check the packages.json structure
  • resolve unmeet peerDependencies
  • manually analyze the lockfile, but I haven't found any doppelganger linked to @golevelup/nestjs-rabbitmq
#

If it helps to understand my problem, attached are the .json packages, the project structure and the lockfile.

oak lintel
#

Interesting, cause the docs says it can.

golden jackal
#

It doesn't seem to work with node_modules symbolic links, the tool just skips my entire node_modules folder.

In any case, days go by and I still haven't found a trace of a potential "doppelganger", I wonder if the problem really comes from there

crystal wyvern
#

Hmm, do you have any .npmrc settings?

#

You could force pnpm to hoist the packages you want

#

(as a workaround)

golden jackal
#

I got this in my .npmrc project settings

auto-install-peers = true
crystal wyvern
#

that's the default, so it should have no effect

#

try

hoist-pattern[]=@nestjs/core
#

then:

rm -rf **/node_modules
pnpm install
#

though you might need public-hoist-pattern

golden jackal
#

I tried with

public-hoist-pattern[]=@nestjs*
public-hoist-pattern[]=@types*

I'm still facing this problem.

Nest can't resolve dependencies of the DiscoveryService (?, MetadataScanner). Please make sure that the argument ModulesContainer at index [0] is available in the DiscoveryModule context.

As well with

hoist = true
crystal wyvern
#

Interesting, could it be that you need a provider or something for that ? dependency?

#

or need to import DiscoveryModule

golden jackal
#

Nothing is mentionned in the doc of @golevelup about adding DiscoveryModule. Just in case, I tested it. But the problem remains.

I have two project using @golevelup/nestjs-rabbitmq in my monorepo.
One is working:


@Module({
  imports: [
    // RabbitMQ configuration
    RabbitMQModule.forRootAsync(RabbitMQModule, {
      imports: [ConfigModule],
      useFactory: async (config: ConfigService) => ({
        // eslint-disable-next-line max-len
        uri: `amqp://${config.get('RMQ_USERNAME')}:${config.get('RMQ_PASSWORD')}@${config.get('RMQ_HOST')}:${config.get('RMQ_PORT')}/${config.get('RMQ_VHOST')}`,
        connectionInitOptions: {
          wait: false,
        },
        logger: new Logger('RabbitMQ'),
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [NotifierService],
  exports: [NotifierService],
})
export class NotifierModule {}

And currently, the other project is not working

@Module({
  imports: [
    MailModule,
    // RabbitMQ configuration
    RabbitMQModule.forRootAsync(RabbitMQModule, {
      imports: [ConfigModule],
      useFactory: async (config: ConfigService) => ({
        // eslint-disable-next-line max-len
        uri: `amqp://${config.get('RMQ_USERNAME')}:${config.get('RMQ_PASSWORD')}@${config.get('RMQ_HOST')}:${config.get('RMQ_PORT')}/${config.get('RMQ_VHOST')}`,
        connectionInitOptions: {
          wait: false,
        },
        logger: new Logger('RabbitMQ'),
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [MessagingService],
})
export class MessagingModule {}

It seems odd to me that it's a configuration problem since the code is similar except that I'm importing extra-module in the second one

#

and the error mentions names of modules and services that I haven't developed at all. This must surely belong to the @golevelup codes.

crystal wyvern
#

interesting

#

I am not super familiar with golevelup, but it seems to me that some hidden behaviour is at play here

golden jackal
#

Yes I've seen this. Every discord conversation or github issue mentions making sure there's only one version of @nestjs/core and @nestjs/common. Without really discussing the problem.

crystal wyvern
#

Yeah, it feels like a rabbit hole.

oak lintel
#

except that I'm importing extra-module in the second one
What extra module?

golden jackal
#

I mean my module called MailModule

oak lintel
#

Does that also entail a third party module? If yes, which one?

golden jackal
#

I'm using MailerModule from @nestjs-modules/mailer

Here is the content of MailModule

import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';
import { Module } from '@nestjs/common';
import { MailService } from '@/services/mail.service';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    MailerModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: async (configService: ConfigService) => ({
        // TODO: transport: 'smtps://[email protected]:[email protected]',
        transport: {
          host: configService.get<string>('SMTP_HOST'),
          port: configService.get<number>('SMTP_PORT'),
          secure: configService.get<boolean>('SMTP_SECURE'),
          auth: {
            user: configService.get<string>('SMTP_USERNAME'),
            pass: configService.get<string>('SMTP_PASSWORD'),
          },
        },
        defaults: {
          from: `${configService.get<string>(
            'MAIL_FROM',
          )} <${configService.get<string>('SMTP_USERNAME')}>`,
        },
        template: {
          dir: 'src/assets/templates',
          adapter: new HandlebarsAdapter(),
          options: {
            strict: true,
          },
        },
      }),
      inject: [ConfigService],
    }),
  ],
  providers: [MailService, ConfigService],
  exports: [MailService],
})
export class MailModule {}
oak lintel
#

Also try auto-install-peers = false and/or strict-peer-dependencies = true in .npmrc and remove node_modules and the lock file and reinstall dependencies. See if you get peer dep failures.

golden jackal
#

Got this for all my project

apps/api-gateway
└─┬ ts-loader 9.5.1
  └── ✕ missing peer webpack@^5.0.0
Peer dependencies that should be installed:
  webpack@^5.0.0

apps/svc-notification
└─┬ ts-loader 9.5.1
  └── ✕ missing peer webpack@^5.0.0
Peer dependencies that should be installed:
  webpack@^5.0.0

Then I installed them all, and problem remains