Сегодня я расскажу о том, как мы можем с помощью типов написать простое расширение для ExpressJS. А если вы в своём приложении/приложениях используете только решения на TypeScript(JavaScript), то у вас отпадёт необходимость в Swagger. Вообще, одно из главных преимуществ разработки серверного кода на NodeJS — это один язык программирования с Web-интерфейсом/React/Vue Native. Это даёт возможность написать общий код в одном месте только один раз и использовать его затем везде. Именно это мы сейчас с вами и попытаемся сделать. Представим простой монорепозиторий, который состоит из двух проектов: server: Backend WebAPI, написанный на ExpressJS; client: Frontend SPA-клиент, написанный на VanillaJS. Приложение крайне простое - todo, которое должно уметь создавать, получать и удалять задания. Как обычно поступают в таких условиях? Пишут backend сервер, в лучшем случае подключают к нему swagger (что приводит к существенным изменениям в коде), или ведут Google Table со списком контрактов. В худшем - вам придётся каждый раз смотреть исходный код сервера, и подгонять под него свой код. Для начала создадим другую папку в нашем монорепозитории и назовём её shared. Сделаем его npm проектом на typescript, выполнив в корне папке команды: Для имени этого проекта я буду использовать имя монорепозитория и название папки: Несколько замечаний об этом проекте Поскольку подразумевается, что код в этом проекте будет использоваться в разных службах и приложениях, то необходимо соблюдать следующие правила: - Не устанавливать специфичные фреймворки и библиотеки; - Желательно вообще ничего не устанавливать :) - Старайтесь писать здесь максимально абстрактный код, или код, который будет работать везде. - Каждое приложение или сервер, если предоставляет какие-то контракты для работы с ним, должно иметь собственную папку, и оно не должно ниоткуда, кроме common импортировать код. Теперь создадим папку common в проекте @express-ts-react/shared и объявим её локальным модулем: Там же создадим файл, в котором мы объявим самые важные генерики, которых уже будет достаточно, чтобы заменить нам swagger. EndpointMeta<T,U> — это тип, который описывает один endpoint. По сути, любой метод нашего RestApi задаётся таким набором. T и U здесь использованы не просто так, они понадобятся для автоматического и динамического формирования интерфейсов наших контроллеров. Пока вам нужно знать, что T —это параметры аргументов, которые принимает метод контроллера, а U — это формат ответа. А теперь используя только эти четыре типа вы можете легко создавать интерфейсы/контракты для ваших контроллеров, и использовать этот код как на сервере, так и на клиенте. Снизу, например, представлены методы для нашего ToDo приложения: Четыре переменные, по сути, содержат всю необходимую информацию, которую нужно, чтобы: Создать ExpressJs роутинг; Без подглядывания в сторонние данные написать клиент для этого сервера. Почему я уверен, что не придётся подглядывать? Давайте создадим простой класс, который можно использовать везде, независимо от среды исполнения и объявим его, как реализующим этот непонятный тип EndpointsProvider: Если мы оставим класс как есть, то, во-первых, на нас будет ругаться vscode, а во-вторых, при попытки собрать проект командой npx tsc мы увидим ошибку: Эта ошибка говорит, что наш класс TaskController неправильно реализует EndpointsProvider: отсутствуют функции addTask, getTaskById, getTasks, deleteTask. Давайте теперь попробуем реализовать эти методы. Теперь команда npx tsx ничего не пишет нам, а значит, всё работает! Как видно, это класс для контроллера, и в нём уже содержится информация о конкретной реализации методов, поэтому по-хорошему этот класс нужно уже выносить в проект нашего сервера. Чтобы сделать клиент, нужно унаследовать точно такой же интерфейс, но уже вместо конкретной реализации, нужно заменять url на параметры и формировать fetch запрос на сервер. В следующей части я покажу, как можно автоматизировать на основе const endpoints формирование сервера и класса клиента. А также мы создадим базовый контроллер, запрос и ответ, сделаем их расширяемыми собственными свойствами и спасём себе жизнь от ошибок и сохраним часы и дни для синхронизации работы серверов и клиентов. При использовании TypeScript код заметно увеличивается в размере, и кому-то это может казаться некрасивым. Но когда вы работаете в современной IDE или текстовом редакторе на стероидах (VSCode) - то вы оцените, насколько силён TypeScript. Его использование существенно снижает количество ошибок, а также доступность для понимания кода для вас и членов вашей команды.