Перейти до основного вмісту

Що таке Ditsmod

Ознайомлення з Ditsmod

Ditsmod - це веб-фреймворк на базі Node.js, призначений для створення добре-розширюваних та швидких застосунків, його назва складається з DI + TS + Mod, щоб підкреслити важливі складові: він має Dependency Injection, написаний на TypeScript у форматі ESM, та спроектований для хорошої Модульності.

Головні особливості Ditsmod

  • Модульна архітектура на декораторах, що дозволяє декларативно описувати структуру застосунку.
  • Можливість написання власних розширень (інколи їх називають плагінами), що можуть асинхронно ініціалізуватись, і що можуть залежати один від одного.
  • Має підтримку OpenAPI, та має можливість проводити валідацію запитів на основі метаданих OpenAPI.
  • На сьогодішній день, Ditsmod є одним із найшвидших серед Node.js веб фреймворків:

Techempower benchmarks

Деякі концепції архітектури Ditsmod взяті з Angular концепцій, а DI побудована на базі нативного модуля Angular DI.

ExpressJS vs. Ditsmod

Для порівняння, в наступних двох прикладах показано мінімальний код для запуску ExpressJS та Ditsmod застосунків.

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, restRootModule, RestApplication } from '@ditsmod/rest';

@controller()
class ExampleController {
@route('GET', 'hello')
tellHello() {
return 'Hello, World!';
}
}

@restRootModule({ controllers: [ExampleController] })
class AppModule {}

const app = await RestApplication.create(AppModule);
app.server.listen(3000, '0.0.0.0');

Але чому Ditsmod не такий мінімалістичний, як ExpressJS? Як бачите в прикладі, ExpressJS створює об'єкт застосунку, в якому потім додає роути. В об'єкті app представлено API різних окремих складових, зокрема: налаштування роутера, налаштування обробки помилок, налаштування системи рендерінгу, HTTP-сервера і т.д. Такий код у простих прикладах виглядає дуже компактно, але у ньому по-суті порушується Принцип єдиної відповідальності. Натомість в Ditsmod чітко розмежовано:

  • роль контролера, в якому створюється роут;
  • роль модуля, в якому задекларовано контролери;
  • роль застосунку, який містить HTTP-сервер.

Оцінюючи об'єм коду, можна припустити, що через свою багатослівність, Ditsmod є повільнішим за ExpressJS. Але насправді трохи повільнішим є лише холодний старт Ditsmod (на моєму ноутбуку він стартує за 34 ms, тоді як ExpressJS стартує за 4 ms). Що стосується швидкості обробки запитів, то Ditsmod більш ніж удвічі швидший за ExpressJS.

Більше прикладів застосунку є у репозиторію Ditsmod, а також у репозиторію RealWorld.

P.S. Хоча нижче надано лінк на репозиторій з усіма необхідними налаштуваннями для Ditsmod-застосунків, але, все ж таки, якщо ви захочете використати лише код з попереднього прикладу, незабудьте у tsconfig-файлах прописати наступне:

{
"compilerOptions": {
// ...
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}

Попередні умови

Будь-ласка, переконайтесь що на вашій операційній системі встановлено Node.js >= v20.6.0.

Встановлення

Базовий набір для роботи застосунку має репозиторій ditsmod/rest-starter. Клонуйте його та встановіть залежності:

git clone --depth 1 https://github.com/ditsmod/rest-starter.git my-app
cd my-app
npm i

У якості альтернативи, ви можете скористатись стартовим монорепозиторієм:

git clone --depth 1 https://github.com/ditsmod/rest-monorepo-starter.git my-app
cd my-app
npm i

Запуск в режимі розробки

Стартувати застосунок в режимі розробки можна наступною командою:

npm run start:dev

Перевірити роботу сервера можна за допомогою curl:

curl -i localhost:3000/api/hello

Або просто перейшовши у браузері на http://localhost:3000/api/hello.

По дефолту, застосунок працює з деталізацією log level на рівні info. Змінити його можна у файлі src/app/app.module.ts (або apps/backend/src/app/app.module.ts у монорепозиторію).

Завдяки використанню у ditsmod/rest-starter так званих Project References і режиму збірки tsgo -b, навіть дуже великі проекти компілюються дуже швидко.

Зверніть увагу, що у репозиторії ditsmod/rest-starter є чотири конфіг-файли для TypeScript:

  • tsconfig.json - базова конфігурація, що використовується вашою IDE (у більшості це мабуть VS Code).
  • tsconfig.build.json - ця конфігурація використовується для компіляції коду з теки src у теку dist, вона призначається для коду застосунку.
  • tsconfig.e2e.json - ця конфігурація використовується для компіляції коду end-to-end тестів.
  • tsconfig.unit.json - ця конфігурація використовується для компіляції коду юніт-тестів.

Окрім цього, зверніть увагу, що завдяки тому, що ditsmod/rest-starter оголошено як EcmaScript Module (ESM), для скорочення шляху до файлів ви можете використовувати нативні аліаси Node.js. Це аналог compilerOptions.paths у tsconfig. Такі аліаси оголошуються у package.json у полі imports:

"imports": {
"#app/*": "./dist/app/*"
},

Тепер ви можете використовувати його, наприклад у теці e2e, ось так:

import { AppModule } from '#app/app.module.js';

На даний момент (2025-10-07) TypeScript ще не у повній мірі підтримує ці аліаси, тому бажано їх продублювати у файлі tsconfig.json:

// ...
{
"compilerOptions": {
// ...
"paths": {
"#app/*": ["./src/app/*"]
}
}
}

Зверніть увагу, що у package.json аліаси вказують на dist, тоді як у tsconfig.json - на src.

Запуск в продуктовому режимі

Компіляція застосунку та запуск сервера в продуктовому режимі відбувається за допомогою команди:

npm run build
npm run start-prod

Вхідний файл для Node.js

Після встановлення Ditsmod starter, перше, що необхідно знати: весь код застосунку знаходиться у теці src, він компілюється за допомогою TypeScript-утиліти tsc, після компіляції попадає у теку dist, і далі вже у вигляді JavaScript-коду його можна виконувати у Node.js.

Давайте розглянемо файл src/main.ts:

import { ServerOptions } from 'node:http';
import { RestApplication } from '@ditsmod/rest';

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 RestApplication.create(AppModule, { serverOptions, path: 'api' });
const port = checkCliAndSetPort(3000);
app.server.listen(port, '0.0.0.0');

Після компіляції, він перетворюється на dist/main.js та стає вхідною точкою для запуску застосунку у продуктовому режимі, і саме тому ви будете його вказувати у якості аргументу для Node.js:

node dist/main.js

Проглядаючи файл src/main.ts, ви можете бачити, що створюється інстанс класу RestApplication, а у якості аргументу для методу create() передається AppModule. Тут AppModule є кореневим модулем, до якого вже підв'язуються інші модулі застосунку.