3
votes

Im trying to import a Module with registerAsync and configure it with a provider I have in my module, but it throws an error it cant find this provider. What am I missing?

My Code:

import { CacheModule, Module } from '@nestjs/common';

@Module({
  imports:     [
    CacheModule.registerAsync({
      useFactory: async (options) => ({
        ttl: options.ttl,
      }),
      inject: ['MY_OPTIONS'],
    }),
  ],
  providers:   [
    {
      provide:  'MY_OPTIONS',
      useValue: {
        ttl: 60,
      },
    },
  ],
})
export class AppModule {
}

Error:

Error: Nest can't resolve dependencies of the CACHE_MODULE_OPTIONS (?). Please make sure that the argument MY_OPTIONS at index [0] is available in the CacheModule context.

The example above is a simplification of my code. But the main issue stays the same: I do have a provider within the AppModule and I need it in the CacheModule.registerAsync() function.

If anyone wants to try figuring this out I made a really simple repository: https://github.com/MickL/nestjs-inject-existing-provider

2
The question was asked too simple I guess and might be closed. The problem actually comes in with registerAsync(). I updated my repo and there is also already an unanswered question on SO: stackoverflow.com/questions/63356440/… - Mick
To import a Module with registerAsync and configure this will help you. - Raksha Saini

2 Answers

2
votes

I'd say the problem is "CacheModule.registerAsync" and "AppModule" work at different levels, so defining a provider on "AppModule" does not make it available for "CacheModule.registerAsync".

Assuming we move the "MY_OPTIONS" to another module: "CacheConfigModule", it could look like:

CacheConfigModule.ts

import { Module } from '@nestjs/common';

@Module({
  providers: [
    {
      provide: 'MY_OPTIONS',
      useValue: {
        ttl: 60
      }
    }
  ],
  exports: ['MY_OPTIONS']
})
export class CacheConfigModule {}

AppModule.ts

import { CacheModule, Module } from '@nestjs/common';
import { CacheConfigModule } from './CacheConfigModule';

@Module({
  imports: [
    CacheConfigModule,
    CacheModule.registerAsync({
      imports: [CacheConfigModule],
      useFactory: async (options) => ({
        ttl: options.ttl
      }),
      inject: ['MY_OPTIONS']
    })
  ]
})
export class AppModule {}
0
votes

This is kind of weird, but even if it is the same @Module you need to explicitly instruct dependency manager about imports and exports like so:

import { CacheModule, Module } from '@nestjs/common';

@Module({
  imports:     [
    CacheModule.registerAsync({
      imports: [AppModule], // <-- ADDED
      useFactory: async (options) => {
        console.log('MY_OPTIONS', options) // <-- LOGS FINE upon NestFactory.create(AppModule)
        return {
        ttl: options.ttl,
        }
        },
      inject: ['MY_OPTIONS'],
    }),
  ],
  providers:   [
    {
      provide:  'MY_OPTIONS',
      useValue: {
        ttl: 60,
      },
    },
  ],
  exports: ['MY_OPTIONS'] // <-- ADDED
})
export class AppModule {
}

EDITED

Based on your repo and specifically file src/my-lib.module.ts, the following seems to work (still not sure if it fully solves the underlying issue):

export class MyLibModule {
  // changed to async version by returning Promise
  static register(options: ModuleOptions): Promise<DynamicModule> {
    // keeps the ref to newly created module
    const OptionsModule = MyLibOptionsModule.register(options)
    // prints 'true', but still importing MyLibOptionsModule does not work
    console.log('who-am-i', OptionsModule.module === MyLibOptionsModule)
    return Promise.resolve({
      module: MyLibModule,
      imports: [
        // OptionsModule, // seems not needed
        HttpModule.registerAsync({
          useFactory: ((options: ModuleOptions) => {
            console.log("Works:", options);
            return {
              url: options.externalApiUrl,
              timeout: options.timeout
            };
          }),
          // importing via newly created ref does work
          imports: [OptionsModule],
          inject: [MODULE_OPTIONS]
        })
      ]
    });
  }
}