Taken from Zeal3dPrinting without permission.

Exporting imported NestJS modules to children

MrManafon
Homullus
Published in
3 min readJan 20, 2023

--

It is perfectly possible to export an imported module in NestJS. This is common and very useful when you wish to configure a dynamic module at app level and then propagate it toward children modules, but there are some caveats. Also, the information around it is a tiny bit scattered.

I’ve been working with NestJS for the past year or so, and it’s quite a nifty framework I must say. Sometimes rough around the edges, at times too magical or silver-bullet-y, but overall it lets you do what you need to do and do it fast.

It had a lot of time to learn from others, and unlike a huge part of Node ecosystem that constantly pretends to re-invent age-old patterns, NestJS actually listened and learned from others. Picked up the parts they liked and wrapped them in a pretty usable way.

Sleepy kitty from Rifle kinda looks like theNestJS logo

Dependency Injection Container in NestJS

Nest divides code into domain modules, usually as folders containing code for a single domain. Where you’d usually define DI scopes, tags, containers, in Nest you’d define (and pass) modules.

It is therefore perfectly reasonable to attempt to instantiate module’s services with a configuration or factory, and then pass them around as service singletons. If that sounds confusing, just think DB. You’d instantiate it once with the proper config, and then pass it around.

Thats a mistake on the right, I meant modules B and C, but I noticed too late and already threw away the Figma file. Sorry. You’ll live.

Regardless of if it was a normal or a DynamicModule we are working with, we should be able to import it, and then immediately export it. In other words, initialise the imported module, and then expose it for others.

@Module({
imports: [ModuleA, ModuleB],
exports: [ModuleA],
})
export class AppModule {}

Now lets look into modules B (or C) next. We see a weird-looking import and a simple controller that expects DI to provide it with an already configured service from module A.

@Module({
imports: [forwardRef(() => AppModule)],
controllers: [ModuleBController],
})
export class ModuleBModule {}

@Controller()
export default class ModuleBController {
constructor(private readonly dynamic: ModuleAService) {}
}

The import we use is just a trick by NestJS. It instructs the DI to reference the AppModule instead of instantiating it anew, since that would be a circular dependency. This way, App initialises A and B, but B doesn’t initialise App as it assumes it will exist.

In other words, this allows all App’s children modules to just reference the App’s ModuleA without having to really import it, instantiate and provide its deps.

Order of imports is important

This will work. Most of the time at least. When working with a real project you’ll have multiple layers here and might need to re-export it between the layers. More importantly, the order of imports is important, and you can very easily find yourself spending a hour fiddling with this, just to realise you imported another module which required the export.

You can guess by reading this, that I did.

--

--