Шесть шаблонов интеграции для надежного соединения сервисов Node.js
Изучите основные принципы надежной интеграции с Node.js: модель запрос-ответ, поллинг, вебхуки, ключи API, механизмы аутентификации JWT и OAuth, повторные попытки с задержками и маппинг данных.
Источники: 6 API Integration Patterns Every Node.js Integration Engineer Must Know
Отправка HTTP-запроса — это просто; настоящая задача заключается в поддержании работоспособности интеграции, когда другая система работает медленно, не функционирует или имеет иной формат. В этом руководстве рассматриваются шесть паттернов, которые часто встречаются при взаимодействии сервисов Node.js с поставщиками платежей, CRM-системами, ERP-системами и друг с другом, а также ошибки, из-за которых демо-версия не превращается в надежную интеграцию.
1. Запрос-ответ: синхронная основа
В модели запрос-ответ клиент отправляет запрос, сервер выполняет работу, а ответ возвращается по той же связи. Эта модель охватывает большинство повседневных операций: вход в систему, создание или обновление записей, получение информации о клиенте, удаление ресурса.
app.get("/users/:id", async (req, res) => {
const user = await db.findUser(req.params.id);
res.json(user);
});
Его легко отлаживать, и он используется по умолчанию для операций CRUD. Его слабостью являются задачи с длительным выполнением: большие отчеты или файлы могут превысить время ожидания клиента или прокси. Также следует отметить, что этот обработчик возвращает null с кодом статуса 200, когда пользователя нет; настоящий эндпоинт должен возвращать код 404.
2. Поллинг: запросы до завершения работы
Когда результат нельзя получить сразу, сервер может принять задачу, вернуть идентификатор и позволить клиенту периодически проверять статус. К типичным случаям относятся ожидание завершения оплаты, генерации отчета, транскодирования видео или отправки заказа.
При запуске задачи возвращается ее ID:
POST /reports
Response:
{
"jobId": "abc123"
}
Затем клиент периодически проверяет эндпоинт статуса:
GET /reports/abc123/status
до тех пор, пока задача не сообщит о завершении работы:
{
"status": "completed"
}
Метод опроса прост в реализации и совместим с брандмауэрами, поэтому многие корпоративные системы до сих пор от него зависят. Недостаток заключается в трате трафика, поскольку большинство проверок возвращают результат «еще нет». Необходимо распределять запросы во времени (соблюдая параметр Retry-After, если он указан), установить общий срок выполнения и обрабатывать как случаи failed, так и completed.
3. Webhooks: возможность вызова с другой стороны
Webhooks меняют направление взаимодействия. Вместо того чтобы ваш сервис постоянно запрашивал информацию, провайдер отправляет HTTP-запрос на ваш конечный пункт при возникновении события: успешная оплата, оформление заказа, поступление сообщения, регистрация клиента, изменение запасов. Stripe, Slack, GitHub и Shopify в значительной степени используют эту модель.
Минимальный пример приемника в Express:
app.post("/webhook/payment", (req, res) => {
const event = req.body;
if (event.type === "payment.success") {
console.log("Payment received!");
}
res.sendStatus(200);
});
Это показывает основную структуру, но производственный приемник требует дополнительных элементов:
- Проверьте отправителя. Любой может опубликовать данные по публичному URL. Провайдеры обычно подписывают каждый пакет данных, и вы должны проверить эту подпись (обычно по сравнению с исходным содержимым запроса), прежде чем доверять событию.
- Ожидайте дубликатов. Попытки доставки обычно повторяются до получения подтверждения, поэтому одно и то же событие может прийти несколько раз. Храните идентификаторы обработанных событий и делайте обработчики идемпотентными.
- Быстро подтверждайте получение. Быстро возвращайте ответ 2xx и откладывайте трудоемкие операции в очередь; в противном случае у провайдера может истечь время ожидания, и он отправит данные заново.
Интеграция, основанная на событиях, все чаще предпочтительна, поскольку она устраняет трафик для опроса статуса.
4. Аутентификация: подтверждение того, кто вызывает
Каждая интеграция начинается с учетных данных. Три подхода покрывают большинство случаев.
Ключи API
Самый простой способ — отправка общего секрета в заголовке:
GET /users
x-api-key: abc123xyz
В Node.js с использованием axios ключ должен браться из конфигурации, а не из исходного кода:
axios.get(url, {
headers: {
"x-api-key": process.env.API_KEY
}
});
API-ключи подходят для внутренних инструментов и простых интеграций от сервера к серверу. Сами по себе они не имеют срока действия и информации об идентичности пользователя, поэтому их необходимо регулярно обновлять и ограничивать их диапазон использования настолько, насколько это позволяет поставщик.
JSON Web Tokens
JWT обеспечивают аутентификацию без сохранения состояния. Типичная последовательность действий:
- Пользователь входит в систему.
- Сервер выдает подписанный токен.
- Клиент хранит этот токен.
- Каждый последующий запрос передает его в заголовке:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
Выдача токена в Node.js:
const token = jwt.sign(
{ userId: 1 },
process.env.JWT_SECRET
);
Любой сервис с ключом верификации может проверять токен без хранилища сессий, что подходит для микросервисов. Однако существует проблема отзыва: токен остается действительным до истечения срока, поэтому всегда необходимо указывать параметр expiresIn, который в этом фрагменте опущен.
OAuth 2.0
OAuth 2.0 обеспечивает возможность делегированного доступа: вход через Google, вход в Facebook, системы единого входа от Microsoft Enterprise, подключения к CRM и любые сторонние приложения, действующие от имени пользователя. В типичном процессе получения кода авторизации:
- Пользователь соглашается на запрашиваемый доступ у поставщика.
- Поставщик перенаправляет пользователя обратно с временным кодом авторизации.
- Ваш сервер обменивает этот код на токен доступа.
- Запросы к API отправляются с использованием токена доступа.
Стоит изучить подробно: токены доступа и обновления, процесс Authorization Code, процесс Client Credentials для вызовов между устройствами, PKCE и OpenID Connect — слой идентификации поверх OAuth.
5. Устойчивость: проектирование с учетом сбоев
Сети теряют соединение, а зависимости перестают работать. Надежные интеграции предполагают возможность таких ситуаций.
Повторная попытка выполнения неудачных вызовов
Базовый цикл повторных попыток:
for (let i = 0; i < 3; i++) {
try {
return await axios.get(url);
} catch (error) {
console.log("Retrying...");
}
}
В текущей реализации попытка выполняется сразу без паузы, и если все три попытки проваливаются, возвращается значение undefined вместо того, чтобы выбросить последнюю ошибку, поэтому вызывающий код не может понять, в чем проблема. Необходимо выбросить ошибку снова после последней попытки и повторять попытки только в случае временных сбоев (сетевые ошибки, таймауты, коды 5xx, 429), а не ошибок 4xx, которые будут вызывать те же проблемы каждый раз. Будьте осторожны при повторных попытках выполнения нейдемпотентных запросов, таких как платежи, если только API не поддерживает ключи идемпотентности.
Время ожидания
Запрос без времени ожидания может застрять навсегда, занимая ресурсы при этом. Всегда устанавливайте его:
axios.get(url, {
timeout: 5000
});
Экспоненциальное увеличение интервалов
Повторные попытки с фиксированным интервалом приводят к постоянной нагрузке на неисправный сервис:
1s
1s
1s
1s
Удваивание времени ожидания с каждой попыткой даёт сервису возможность восстановиться:
1s
2s
4s
8s
16s
Расчёт выполняется одной строкой кода:
const delay = attempt =>
Math.pow(2, attempt) * 1000;
Установите максимальное время задержки и добавьте случайные отклонения, чтобы множество клиентов, выходящих из строя одновременно, не пытались выполнить запросы синхронно.
Прерыватели цепочек ошибок
Прерыватель цепочек ошибок подсчитывает количество сбоев в работе сервиса нижнего уровня; при превышении заданного порога он прекращает вызовы на определённый период охлаждения перед отправкой нового запроса. Это помогает избежать каскадных сбоев, исчерпания ресурсов и полномасштабных сбоев всей системы. Opossum — популярный выбор в Node.js; Resilience4j выполняет аналогичную функцию в Java, а Hystrix используется в более старых системах.
6. Преобразование данных: конвертация между системами
Два системы редко совпадают по формату данных, поэтому значительная часть работы по интеграции сводится к их преобразованию.
Сопоставление полей
Одна система использует формат camelCase:
{
"firstName": "John"
}
другая ожидает формат snake_case:
{
"first_name": "John"
}
Небольшой маппер соединяет их:
function transform(user) {
return {
first_name: user.firstName
};
}
Функции-мапперы легко тестировать в режиме единиц, а также позволяют избавить бизнес-логику от особенностей поставщиков данных.
JSON и XML
Многие устаревшие системы по-прежнему используют XML, в то время как новые сервисы генерируют JSON, поэтому слой интеграции часто осуществляет преобразование между ними в обоих направлениях.
Даты
Одна сторона может отправлять данные в формате ISO:
2026-06-26
в то время как другая ожидает формат с указанием дня в первую очередь:
26/06/2026
Преобразуйте данные явно и четко указывайте часовые пояса, если значения включают время; дата, смещенная через полночь, является классической ошибкой интеграции.
Суммы в валюте
Один сервис требует указания суммы с десятичной точкой:
{
"amount": 10.50
}
Другой сервис требует использования целых чисел для мелких единиц:
{
"amount_cents": 1050
}
Значения с плавающей точкой не могут точно представлять любую десятичную числовую величину, поэтому при преобразовании необходимо округлять значения (например, Math.round(amount * 100)) и внутри системы использовать целые числа для обработки сантов.
Как на практике сочетаются эти подходы
Типичная архитектура корпоративных систем включает шлюз Node.js, расположенный перед несколькими бэкендами:
Frontend
|
Node.js API Gateway
|
+-- Authentication Service
+-- Payment Service
+-- CRM Service
+-- ERP Systems
+-- Notification Service
Эти соединения объединяют вышеописанные подходы: API с механизмом запрос-ответ, JWT и OAuth, вебхуки, технологии повторных попыток и защиты от сбоев, слои преобразования данных, а также системы мониторинга и логирования. Для инженеров, переходящих к работе по интеграции, наиболее полезными первоначально будут REST API, вебхуки, JWT, OAuth 2.0, преобразование данных и проектирование на основе событий; затем — технологии повторных попыток и обеспечения устойчивости, а уже потом — механизмы опроса и защиты от сбоев.
Основные выводы
- Используйте механизм запрос-ответ для быстрых операций; переходите на поллинг или вебхуки, когда обработка занимает время.
- Проверяйте подписи вебхуков, удаляйте дублирующиеся события и быстро подтверждайте их получение.
- Храните учетные данные в конфигурации, устанавливайте срок действия токенов и тщательно изучите принципы работы OAuth.
- Повторяйте попытки только при временных сбоях, используя таймауты, ограничения по экспоненциальному откату и добавление случайных задержек.
- Изолируйте преобразование данных в тестированных чистых функциях и уделяйте особое внимание датам и финансовым данным.