Introduction
About the project
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.
The main features of Ditsmod:
- Modular architecture on decorators, which allows you to declaratively describe the structure of the application.
- A convenient mechanism for specifying and resolving dependencies between different classes: you specify the instances of which classes you need in the constructor, and DI does the hard work of "how to get them".
- Ability to write your own extensions (sometimes called plugins) that can be asynchronously initialized and that can depend on each other.
- Ability to dynamically add and remove modules after starting the web server, without the need to restart.
- Has OpenAPI support, and has the ability to validate queries based on OpenAPI metadata.
- To date, Ditsmod is one of the fastest among Node.js web frameworks.
Some concepts of Ditsmod architecture are taken from Angular concepts, and DI is built based on the native Angular DI module.
ExpressJS vs. Ditsmod
For comparison, the following two examples show the minimal code required to run ExpressJS and Ditsmod applications.
import express from 'express';
const app = express();
app.get('/hello', function (req, res) {
res.send('Hello, World!');
});
app.listen(3000, '0.0.0.0');
import { controller, route, Res, rootModule, Application } from '@ditsmod/core';
import { RoutingModule } from '@ditsmod/routing';
@controller()
class ExampleController {
@route('GET', 'hello')
tellHello(res: Res) {
res.send('Hello, World!');
}
}
@rootModule({
imports: [RoutingModule],
controllers: [ExampleController],
})
class AppModule {}
const app = await new Application().bootstrap(AppModule);
app.server.listen(3000, '0.0.0.0');
Looking at the amount of code, you might think that Ditsmod is slower than ExpressJS because of its verbosity. But in fact, only Ditsmod's cold start is slightly slower (it starts in 18ms on my laptop, while ExpressJS starts in 4ms). In terms of request processing speed, Ditsmod is more than twice as fast as ExpressJS.
More application examples are available in the Ditsmod repository, as well as in the RealWorld repository.
P.S. Although a link to the repository with all the necessary settings for Ditsmod applications is provided below, if you choose to use just this code, don't forget to include the following in your tsconfig files:
{
"compilerOptions": {
// ...
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Prerequisites
Please make sure that Node.js >= v18.14.0 is installed on your operating system.
Installation
The basic set for running the application has a repository ditsmod/seed. Clone it and install the dependencies:
git clone --depth 1 https://github.com/ditsmod/seed.git my-app
cd my-app
npm i
Alternatively, you can use the starter monorepo:
git clone --depth 1 https://github.com/ditsmod/monorepo.git my-app
cd my-app
npm i
Start in Development Mode
You can start the application in development mode with the following command:
npm run start:dev
You can check the server operation using curl
:
curl -i localhost:3000/api/hello
Or simply by going to http://localhost:3000/api/hello in your browser.
By default, the application works with info
log level. You can change it in the file src/app/app.module.ts
or apps/backend/src/app/app.module.ts
(in the monorepository).
Thanks to ditsmod/seed's use of the so-called Project References and tsc -b
build mode, even very large projects compile very quickly.
Note that there are four config files for TypeScript in the ditsmod/seed
repository:
tsconfig.json
- the basic configuration used by your IDE (in most cases it is probably VS Code).tsconfig.build.json
- this configuration is used to compile the code from thesrc
directory to thedist
directory, it is intended for application code.tsconfig.e2e.json
- this configuration is used to compile end-to-end tests.tsconfig.unit.json
- this configuration is used to compile unit tests.
Also, note that since ditsmod/seed
is declared as an EcmaScript Module (ESM), you can use native Node.js aliases to shorten file paths. This is analogous to compilerOptions.paths
in tsconfig
. Such aliases are declared in package.json
in the imports
field:
"imports": {
"#app/*": "./dist/app/*"
},
Now you can use it, for example in the e2e
folder, like this:
import { AppModule } from '#app/app.module.js';
At the moment (2023-10-13) TypeScript does not yet fully support these aliases, so it is advisable to duplicate them in the tsconfig.json
file:
// ...
"paths": {
"#app/*": ["./src/app/*"]
}
// ...
Note that in package.json
the aliases point to dist
, while in tsconfig.json
they point to src
.
Start in product mode
The application is compiled and the server is started in product mode using the command:
npm run build
npm run start-prod
Entry file for Node.js
After installing Ditsmod seed, the first thing you need to know: all the application code is in the src
folder, it is compiled using the TypeScript utility tsc
, after compilation it goes to the dist
folder, and then as JavaScript code it can be executed in Node.js.
Let's look at the src/main.ts
file:
import { ServerOptions } from 'node:http';
import { Application } from '@ditsmod/core';
import { AppModule } from './app/app.module.js';
import { checkCliAndSetPort } from './app/utils/check-cli-and-set-port.js';
const serverOptions: ServerOptions = { keepAlive: true, keepAliveTimeout: 5000 };
const app = await new Application().bootstrap(AppModule, { serverOptions, path: 'api' });
const port = checkCliAndSetPort(3000);
app.server.listen(port, '0.0.0.0');
After compilation, it becomes dist/main.js
and becomes the entry point for running the application in production mode, and so why you will specify it as an argument to Node.js:
node dist/main.js
Looking at the file src/main.ts
, you can see that an instance of the class Application
is created, and as an argument for the method bootstrap()
is passed AppModule
. Here AppModule
is the root module to which other application modules then imports.
Ditsmod on Bun
Ditsmod can run on Bun. However, as of version (v1.1.29), Bun has a bug that causes it to work incorrectly with TypeScript. If you download Ditsmod's seed, install the dependencies, and try to run the application:
git clone --depth 1 https://github.com/ditsmod/seed.git my-app
cd my-app
bun install
bun run build
bun dist/main.js
Bun will throw the following error:
1 | (function (entry, fetcher)
^
SyntaxError: export 'ValueProvider' not found in './types-and-models.js'
At the moment, this bug can be worked around by removing the tsconfig.json
files from all Ditsmod packages:
rm node_modules/@ditsmod/*/tsconfig.json
Additionally, if your application has compilerOptions.paths
configured in tsconfig.json
, Bun will also malfunction. Simply comment out or remove this section from tsconfig.json
. Afterward, you need to run the compiled version of the entry file:
bun dist/main.js