Ditsmod — new NodeJS webframework writen in TypeScript

Костя Третяк
4 min readMay 24, 2021

Ditsmod is a Node.js web framework, named DI + TS + Mod to emphasize its important components: it has Dependency Injection, written in TypeScript, and designed for good Modularity. Some of the architecture concepts of this framework are taken from Angular.

Motivation to create Ditsmod

Before creating Ditsmod, of course, I was looking for: “Are there ready-made solutions for my purposes?”.

I believe that the set of the following features is unique at the moment:

1. The framework is written in TypeScript (and under the hood there is no ExpressJS or Fastify ;).

2. Modular architecture with decorators.

3. Has a hierarchical Dependency Injection that allows you to use classes as a search token, rather than their names.

4. Clearly delineated providers arrays for DI injectors at the application, module, route, or request level.

5. Built-in support for nested routes, such as `/api/posts/:postId/comments/:commentId`.

6. Check for collisions when importing providers. This feature allows you to detect situations where you import two or more modules that export providers with the same token.

7. Support for guards with parameters (for example, to pass user roles).

8. Support for HTTP interceptors (they are very similar to middleware, but can use DI).

9. Support for multi-providers.

10. Support for extensions that can be asynchronously initialized and that can depend on each other.

11. Support for extension groups, with the ability to specify “before” or “after” which group you want to run your extension group.

12. Ability to dynamically add providers and routes in extensions.

13. Ability to dynamically add and remove modules after starting the web server, without the need to restart. Moreover, if you fail to add a particular module, you can automatically roll back to the previous set of modules. This rollback can be done non-automatically, for example, if the error did not occur during the adding of a new module, but later.

14. Has OpenAPI support.

NestJS vs Ditsmod

Of course, I’ve seen NestJS exist, and its author, like me, was inspired by the Angular architecture. NestJS also has DI, a modular architecture, it’s written in TypeScript… but at the moment (v7.6.17) NestJS has a number of the following architectural solutions, which is why I decided to write Ditsmod.

DI searches for instances by class name

DI uses class names as search tokens, not references to them. That is, if you have two different classes that have the same name, NestJS DI will consider it the same class. This is especially problematic when you import third-party modules into your application, and they have no idea what classes are in your application.

In the next — 8 version of NestJS — it is planned to fix, but, in my opinion, it had to be done in the first version. Especially since NestJS is positioned as a “framework for scalable applications”.

Lack of support for nested routes

NestJS has the ability to create a single global prefix for routes, and there are controller-level prefixes, but module-level prefixes are not supported. That is, if you want to have a parent module at `/api/posts/:postId` and a child module at `/api/posts/:postId/comments/:commentId`, you will not be able to do so.

It is true that there is a third-party nest-router module, which does something like that, but at the moment NestJS does not have it in the official module. This is also bad for a “framework for scalable applications”.

Almost everything is shared across incoming requests

I was surprised to learn that NestJS controllers by default have application-level sigletons. Can you imagine!? =). That is, if you want to create a variable at the class level in the controller, you can get into trouble, because all requests coming on a particular route will have access to this variable.

Exported providers are not checked for collisions

Imagine you have `Module1` where you imported `Module2` and `Module3`. You did this import because you need `Service2` and `Service3` from these modules, respectively. You are viewing how these services work, but for some reason `Service3` does not work as expected. You start debug and it turns out that `Service3` exports both modules:` Module2` and `Module3`. You expected that `Service3` would only be exported from `Module3`, but the version exported from `Module2` actually worked.

Specifically in this case:

1. `Module2` substitute and then exports the provider with the token `Service3`;

2. and `Module3` substitute and then exports the provider with the token `Service3`.

Therefore, the framework must have to detect such collisions.

It is not possible to connect or disconnect modules after starting the web server

In NestJS it is possible to pass parameters for modules (here such modules are called dynamic), but there is no possibility to connect or disconnect modules after start of the web server.

This feature can also be quite desirable, for example for temporary connection monitoring, or for CMS such as Wordpress, etc.

No extension system (or plugins)

NestJS has the ability to pass arguments to modules, and it has Lifecycle hooks, but these mechanisms are not a system of extensions (or plug-ins) where you can specify one extension in dependency from another, or determine the order in which different groups of extensions run.

--

--