Livio is a member of the NestJS core team and creator of the @nestjs/terminus integration
Intro
Dependency Injection (short DI) is a po...
For further actions, you may consider blocking this person and/or reporting abuse
Hi Livio,
Can you please illustrate more on this:
So how do we bypass that? The holy words are Dynamic Module
How would that overcome the problem? Are classes' constructors called before calling a Dynamic Module?
Thanks
Great article. I was looking for this kind of example.
I had only one issue with re-exporting the dynamic LoggerModule.
The
prefixesForLoggers
was not call in time (same issues as you explained in the article) due to other modules loading.In my case I have a
CoreModule
that includes many base modules and services.I was able to overcome the issue by making the LoggerModule an Asyc module (using
Promise
andsetTimeout(..., 0)
). This let all the files and@Logger(...)
to be called and add all the prefixes toprefixesForLoggers
array before resolving.Thanks :)
Or, you could just...
That is why the article is called "Advanced NestJS: Dynamic Providers", not "How to create your own Logging Library".
Seems like I failed to convey that "Logger" was just an easy-to-understand vehicle to bring closer how you can dynamically generate providers using NestJS. This is quite powerful pattern which is used for instance in
@nestjs/typeorm
.Your average user won't need this. This is mainly targeted for advanced NestJS users (hence the title) + library authors
In heavens sake I don't wanna say "You should build your own logger like this". Off course not, that is why we already provide the logger you mentioned .
I am part of the NestJS core team -- off course I wouldn't try to promote a different logger. That wasn't my point of the article. Sorry for the misunderstanding will try to make it clearer next time.
Respectfully, that it was a logger wasn't relevant to my comment either. I would feel the same about it as a queue name, or a repository type. Rather, I was pointing out that this is quite a lot of complexity to solve the "problem" of passing a string to a function.
I don't get this obsession with decorators. They're not a particulary powerful pattern. You shed the runtime context that's necessary to instantiate things, and it takes a tremendous amount of effort to avoid falling back on static contexts, paradoxically creating the problem that DI frameworks claim to solve. As Daniel Huly points out, there are are numerous ways that consumers of this can be loaded later than the module, and this will mysteriously not work without leaving the user any way to trace it short of hacking around in the library's compiled code.
Further, while decorators can enforce types on their target, the NestJS ones don't, so anything relying on
@Inject
completely disposes of type safety:"We do not want to explicitly write
this.logger.setPrefix('AppService')
in the constructor of our services?" The mutation is a poor pattern, yes. After all, your service has no real way to know if it's actuallyTRANSIENT
. But otherwise, when something is used in exactly one place, the place that's using it is a perfectly fine place to instantiate it. The DI container is already managing the singleton service.If you really need a bunch of other injected stuff to do so, expose the factory:
It's actually dynamic, it's type-safe, it doesn't make assumptions about whether the consumer actually wants a transient instance or not, you can debug it, and most importantly, it doesn't rely on load order to work. All things a library author should consider.
Yes I actually agree. That is one of the reasons why I am personally pushing to always offer decorators alternative.
For me decorators are nice-to-have-syntax-sugars. It's alright for certain things (e.g. defining a Controller), but you always want to have the option to for instance create a Controller dynamically. This can't be done nicely with decorators (except you create class mixins). It's much more convenient to use a service or similar where you can call some functions.
Nonetheless, I believe decorators can be nice. Dynmically generating providers can be useful. I agree - my example wasn't the best - but a lot of the things you've mention (e.g. type-security) can be fixed depending on the context even with Decorators. Libraries authors should strive for a programmatic service-based API and as a nice-to-have maybe an applicable decorator.
In the end its up to the Library Author what they want to offer and how they do it. This article was meant to look into one strategy which might help in your specific use-case. If you -- as a library author -- believe its not the right fit then so be it.
underrated comment right here. This is how the nestjs logger works.
I just keep banging my head against the wall. It's like some kind of contest to see who can overcomplicate things the most. It's rooted in this archaic belief that initializing objects is expensive, then just snowballs with tactics designed to prevent duplicate initializations across use cases where there would NEVER be duplicates for objects in places where it wouldn't matter if there were.
Very nice article :)
Very nice article :)
Nice work! Thank you.
Great article... keep sharing things like that, we appreciate it.