Веб-приложение на чужом API: как работать без своей копии данных

Обычно данные партнёра выгружают к себе в базу. Но бывает, что поставщик это прямо запрещает. Разбираем на примере сервиса для покупки авто с японских аукционов, как построить быстрый каталог на 37 000 лотов, когда каждая страница — живой запрос к чужому API.

Когда приложению нужны чужие данные, первое желание — выгрузить их к себе. Настроил ночной импорт, положил всё в свою базу и дальше работаешь с ней как с родной: быстро, предсказуемо, без оглядки на партнёра.

В сентябре мы делали сервис для агента, который покупает машины на японских аукционах. Лоты приходят от поставщика аукционных данных: около 37 000 актуальных лотов и 2,2 миллиона итогов торгов за три месяца. Первое же правило поставщика: базу целиком не выгружать и из своей БД не показывать. Каждый список и каждая карточка — запрос к API по действию пользователя. Вот как с этим жить.

Что это значит для архитектуры

API поставщика устроен как SQL поверх HTTP: приложение отправляет SELECT и получает JSON. Один ответ занимает 0,2–0,3 секунды. Звучит терпимо, пока не посчитаешь. Страница каталога — это список, счётчик и пять фасетов, то есть семь запросов. Страница статистики — тринадцать, часть из них считает агрегаты по двум миллионам строк. Если слать их по очереди, пользователь ждёт секунды. В первой версии архив торгов на пустом кэше открывался 44 секунды.

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

Запросы идут пакетом

Все запросы страницы уходят одновременно через curl_multi. Тринадцать запросов статистики занимают столько же времени, сколько самый долгий из них. После этой и ещё нескольких правок холодная загрузка архива сократилась с 44 до 10 секунд, а каталога — с 1,12 до 0,33 секунды.

Кэш с разными сроками

Правила поставщика разрешают кэшировать справочники, счётчики и результаты конкретных запросов на разумное время. Мы развели сроки по смыслу данных: открытые торги живут в кэше 30 минут, итоги прошедших торгов — сутки, справочники марок и моделей обновляются по таймеру.

Устаревшая копия не заставляет пользователя ждать. Он сразу получает то, что лежит в кэше, запись помечается на обновление, и её освежает фоновая команда. Этот подход называют stale-while-revalidate. Есть и предохранитель: копию старше четырёх сроков жизни приложение всё-таки запрашивает заново, пока посетитель ждёт. Лучше подождать секунду, чем показать позавчерашние торги.

Раз в пять минут таймер прогревает самое популярное: каталог, архив за месяц, мотоциклы и спецтехнику. Долго ждать может фоновая задача, посетитель — нет. На прогретом кэше PHP собирает страницу за 16–34 миллисекунды.

Запросы собираются из белого списка

Раз API принимает SQL, собирать его из того, что пришло в адресной строке, нельзя. Запросы строит отдельный класс, который знает допустимые колонки и операторы. Фильтр «пробег до 80 000 км» превращается в условие по разрешённой колонке с числом, а не в кусок текста, подставленный в запрос. Даже если доступ только на чтение, произвольный запрос из браузера может нагрузить чужой сервер или показать то, что показывать не нужно.

Когда поставщик отвечает медленно

Чужой API — это ещё и чужие сбои. Поэтому любой ответ дольше двух секунд пишется в журнал с разбивкой: сколько времени ушло на API, сколько на картинки поставщика, сколько на свою базу. Когда кто-то говорит «иногда долго грузится», по журналу сразу видно, чей это тормоз.

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

Что получилось

Каталог, карточка лота, калькулятор «под ключ» и архив торгов работают на живых данных поставщика и не нарушают его правил. Всё остальное живёт в своей базе и связано с лотами через снимки: заявки на ставку, встроенная CRM для менеджеров, кабинет клиента, чат с push-уведомлениями. Каталог на живом API заработал в первые дни разработки, а через две недели сервис переехал на домен заказчика.

Если у вас похожая задача, например сервис поверх данных партнёра, маркетплейса или отраслевой базы, посмотрите кейс целиком и страницу «Разработка веб-приложения».

Разработка веб-приложения
Услуга по теме — обсудим ваш проект.

Есть задача по теме? Напишите — вернёмся с оценкой и сроками.

Написать в Telegram → Написать в MAX