
Контракти TypeScript між Angular і NestJS
- Angular
- NestJS
- TypeScript contracts
- OpenAPI
- API schema
- code generation
- backward compatibility

Як узгодити контракти Angular і NestJS
Angular і NestJS використовують одну мову програмування, але працюють у різних середовищах і випускаються не одночасно. TypeScript перевіряє вихідний код до запуску, а більшість відомостей про типи зникає зі створеного JavaScript. HTTP перетворює значення на байти й назад. Вкладка, відкрита напередодні, може сьогодні звернутися до щойно оновленого API, а інший споживач узагалі може бути написаний іншою мовою.
Надійний контракт описує саме те, що перетинає межу: назви полів, формати передавання, обов’язкові й необов’язкові значення, можливість null, зміст, помилки, автентифікацію, сторінкову навігацію та сумісність. Він також повинен визначати, як ці твердження перевіряються під час виконання і як зміни доходять до споживачів, що розгортаються незалежно. Імпорт одного інтерфейсу у два проєкти доводить лише те, що обидва скомпілювалися за однаковим оголошенням.
Більшість систем Angular і NestJS використовує один із трьох підходів: навмисно невеликий спільний пакет TypeScript, клієнт, створений із документа OpenAPI, або виконувані схеми, з яких отримують перевіряльники та статичні типи. Жоден підхід не кращий завжди. Належність сховищ, кількість споживачів, незалежність випусків, різноманіття мов, межа довіри й правила керування визначають належне першоджерело.
Корисне запитання звучить не «Як не описувати DTO двічі?», а «Як виробники й споживачі виявлять розбіжність раніше за користувачів?». Відповідь поєднує одне авторитетне визначення, відтворюване створення похідних матеріалів, перевірку на правильних межах, тести сумісності, правила випуску й виробничі дані про версії контракту, які досі використовуються.
Спільною робіть форму обміну, а не внутрішню реалізацію
Зовнішній контракт має бути меншим і стабільнішим за внутрішню модель будь-якого застосунку. Не передавайте сутності ORM до браузера. Вони містять стовпці зберігання, зв’язки, відкладене завантаження, аудиторські поля, індекси, обробники життєвого циклу й чутливі властивості. Перебудова сховища не повинна випадково перейменовувати поле JSON, відкривати внутрішню ознаку або змушувати Angular розуміти відношення бази даних.
Предметні моделі серверної частини й моделі представлення Angular повинні залишатися по різні боки межі. NestJS перетворює перевірені DTO запиту на команди чи предметні значення, а результати — на DTO відповіді. Angular перетворює транспортні відповіді на стан представлення, де можна об’єднати поля, розібрати дати, відформатувати гроші або відобразити завантаження й права. Такий переклад корисно ізолює зміни й не є марним дублюванням.
Явно визначте подробиці, яких структурна типізація безпечно не виведе: чи відрізняється відсутність від null; чи час є рядком RFC 3339 і в якому часовому поясі; чи гроші передаються цілим числом у найменших одиницях або десятковим рядком; чи ідентифікатори непрозорі; як розширюються переліки; як поводяться курсори сторінок; які поля можна записувати; яка форма помилки гарантована. Додайте правила між кількома полями, наприклад вимогу, щоб кінцева дата була пізнішою за початкову.
На сервері перевіряйте кожний недовірений запит. На клієнті перевірка відповіді потрібна, якщо виробник змінюється незалежно, дані перетинають межу довіри, пошкодження дорого коштуватиме або автономне сховище переживає випуск. Параметр типу в Angular HttpClient є лише твердженням часу компіляції й не перевіряє отриманий JSON. Та сама дисципліна потрібна, коли серверна частина для клієнта об’єднує кілька служб: агрегація не скасовує потреби в явній зовнішній формі.
Спільний пакет доречний за єдиного керування випусками
Спільний пакет добре працює, коли одна організація керує сховищами Angular і NestJS, надійно публікує пакети та узгоджує відносно короткий цикл випусків. Редактор одразу показує помилки типів, перебудова коду стає видимою, а окремий етап створення коду не потрібен. У єдиному сховищі межі проєктів і засоби вибіркового складання можуть пришвидшити зворотний зв’язок ще більше.
Пакет має бути навмисно простим. Додавайте орієнтовані на передавання псевдоніми типів, форми запитів і відповідей, сталі рядкові значення, оболонки помилок і, можливо, незалежні від програмної платформи виконувані схеми. Не включайте декоратори й модулі NestJS, сутності ORM, сховища, предметні служби, стан Angular, помічники інтерфейсу та залежні від середовища налаштування. Знання про форму замовлення не повинно змушувати браузер завантажувати залежності серверної частини.
Публікуйте пакет як зовнішню залежність навіть усередині єдиного сховища. Дайте йому відкриту точку входу, забороніть глибокі імпорти, свідомо закріплюйте версії або діапазони, ведіть перелік змін і перевіряйте скомпільований результат. Не покладайтеся на псевдоніми шляхів TypeScript, які працюють у місцевому складанні, але не описують артефакт для розгортання. Якщо пакет відкриває виконувані схеми, перевірте роботу формату і в серверному, і в браузерному збирачі без побічних дій.
Один коміт, що оновлює пакет, API й вебзастосунок, доводить узгодженість лише найновіших версій. Він не підтверджує сумісність під час поступового розгортання й не захищає користувачів зі старішою браузерною збіркою. Перевіряйте підтримувану матрицю: старий клієнт із новим сервером, новий клієнт зі старим сервером, якщо порядок розгортання може змінитися, та збережені автономні дані з новим читачем. Спільний пакет зменшує помилки переписування, а безпеку дають правила випуску.
// packages/order-contracts/src/public.ts
export const orderStatuses = ['pending', 'paid', 'cancelled'] as const;
export type OrderStatus = (typeof orderStatuses)[number];
export interface OrderSummaryContract {
id: string;
status: OrderStatus;
totalMinor: number;
currency: string;
createdAt: string; // RFC 3339 on the wire
}
export interface OrdersPageContract {
items: readonly OrderSummaryContract[];
nextCursor?: string;
}
// Keep ORM entities and Angular view models elsewhere.OpenAPI створює незалежну межу для споживачів
OpenAPI створює сильнішу межу, коли команди випускаються незалежно, клієнти живуть в окремих сховищах, існують споживачі не на TypeScript або API є загальнодоступним. Специфікація не залежить від мови, переглядається без вихідного коду NestJS і підтримує документацію, створення клієнтів, імітації, перевірку стилю, пошук змін та керування з одного переносного артефакту.
NestJS може створити документ OpenAPI з контролерів і класів DTO, але відбиття TypeScript має межі. Інтерфейси зникають під час виконання, а складні узагальнення, об’єднання, відображені типи, умовні форми та приклади можуть потребувати явних декораторів, зареєстрованих додаткових моделей або додатка Nest Swagger CLI. Додаток зменшує кількість позначок, але перевірка запитів під час виконання все одно потребує справжніх перевіряльників. Завжди переглядайте й випробовуйте створений документ, не вважаючи компіляцію доказом повноти.
Публікуйте специфікацію як версійований артефакт складання. Нормалізуйте її, щоб уникати шумних різниць, перевіряйте правила назв і відповідей, порівнюйте з виробничою вихідною версією для пошуку несумісних змін і створюйте клієнти закріпленими версіями інструментів. Безперервна інтеграція повинна завершуватися помилкою, якщо похідний код розійшовся. Не редагуйте створені файли вручну: обгорніть транспорт невеликим адаптером Angular для автентифікації, повторів, перетворення помилок, предметного перекладу й тестів.
Описуйте відмову так само ретельно, як успіх. Додайте тіла помилок, коди стану, вимоги автентифікації, типи вмісту, заголовки, сторінкову навігацію, можливість null, приклади та поведінку ідемпотентності. Перевіряйте створені клієнти на справжньому API або наборі перевірок відповідності. Красива специфікація, що розходиться з виробничою системою, гірша за її відсутність, бо створює переконливу на вигляд упевненість у хибній поведінці.
npm --workspace api run export:openapi -- --output artifacts/openapi.json
# Compare with the deployed baseline, then regenerate deterministically.
npx openapi-diff baseline/openapi.json artifacts/openapi.json --fail-on-incompatible
npx openapi-typescript artifacts/openapi.json --output apps/web/src/generated/api-schema.ts
git diff --exit-code -- apps/web/src/generated/api-schema.tsВиконувана схема потрібна там, де вирішальним є доказ під час роботи
У підході «схема як першоджерело» робота починається з виконуваної схеми, а з неї отримують статичні типи, розбір під час виконання й інколи OpenAPI або JSON Schema. Він підходить системам, де Angular і NestJS повинні перевіряти невідомі дані, події чи збережене корисне навантаження переживають одне розгортання або незалежні від програмної платформи правила важливіші за документацію з декораторів.
Бібліотеку схем і шлях перетворення обирайте за потрібними властивостями контракту, а не популярністю. Перевірте подання приведення введення, перетворення виведення, розрізнюваних об’єднань, рекурсії, дат, маркованих типів, початкових значень, уточнень, невідомих ключів та окремих типів введення й виведення. Перетворення, що непомітно послаблює уточнення або змінює обов’язковість, може створити коректний на вигляд документ OpenAPI з іншою семантикою.
Розміщуйте перевірку свідомо. NestJS має розбирати зовнішні дані до передавання предметній логіці й повертати навмисно сформовану відповідь. Перед зміною стану Angular варто перевіряти відповіді, що критично впливають на роботу або надходять від незалежно оновлюваного виробника. Фонові обробники повинні перевіряти повідомлення черги під час споживання, а не довіряти компіляції виробника. Визначте, чи відхиляються, вилучаються або зберігаються невідомі поля, і повертайте помилки зі шляхом проблеми без розкриття чутливих значень.
Схема не замінює перевірку прав, обмеження бази або предметні правила. «Це правильний UUID» не означає, що виклик має доступ до цього клієнта. Синтаксично правильна сума може перевищувати кредитне правило. Перевірку транспорту тримайте біля межі, а ділові незмінні правила — у предметній області. Підтримуйте одне авторитетне визначення й створюйте з нього похідні матеріали: паралельні рукописні типи, перевіряльники та документація неминуче розходяться.
Вбудуйте сумісність у процес випуску
Сумісність є властивістю розмови двох версій, а не міткою одного випуску. Нове необов’язкове поле відповіді зазвичай не порушує сумісності, коли споживачі пропускають незнайомі поля. Вилучення чи перейменування поля, зміна його сенсу або формату, можливість null у відповіді, нове обов’язкове поле запиту, звуження дозволених даних або зміна семантики помилок зазвичай ламають сумісність. Навіть нове значення переліку може зламати клієнт, який під час роботи вважає свій розгалужувач вичерпним.
Надавайте перевагу терпимим читачам і обережним авторам. Якщо предметні правила це дозволяють, споживачам слід пропускати незнайомі поля відповіді та мати безпечну поведінку для нових значень переліків. Виробникам слід відхиляти неоднозначне введення, а не вгадувати, зберігати задокументовані початкові значення й не використовувати старі назви чи значення з новим сенсом. Окремо зафіксуйте, коли відсутнє значення, null, порожній текст, нуль і порожня колекція означають різні стани.
Зміни проводьте через розширення й подальше звуження. Спочатку випустіть сервер, що приймає старе й нове введення та повертає зрозумілу старим споживачам відповідь. Потім оновіть споживачів, спостерігайте впровадження й відмови, за потреби перенесіть збережені дані та вилучіть стару форму лише після завершення строку підтримки. Пакет версіонуйте семантично, а нову версію HTTP створюйте тільки для справжніх навмисно підтримуваних розривів; розмноження версій не замінює міграцію.
Автоматизуйте докази у безперервній інтеграції: перевірку схем, пошук розходження похідних артефактів, порівняння несумісних змін, приклади серіалізації, тести виробника, очікування споживачів і наскрізні випробування з випущеними версіями. У робочій системі записуйте версію контракту чи клієнта з обмеженим набором міток, вимірюйте використання застарілих полів і призначайте відповідального та дату вилучення. Найбезпечніша стратегія — та, яку команда здатна послідовно виконувати, а не та, що має найскладніший генератор.
Контрольний список стратегії контрактів
- Виходьте з умов експлуатації: обирайте спільний пакет, OpenAPI або схему як першоджерело за належністю сховищ, мовами споживачів, незалежністю випусків, потребою в перевірці під час виконання та здатністю команди керувати створенням похідних матеріалів.
- Захищайте межу: не включайте до контракту обміну сутності ORM, предметну поведінку, серверні декоратори, подробиці зберігання, стан Angular і помічники інтерфейсу; явно перетворюйте дані з обох боків.
- Визначайте семантику: опишіть дати, часові пояси, гроші, ідентифікатори, різницю між відсутністю й null, розвиток переліків, невідомі поля, сторінкову навігацію, автентифікацію, ідемпотентність, помилки та правила між полями.
- Автоматизуйте докази: закріплюйте версії генераторів, відтворювано створюйте похідні артефакти, зупиняйте CI за розходження або несумісної зміни й перевіряйте серіалізацію та виконувану перевірку на правильних і хибних прикладах.
- Випускайте сумісно: перевіряйте поєднання старих і нових клієнтів та серверів, застосовуйте розширення зі звуженням, спостерігайте за застарілою поведінкою у виробничій системі й вилучайте стару форму лише після завершення підтримки та фактичного використання.
Чи варто імпортувати класи DTO з NestJS безпосередньо в Angular?
Чи гарантує TypeScript правильність відповіді API під час виконання?
Коли варто обрати OpenAPI замість спільного пакета TypeScript?
Чи сумісне з попередньою версією нове необов’язкове поле відповіді?
Покажіть сховища, DTO, документи OpenAPI, створених клієнтів, ритм випусків, виконувані перевіряльники та недавні порушення сумісності. Ми визначимо одне першоджерело й побудуємо поступовий шлях із вимірюваними запобіжниками.
Наші дослідження
Дослідження та розробка рішень на основі штучного інтелекту для оптимізації бізнес-процесів і підвищення ефективності прийняття рішень.
Аналіз моделей машинного навчання для прогнозної аналітики у фінансовій сфері, електронній комерції та SaaS-платформах.
Дослідження технологій обробки природної мови та комп'ютерного зору для посилення автоматизації, персоналізації та підтримки клієнтів.


