
Контракты 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-платформ.
Исследование технологий обработки естественного языка и компьютерного зрения для усиления автоматизации, персонализации и поддержки клиентов.


