Создайте надежный API узла с TypeORM, используя транзакции

Javascript и NodeJS имеют одну из самых популярных экосистем для разработки современных веб-приложений. Он содержит множество полезных ресурсов и фреймворков, которые облегчают нам часть работы.

Одной из основных проблем сегодня при создании API, который будет общедоступным, является обеспечение надежности и безопасности предоставляемого сервиса. Сегодня мы увидим, как транзакции базы данных — отличный способ улучшить качество службы API и избежать множества проблем в вашем приложении.

Пример интернет-магазина

Давайте рассмотрим простой Node Express API, который мы разработали для управления некоторыми нашими интернет-магазинами.

В этом примере мы собираемся развернуть базу данных PostgreSQL через контейнер Docker.

API был настроен с использованием TypeScript и фреймворка TypeORM для взаимодействия с базой данных.

Конечные точки API

Чтобы клиент мог взаимодействовать с нашим приложением, API предоставляет следующие конечные точки на /order router:

  • Открытый приказ
  • Порядок обновления
  • Полный заказ
  • Получить заказ

Затем мы можем открыть заказ с почтовым запросом в конечной точке /order, указав shopId, желаемые продукты и их количество:

{ 
  "shopId": 1,
  "productsRelation": [
    { "productId": 1, "quantity": 2},
    { "productId": 2, "quantity": 1}
  ],
  "clientEmail": "[email protected]"
}

В этом простом API мы не собираемся учитывать все функции безопасности, которые должны быть в реальном сервере электронной коммерции, поскольку мы стараемся сделать его максимально простым. При открытом заказе пользователь может обновлять его, добавлять или удалять товары по своему усмотрению.

В конце этого процесса заказ может быть подтвержден в процессе оформления заказа по маршруту /order/complete . Что будет обрабатываться специальным методом в классе OrderService:

Мы считаем, что маршрут completeOrder будет отвечать в процессе оформления заказа за:

  • Проверяя оплату заказа, для простоты мы собирались предположить, что она будет зарегистрирована в нашей базе данных.
  • Отправка уведомления пользователю по электронной почте и регистрация его в нашей базе данных
  • Обновление записи заказа в нашей базе данных

Наш API не очень надежен

Обычно описанный выше метод работает без проблем при обработке любого заказа клиента. Однако мы должны учитывать возможность того, что что-то пойдет не так в середине этого процесса.

Что делать, если произошла ошибка в тот момент, когда клиент должен был быть уведомлен по электронной почте? Это означает, что процесс оформления заказа будет терпеть неудачу прямо посреди него, а мы только что завершили часть всей операции!

Платеж был подтвержден, но не было уведомления по электронной почте, а статус заказа не был обновлен. Мы получаем нашу базу данных в середине процесса оформления заказа.

Как мы можем решить эту проблему?

Сначала мы можем подумать о решении этой проблемы, сначала обновив запись заказа в базе данных, а затем уведомив клиента по электронной почте о том, что заказ выполнен. Хотя иногда это может работать, это не является последовательным решением. Что, если нам придется ввести еще один шаг в процессе оформления заказа? Это может быть не так просто, как в этом случае.

Здесь в игру вступают транзакции базы данных. Транзакция — это просто блок операций «все или ничего». Либо все они работают гладко, либо один из них дает сбой, и каждая сделанная модификация откатывается в БД перед транзакцией.

Эта концепция широко используется для систем баз данных. В PostgreSQL команды запуска и завершения транзакции — START и COMMIT:

START;
UPDATE product SET stock_quantity = 0 WHERE id = 2;
UPDATE order SET order_status = 'awaiting_delivery' WHERE id = 1;
INSERT into email VALUES ('[email protected]', 'Thank you for your purchase');
COMMIT;

Если мы передумаем о каких-либо наших изменениях, мы можем просто запустить команду ROLLBACK вместо COMMIT. Подробнее о них можно узнать в официальной документации.

Использование транзакций с TypeORM

К счастью, TypeORM — довольно полная структура, которая также поддерживает транзакции. Следуя официальной документации, их можно настроить по-разному.

В этой статье я собираюсь показать вам тот, который я принял в своих проектах. Это достигается путем помещения всей бизнес-логики в вспомогательный элемент с именем transactionContext:.

На вход поступает функция, которая получает в качестве аргумента TypeORM EntityManager и отвечает за выполнение всей бизнес-логики.

Возможны два исхода:

  • Либо оболочка транзакции успешно выполнит ввод serviceMethod, вернет свой вывод и зафиксирует транзакцию.
  • Либо внутри serviceMethod будет выброшена ошибка, и вся транзакция будет отброшена к своему состоянию до начала транзакции.

Теперь мы можем применить нашу оболочку к нашим контроллерам маршрутов и решить нашу проблему с маршрутом оформления заказа:

Каждое взаимодействие с БД будет осуществляться через transactionManager, его можно внедрить везде, где нам нужно выполнить любой запрос.

Возвращаясь к методу completeOrder, мы имеем:

Где мы внедряем диспетчер транзакций везде, где хотим общаться с базовой БД. Теперь, если где-то в нашем коде есть ошибка, он будет откатывать любые изменения, внесенные в базу данных, прежде чем выдать ошибку.

Весь код API доступен в этом git-репозитории.

Отныне вы сможете создавать гораздо более надежные API и сервисы с помощью Node и TypeORM. Также я хотел бы уточнить, что это только из возможных подходов обработки транзакций базы данных с помощью TypeORM, вы можете обратиться к официальной документации, чтобы узнать больше о том, как они могут применяться.

Спасибо за внимание и доведение до конца!

Бит: почувствуйте мощь компонентно-ориентированной разработки

Скажи привет Bit. Это инструмент №1 для разработки приложений на основе компонентов.

С помощью Bit вы можете создать любую часть своего приложения в виде «компонента», который можно компоновать и использовать повторно. Вы и ваша команда можете совместно использовать набор компонентов для более быстрой и последовательной совместной разработки большего количества приложений.

  • Создавайте и компонуйте «строительные блоки приложения»: элементы пользовательского интерфейса, полные функции, страницы, приложения, бессерверные или микросервисы. С любым стеком JS.
  • С легкостью делитесь и повторно используйте компоненты в команде.
  • Быстро обновляйте компоненты в разных проектах.
  • Делайте сложные вещи простыми: Монорепо, дизайн-системы и микрофронтенды.

Попробуйте Bit бесплатно и с открытым исходным кодом→

Узнать больше