DEV Community

Костя Третяк
Костя Третяк

Posted on • Edited on

NestJS vs. Ditsmod: features of the router

The description of NestJS specifically states that this framework was designed for "scalable server-side applications". Surprisingly, only in the eighth version it began to support prefixes at the module level, and this feature has a number of significant limitations.

In this post, NestJS v9.2 and Ditsmod v2.27 are used for comparison. A finished example with nested routes in Ditsmod can be viewed on github. I am the author of Ditsmod.

In addition to the slow implementation of nested routes in NestJS, its router even now (in the ninth version) does not care about collisions of path in routes. If you are writing a large project and accidentally duplicated a path, the NestJS router will not tell you anything about it. Likewise, it won't say anything if you accidentally duplicated the parameter name: one/:two/:two. It's especially easy to make such duplicates when you use third-party modules. Although this is how the Express router works, NestJS could fix this if it positions itself as a framework for "scalable server-side applications".

Currently, NestJS does not support the ability to import a module while simultaneously adding guards to routes of this module. This feature can also be very useful in large projects, especially for importing third-party modules.

Unlike NestJS, Ditsmod has been able to add prefixes at the module level since the first version. For example, to serve /posts/:postId, the parent module is imported as follows:

import { rootModule } from '@ditsmod/core';
import { PostsModule } from './posts/posts.module';

@rootModule({
  appends: [
    { path: 'posts/:postId', module: PostsModule }
    // ..
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

And to import the child module serving /posts/:postId/comments/:commentId, in Ditsmod it is done as follows:

import { featureModule } from '@ditsmod/core';
import { CommentsModule } from './comments/comments.module';

@featureModule({
  appends: [{ path: 'comments/:commentId', module: CommentsModule }],
  // ...
})
export class PostsModule {}
Enter fullscreen mode Exit fullscreen mode

In Ditsmod, appends is a lightweight version of imports that only allows you to appending modules, inheriting only the prefix of the current module. No providers or extensions are imported in this case.

Starting with the eighth version, NestJS can create nested routes in a similar way:

@featureModule({
  imports: [
    PostsModule,
    CommentsModule,
    RouterModule.register([
      {
        path: 'posts',
        module: PostsModule,
        children: [
          {
            path: ':postId/comments/:commentId',
            module: CommentsModule
          },
        ]
      },
    ]),
  ],
  // ...
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

But there are the following limitations:

  • Such configuration import can be done only once for the entire application.
  • If imported modules have no controllers, all controllers from their child modules are ignored. That is, if there are no controllers in PostsModule from the previous example, then all controllers from CommentsModule will be ignored.
  • In this case, it is not enough that you pass PostsModule and CommentsModule to the RouterModule.register() method, you also need to directly pass them to the imports array of AppModule data. Moreover, if you do not make such an import, NestJS will not tell you anything about it, but routes from imported modules will not work.

These limitations very significantly reduce the possibility of scaling projects, their modularity.

Conclusion

Statistics show that currently NestJS is downloaded about 1.8 million times per week, which shows how much developers like this framework. NestJS took many concepts from Angular. Likewise, many concepts from Angular were taken to design Ditsmod. As the author of Ditsmod, I may not always be objective, but I try to criticize NestJS constructively. If you have anything to add to this criticism, please do so in the comments.

Top comments (8)

Collapse
 
micalevisk profile image
Micael Levi L. C.

A note regarding RouterModule from Nestjs: I'm pretty sure that we still need to import the modules at the parent due to some design decision made by Kamil. After changing one line at @nestjs/core's source I was able to circumvent that duplication (at least in a simple use case of mine).


btw looks like in Ditsmod we can easily add prefixes to several modules at once, which we can't do with RouterModule. Am I correct?

Collapse
 
kostyatretyak profile image
Костя Третяк • Edited

in Ditsmod we can easily add prefixes to several modules at once

Yes you're right.

Collapse
 
kostyatretyak profile image
Костя Третяк

Oh, I looked into this issue, it turns out that the router in NestJS is even worse than I imagined before. The issue is still not fixed in NestJS v9.2.

Collapse
 
micalevisk profile image
Micael Levi L. C.

judging by Kamil's response, there's nothing to fix. I can't tell if that's a limitation or not, but I found it pretty odd.

Collapse
 
micalevisk profile image
Micael Levi L. C.

does Ditsmod has lifecycle hooks like Angular and Nest?

Collapse
 
kostyatretyak profile image
Костя Третяк

No, Ditsmod does not currently have hooks. I don't see the need for them yet.

Collapse
 
micalevisk profile image
Micael Levi L. C.

I'd like to see a comparison on the circular dependencies issue.

Collapse
 
kostyatretyak profile image
Костя Третяк • Edited

Both NestJS and Ditsmod use the forwardRef() function to avoid circular dependencies issue. In both cases, this is also taken from Angular.