Drupal и сторонний API: чек-лист перед продакшеном
Интеграция Drupal со сторонним API ломается не на отправке первого запроса, а на таймаутах, дублях, истёкших токенах и непредсказуемых ответах. Проверьте архитектуру, безопасность, очередь, кэш и наблюдаемость до релиза.

Первый успешный запрос к API ещё не означает, что интеграция готова к продакшену. На реальном сайте внешний сервис ответит с задержкой, вернёт 429, изменит формат поля или станет недоступен в момент оформления заказа. Для Drupal 10.3+ и Drupal 11.x проверка должна охватывать не только код клиента, но и очередь, конфигурацию, повторные запросы, логи и сценарий восстановления.
Сначала выберите границу интеграции
Главный архитектурный вопрос — где ждать ответ стороннего сервиса. Если пользователь не может продолжить без результата API, синхронный запрос допустим. Если данные можно получить позже, выносите операцию из веб-запроса.
| Подход | Когда применять | Цена решения |
|---|---|---|
| Синхронный HTTP-запрос | Проверка доступности, расчёт цены, получение короткого списка | Пользователь ждёт ответ; сбой API влияет на страницу |
| Drupal Queue API | Импорт, отправка событий, массовое обновление сущностей | Нужны повторные попытки, обработчик ошибок и контроль дублей |
| Периодический запуск через cron | Регулярная сверка данных и восстановление пропущенных изменений | Данные появляются с задержкой; нужно хранить курсор или дату синхронизации |
Не превращайте любой вызов в очередь автоматически. Для запроса, без которого нельзя показать пользователю актуальную стоимость, очередь создаст другую проблему: интерфейс получит устаревшие данные. И наоборот, импорт нескольких тысяч записей в веб-запросе почти гарантированно приведёт к таймауту или повторному запуску операции.
Продакшен-интеграция — это не «мы умеем вызвать endpoint», а заранее описанный ответ на четыре события: API медленный, API недоступен, ответ изменился, запрос выполнился дважды.
Проверьте HTTP-клиент и границы ожидания
В Drupal для исходящих HTTP-запросов обычно используют клиент, доступный через сервис http_client. Не создавайте новый клиент в каждом методе и не прячьте сетевой вызов в шабплейте или хуке, который вызывается при каждом рендеринге страницы.
Пример ниже предназначен для Drupal 10.3+ и 11.x. Он показывает фрагмент сервиса, которому передали стандартный HTTP-клиент Drupal. URL, заголовки авторизации и формат ответа зависят от конкретного API, поэтому их нельзя копировать без адаптации.
<?php
namespace Drupal\example_api;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
use Psr\Log\LoggerInterface;
final class RemoteApiClient {
public function __construct(
private ClientInterface $httpClient,
private LoggerInterface $logger,
) {}
public function fetch(string $resourceId): ?array {
try {
$response = $this->httpClient->request('GET', 'https://api.example.test/resources/' . rawurlencode($resourceId), [
'connect_timeout' => 2.0,
'timeout' => 5.0,
'http_errors' => false,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer YOUR_API_KEY',
],
]);
}
catch (GuzzleException $exception) {
$this->logger->error('Remote API request failed: {message}', [
'message' => $exception->getMessage(),
]);
return NULL;
}
$status = $response->getStatusCode();
if ($status < 200 || $status >= 300) {
$this->logger->warning('Remote API returned HTTP {status}', [
'status' => $status,
]);
return NULL;
}
$data = json_decode($response->getBody()->getContents(), TRUE);
return is_array($data) ? $data : NULL;
}
}
Таймаут подключения и общий таймаут — разные ограничения. Первый защищает от зависшего установления соединения, второй ограничивает всю операцию. Значения из примера не универсальны: замерьте нормальное время ответа именно вашего API и оставьте запас, не превращая страницу в ожидание на десятки секунд.
Что проверить вокруг запроса
- URL, версию API и обязательные заголовки берите из конфигурации, а не из кода.
- Секреты храните вне репозитория и не выводите в сообщения исключений, watchdog и трассировки.
- Перед разбором JSON проверяйте HTTP-статус и тип тела ответа.
- Ограничивайте размер ответа, если провайдер это поддерживает, и не принимайте без проверки массив, который должен быть объектом.
- Передавайте идентификатор запроса провайдера в лог, если API его возвращает. Это ускоряет разбор инцидента.
Идемпотентность1 особенно важна для POST и операций изменения данных. Повторная отправка после таймаута может создать дубль, хотя первый запрос уже дошёл до провайдера. Используйте поддерживаемый API ключ идемпотентности или собственный уникальный идентификатор операции, если это предусмотрено контрактом.
Сделайте ошибки частью контракта
Не сводите все неуспешные ответы к сообщению «API недоступен». Для приложения это разные ситуации:
- 401 или 403. Проверьте секрет, права приложения и окружение. Повторять такой запрос бессмысленно.
- 404. Ресурс отсутствует или его идентификатор устарел. Сохраните состояние «не найден», а не запускайте бесконечные повторы.
- 409. Возможен конфликт версии или повтор операции. Нужна логика сверки, а не слепой retry.
- 429. Сервис ограничил частоту. Учитывайте заголовок
Retry-After, если он есть, и снижайте параллельность. - 5xx и сетевые ошибки. Повторяйте только безопасные операции и ограниченное число раз. Для записи используйте очередь и защиту от дублей.
Тело ответа внешнего сервиса не должно напрямую попадать в пользовательский HTML. Сначала проверьте структуру, преобразуйте значения в ожидаемые типы и экранируйте вывод средствами Drupal. Отдельно зафиксируйте, что происходит с локальными данными при частичном успехе: например, три записи импортировались, а четвёртая завершилась ошибкой.
Очередь, повторы и cron
Для массовой синхронизации добавляйте в очередь небольшие задания: одну сущность, страницу API или ограниченную пачку. Большое задание труднее повторить и сложнее восстановить после фатальной ошибки.
У задания должны быть:
- уникальный идентификатор объекта и операции;
- число попыток и время последней попытки;
- отдельное состояние для временной ошибки и постоянного отказа;
- защита от параллельной обработки одной записи;
- понятное действие после исчерпания повторов: ручной запуск, отчёт или отдельная очередь ошибок.
Повторяйте запрос с увеличивающейся паузой, а не сразу после ошибки. Не повторяйте 400, 401, 403 и 404 без изменения входных данных. Для синхронизации по расписанию храните курсор, дату последнего успешного изменения или другой маркер, который предлагает API. Иначе каждый запуск cron начнёт импорт с начала.
Конфигурация и секреты перед релизом
Разделите кодовую конфигурацию и значения окружения. URL тестового API, идентификатор клиента, таймауты и режим отладки не должны случайно попасть в продакшен через экспорт конфигурации. Секреты не стоит хранить в обычной конфигурации сайта, если доступ к ней получают разработчики или она попадает в репозиторий.
Проверьте отдельно:
- включён ли нужный модуль и зарегистрирован ли сервис в контейнере Drupal;
- доступен ли внешний DNS и исходящий HTTPS с продакшен-сервера;
- совпадают ли сертификаты, часовой пояс и формат дат с ожиданиями API;
- не включён ли тестовый endpoint в production settings;
- можно ли безопасно заменить ключ без изменения кода и полного простоя сайта.
Логи, метрики и проверка перед выпуском
Логируйте длительность запроса, HTTP-статус, имя операции и безопасный идентификатор объекта. Не логируйте токены, cookies, полные заголовки авторизации и персональные данные из тела ответа. Если провайдер присылает чувствительные поля, маскируйте их до записи.
Минимальный набор наблюдаемости:
- количество запросов по endpoint и статусу;
- время ответа и количество таймаутов;
- размер очереди и возраст самого старого задания;
- число окончательно неуспешных операций;
- дата последней успешной синхронизации.
На staging пройдите сценарии с валидным ответом, пустым результатом, повреждённым JSON, 401, 404, 429, 500, медленным соединением и повторным запуском той же операции. Затем проверьте восстановление: отключите API, накопите задания, верните доступ и убедитесь, что очередь догоняет изменения без дублей.
Где подход ломается
Синхронная интеграция ломается, когда внешний сервис становится частью критического пути страницы. Даже короткий таймаут, умноженный на несколько последовательных вызовов, ухудшает загрузку и увеличивает число ошибок. Очередь не спасает, если обработчик не идемпотентен: после перезапуска одно и то же событие может создать две сущности.
Кэш тоже не заменяет контракт с API. Он снижает число запросов, но способен показать устаревшие данные и скрыть проблему до момента истечения срока хранения. Укажите срок жизни кэша, событие сброса и допустимую свежесть данных. Инвалидация кэша Drupal — отдельная тема; здесь важно не смешивать её с повторной отправкой внешнего запроса.
Что сделать следующим шагом
Составьте короткий runbook для дежурного: где посмотреть последнюю успешную синхронизацию, как отличить 429 от неверного ключа, как безопасно остановить очередь и как повторить одну неуспешную операцию. После релиза неделю наблюдайте не только за HTTP-ошибками, но и за бизнес-результатом: числом дублей, пропущенных объектов и задержкой между изменением во внешнем API и Drupal.
1 Идемпотентность — свойство операции, при котором повтор одного и того же запроса не меняет итог после первого успешного выполнения.
Тендерный специалист 44-ФЗ / 223-ФЗ: Разбор PDF-документации закупки, поиск «заточек» под поставщика, чек-лист заявки.
Спросить за 20 ₽Источники: Drupal: Services and dependency injection, Drupal: Managing dependencies with Composer
Частые вопросы
Какую версию Drupal учитывать при разработке интеграции?
Нужно ли повторять каждый запрос, если API вернул ошибку?
Что будет, если API ответит таймаутом после изменения данных?
Можно ли хранить токен API в конфигурации Drupal?
Что делать, если очередь Drupal постоянно растёт?
Можно ли выводить ответ стороннего API прямо в шаблон?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.