NestJS vs. Ditsmod: features of the router

Костя Третяк
3 min readNov 30, 2022

--

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 {}

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 {}

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:

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

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.

See also

--

--