#doubts on NestJs modules

1 messages ยท Page 1 of 1 (latest)

bitter lotus
#

I have some 5 years experience in developing HTTP services using Node, but still I consider myself a newbie in Nest :/ I'm having maybe a rather simple double about the NestJs module system and how it's intended to be understood:

  1. where should we define the public interface of a module? aaa/index.ts X aaa/aaa.module.ts?
  2. should we depend modules on other modules or on individual services of other modules? providers X import?
  3. would this choice have an impact on how Next DI engine creates the injectables?
  4. how can we address transitive dependencies on module import, when testing?
  5. on *.module.spec.ts files, should we use the same approach regarding providers X import used on their correspondent *.spec.ts files?
  6. how can we mock transitive dependencies (ie: moduleA depends on moduleB, which depends on moduleC)?

"you may consider the exported providers from a module as the module's public interface"
"(imports) the list of imported modules that export the providers which are required in this module"
https://docs.nestjs.com/modules

#

doubts on NestJs modules

grave whale
#
  1. I'm not sure what you mean by public interface. Module definitions are usually in aaa/aaa.module.ts
  2. Depend on modules, using imports, never add provider of a different module to other's providers array (there might be situations where you can break this rule, but I don't remember one).
    2a. Yes, it has impact - when provider is in providers array, nest creates a new instance. When module imports other module, the instance of that module is reused.
  3. https://github.com/jmcdo29/testing-nestjs should have enough examples on how to approach different test scenarios.
bitter lotus
#

hey, @grave whale, thank you for your feedback! What I meant on item #1 was related to how the interface of a NestJs module should be described:

case 1: regular TypeScript

A folder-module AModule exports the service AService:

  1. AService is defined at AModule/service.ts;
  2. AModule interface is defined at AModule/index.ts;
  3. AModule exports AService using standard export scope modifier;
  4. for another service BService to consume AService:
  5. import identifier as import {AService} from '.../AModule'
  • NO file inside the directory AModule/ is directly accessed by the code that uses this module; they should be considered implementation details, subject to change.

case 2: NestJs

A module QModule exports the service QService:

  1. QService is defined at q/q.service.ts;
  2. QModule is defined at q/q.module.ts;
  3. QModule exports QService using ModuleMetadata.exports on @Module class decorator;
  4. for another service WService to consume QService:
  5. on corresponding module WModule:
    1. import identifier as import {QModule} from '.../q/q.module.ts'; ๐Ÿ‘ˆ๐Ÿฝ access file inside directory q/
    2. add QModule to the scope of the DI engine using ModuleMetadata.imports on @Module class decorator;
  6. on service WService:
    1. iimport identifier as import {QService} from '.../q/q.service.ts'; ๐Ÿ‘ˆ๐Ÿฝ access file inside directory q/
    2. add a parameter on the class constructor for an instance of QService to be injected: private readonly qService: QService

Although case2 describes most examples I've seen using NestJs, I'd like to assess with people more experienced in NestJs than me whether it'd make sense to create an actual regular folder-module q/index.ts, to expose both QModule and QService.

grave whale
# bitter lotus hey, <@118633408438992896>, thank you for your feedback! What I meant on item #1...

In order for nest's DI to work correctly, you have to do case 2, the important bits:

  • in QModule you have QService in exports and providers in @Module decorator options
  • in WModule you have QModule in imports of the @Module decorator, and WService in providers or controllers
  • in WService you have QService in constructor like you mentioned

That's the minimum for Nest's DI. What doesn't matter is the location of the files. The whole stuff can be in a single file, be it 10 modules, 40 services and the bootstrap if you want to go extreme.
Given that, you can adapt any folder structure you'd like. A common practice is to use barrel-files, like the one in Nest itself: https://github.com/nestjs/nest/blob/master/packages/common/index.ts
There you simply re-export any services/modules you want to be part of the "public api". It doesn't stop the user to import something from q/q.service.ts directly, even if it's not re-exported from the index.ts, but that's probably solvable/preventable with some eslint rule.

With this, you would
in WModule: import {QModule} from '.../q'
In WService: import {QService} from '.../q'

vague helm
#

i got a folder called features for folders when generating a resource with the cli
or a folder called services for stand-alone services (and everything required for that service)
same logic goes for guards and the guards folder etc..

indigo willow
#

I'd like to add (and contrary to weshuiz), using Nest, one should think of Nest modules as like having a box of stuff inside each module. It can be anything that is part of Nest, like Controllers, Resolvers or any provider like Guards, Interceptors, etc. etc. What you define inside the @Module decorator then tells Nest what you want to be in that module.

What you define in the exports property tells Nest what should be made available to any module that imports that module.

Nest then takes the modules and creates its dependency injection container. That is what you are calling with anything "injectable", i.e. those things you've defined as exported.

Any module can be dependent on other modules. Basically, you should try to create a hierarchy of modules, starting with the root module. The root modules should be pulling in the features and any cross-cutting concern modules.

vague helm
#

i was about to say
but it is also accessible to just shove everything related to a feature in 1 folder
like their respective guards decorators etc

indigo willow
#

Yes, because this allow for code reuse. Imagine creating an auth module. Then using it in any other app you build. ๐Ÿ™‚

#

I'm working with a fantastic library that builds CRUD for GraphQL. So, as a Nest module, it defines resolvers and services and also allows for extending them. Pretty cool.

vague helm
indigo willow
#

If your features need to use the same things, all they have to do is import the module holding those pieces. Or, you make those cross-cutting concerns global (which I forgot to mention would need to happen above).

#

On finding things, I would argue having "complete" feature modules improves troubleshooting, because everything involved is all in the same folder.

vague helm
indigo willow
#

Hehe... this is my "core" module.

#

It will be a library for any and every app I build in the future (and hopefully for other devs too, but more on that later. ๐Ÿ™‚ )

vague helm
#

i might have to restructure my whole app
after i reworked authentication

If your features need to use the same things, all they have to do is import the module holding those pieces.

mind sharing where i can see a example of this?
or post one if there aren't any

indigo willow
#

My auth and authz both need the Usermodule. They both import the User module. Nest is smart and will only instantiate User once though. The power of a DI container. I have to go. Fun chatting! ๐Ÿ˜„

vague helm