-10%Переходите к нам от другого подрядчика — дадим скидку на первый этап работ

GraphQL API поверх 1С-Битрикс

GraphQL API поверх 1С-Битрикс: единый endpoint, схема данных и клиенты — SPA и мобильное приложение

Фронтенд-команда просит «отдайте товар сразу с ценами, остатками, картинками и разделом одним запросом», мобильное приложение жалуется на медленную загрузку карточки, а бэкенд каждый раз лепит новый REST-метод под очередной экран. Знакомая картина для магазина на Битриксе, который перерос классическую серверную вёрстку и оброс SPA-витриной и приложением. Ответ на этот запрос — GraphQL-слой поверх данных 1С-Битрикс: единая точка входа, где клиент сам описывает нужные поля, а сервер собирает ответ из инфоблоков, торгового каталога и заказов.

Ниже — практический разбор того, как спроектировать и внедрить GraphQL над Битриксом, не переписывая ядро: как устроена схема, как писать резолверы на D7, как убить проблему N+1, кэшировать ответы и защитить endpoint от тяжёлых запросов. Это выжимка из наших проектов по разработке на 1С-Битрикс — с прицелом на реальную нагрузку, а не на демо из документации.

Коротко

  • GraphQL — это отдельный endpoint поверх Битрикса, а не замена ядра: витрина работает как прежде.
  • Клиент запрашивает ровно нужные поля одним запросом — меньше трафика и меньше обращений, чем у REST.
  • Главный технический враг — проблема N+1; лечится паттерном DataLoader (пакетная загрузка).
  • Кэшируйте на уровне резолверов через тегированный кэш Битрикса, а не тело запроса целиком.
  • Обязательная защита: лимит глубины, стоимость запроса (complexity), тайм-аут и персистентные запросы.

Что такое GraphQL и зачем он поверх Битрикса

GraphQL — это язык запросов к данным и среда их выполнения на сервере. В отличие от привычного REST, где каждый ресурс живёт по своему URL и возвращает фиксированный набор полей, у GraphQL одна точка входа (обычно /graphql), а форму ответа задаёт сам клиент. Он присылает запрос, описывающий дерево нужных полей, а сервер возвращает JSON ровно той же формы — ни больше, ни меньше.

Для магазина на 1С-Битрикс это решает застарелую боль. Данные магазина живут в инфоблоках (каталог, разделы, свойства), в модуле торгового каталога (цены, остатки, торговые предложения), в модуле заказов и в профилях пользователей. Классическая серверная вёрстка собирает всё это на PHP и отдаёт готовый HTML. Но как только появляется SPA-витрина на Vue или React, а следом мобильное приложение, возникает вопрос: как отдать этим клиентам данные удобно и экономно? Плодить десятки узкоспециализированных REST-методов — путь в тупик поддержки.

GraphQL-слой становится «фасадом» над всеми этими источниками. Он не хранит данные сам — он знает, как достать их из Битрикса через D7 API, и предоставляет клиенту единый типизированный контракт. Один экран приложения запрашивает у товара название и цену, другой — характеристики и отзывы, третий — остатки по складам, и всё это без единой правки на бэкенде, потому что схема уже описывает все доступные поля.

Важно сразу очертить границы применимости. GraphQL — не «ускоритель сайта» и не замена кэшу. Это способ организовать доступ к данным для клиентских приложений. Если у вас классический сайт на серверной вёрстке без SPA и мобильных клиентов, отдельный GraphQL-слой скорее избыточен. Он раскрывается там, где есть несколько разных потребителей одних и тех же данных с разными требованиями к их форме.

Важно: GraphQL-слой поверх Битрикса — это архитектурное дополнение, а не миграция. Ядро, бизнес-логика, обмен с 1С и админка остаются на месте. Вы добавляете точку входа для внешних клиентов, а не переносите магазин на новый движок.

GraphQL против REST: в чём разница

Чтобы решение было осознанным, полезно честно сравнить два подхода на языке магазина. И REST, и GraphQL в экосистеме Битрикс вполне жизнеспособны — вопрос в сценарии. О REST-подходе в Битриксе у нас есть отдельный разбор про REST API, вебхуки и приложения; здесь же сфокусируемся на отличиях с точки зрения фронтенда.

КритерийRESTGraphQL
Форма ответаФиксирована сервером на каждый ресурсОпределяется клиентом в запросе
Число запросов на экранЧасто несколько (товар, цены, остатки отдельно)Обычно один запрос с вложенностью
Лишние данные (over-fetching)Частая проблемаОтсутствуют — берётся только нужное
Нехватка данных (under-fetching)Требует новых методовРешается расширением запроса
Кэширование по URLПростое, из коробкиСложнее: нужен кэш резолверов или персист-запросы
Контракт и типизацияПо документации / OpenAPIСтрогая схема как единый источник правды
Порог входаНизкийВыше: схема, резолверы, защита

Ключевой практический выигрыш GraphQL — устранение over-fetching и under-fetching. Представьте карточку товара в мобильном приложении: ей нужны название, одна картинка, цена и признак наличия. REST-метод catalog.product.get вернёт всю модель товара со всеми свойствами — десятки лишних полей, которые приложение выбросит. GraphQL отдаст ровно четыре запрошенных поля. На каталоге, где такие карточки грузятся десятками, экономия трафика и времени рендера становится заметной.

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

Загрузка карточки товара для мобильного экрана REST: 3 запроса GET товар (все поля) GET цены GET остатки много лишних полей в ответе GraphQL: 1 запрос name · price inStock · image ровно 4 поля ничего лишнего
Схема: там, где REST делает несколько запросов и отдаёт лишние поля, GraphQL собирает точный набор данных одним обращением.

Архитектура GraphQL-слоя над Битрикс

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

Технически на PHP endpoint чаще всего собирают на библиотеке webonyx/graphql-php — это де-факто стандарт для GraphQL-сервера на PHP, совместимый с окружением Битрикса. Она отвечает за парсинг запроса, валидацию по схеме и обход дерева резолверов. Всё, что касается данных, вы пишете сами поверх D7. Такой подход держит бизнес-логику внутри привычного ядра Битрикса, а GraphQL оставляет тонким транспортным слоем.

Запрос проходит по цепочке так: клиент отправляет POST на endpoint, слой безопасности проверяет глубину и стоимость, движок валидирует запрос по схеме, затем обходит дерево и для каждого поля вызывает резолвер, а тот берёт данные из источников (с кэшем и батчингом). Собранный JSON возвращается клиенту. Если ответ медленный — смотрим резолверы и источники; если запрос вообще отклонён — смотрим слой безопасности и схему.

SPA / Mobile клиенты /graphql endpoint валидация · защита Схема + резолверы контракт D7 API Битрикса + DataLoader / кэш Инфоблоки Каталог · цены Заказы · юзеры
Схема: тонкий GraphQL-endpoint валидирует запрос, резолверы через D7 API достают данные из инфоблоков, каталога и заказов — ядро Битрикса остаётся источником правды.

Схема (SDL): типы, запросы, мутации

Схема — сердце GraphQL. Она описывается на языке SDL (Schema Definition Language) и служит единым контрактом: клиент видит все доступные типы и поля, а сервер по этой же схеме валидирует входящие запросы. Проектирование схемы — это, по сути, моделирование предметной области магазина в терминах, удобных фронтенду, а не таблиц базы.

Минимальный пример схемы каталога может выглядеть так:

type Product {
  id: ID!
  name: String!
  slug: String!
  price: Money!
  inStock: Boolean!
  section: Section
  images: [Image!]!
  props: [PropertyValue!]!
}

type Section {
  id: ID!
  name: String!
  products(limit: Int = 20, offset: Int = 0): [Product!]!
}

type Query {
  product(id: ID!): Product
  catalog(sectionId: ID!, filter: ProductFilter): [Product!]!
}

type Mutation {
  addToCart(productId: ID!, qty: Int = 1): Cart!
}

Обратите внимание на несколько принципов. Восклицательный знак означает non-null — поле гарантированно вернёт значение. Связи между сущностями описываются как вложенные типы (у товара есть section, у раздела — список товаров), и клиент может обходить их в глубину одним запросом. Аргументы полей (limit, filter) позволяют управлять выборкой прямо из запроса, что заменяет россыпь узких REST-методов.

Отдельного внимания заслуживает моделирование доменных типов вроде Money. Не отдавайте цену «сырым» числом с плавающей точкой — заведите тип с суммой, валютой и, при необходимости, форматированным представлением. Это избавляет фронтенд от логики форматирования и локализации и делает контракт устойчивее к изменениям. Тот же принцип касается остатков, скидок и торговых предложений: описывайте их как осмысленные доменные типы, а не как плоский слепок полей инфоблока.

Совет: проектируйте схему от потребностей экранов, а не от структуры инфоблоков. Схема — это публичный контракт, и его дорого менять. Лучше потратить время на продуманные имена и типы, чем потом ломать совместимость с уже выпущенным мобильным приложением.

Резолверы и доступ к данным через D7

Резолвер — это функция, которая знает, как получить значение конкретного поля. Движок GraphQL обходит дерево запроса и для каждого поля вызывает соответствующий резолвер, передавая ему родительский объект, аргументы и контекст запроса. Именно здесь GraphQL-слой встречается с Битриксом: внутри резолвера вы обращаетесь к D7 API.

Схематично резолвер товара по идентификатору на PHP выглядит так:

use Bitrix\Iblock\Elements\ElementCatalogTable;

$resolvers['Query']['product'] = function ($root, $args, $context) {
    $id = (int)$args['id'];

    $element = ElementCatalogTable::getByPrimary($id, [
        'select' => ['ID', 'NAME', 'CODE'],
    ])->fetchObject();

    if (!$element) {
        return null;
    }
    return [
        'id'    => $element->getId(),
        'name'  => $element->getName(),
        'slug'  => $element->getCode(),
        // цены и остатки резолвятся отдельными полями лениво
    ];
};

Ключевая идея — ленивость. Резолвер верхнего уровня не тянет сразу все цены, картинки и остатки. Он возвращает «заготовку» товара, а поля price, images, inStock резолвятся отдельными функциями и только если клиент их запросил. Если приложению нужны лишь название и цена, резолверы картинок и характеристик вообще не вызовутся, и лишних запросов к базе не будет.

При работе с D7 придерживайтесь нескольких правил, чтобы резолверы оставались быстрыми и предсказуемыми. Всегда указывайте явный select с нужными полями — не тяните всю модель. Выносите обращения к каталогу и ценам в отдельные источники данных, чтобы их можно было кэшировать и батчить. И держите бизнес-правила (доступность товара, расчёт цены для группы пользователя) внутри сервисов ядра, а не размазывайте по резолверам — резолвер должен оставаться тонкой обёрткой над доменной логикой.

Проблема N+1 и DataLoader

Это главная техническая ловушка любого GraphQL-сервера, и на каталоге Битрикса она проявляется особенно ярко. Представьте запрос: «дай 20 товаров раздела, и у каждого — его раздел и остаток». Наивная реализация сделает один запрос за списком товаров, а затем по одному запросу за разделом и остатком на каждый из 20 товаров. Итого 1 + 20 + 20 = 41 обращение к базе там, где могло быть три.

Отсюда и название — N+1: один запрос за списком плюс N запросов за деталями каждого элемента. С ростом выборки и вложенности число паразитных обращений растёт лавинообразно, и быстрый на демо-данных GraphQL превращается в тормоз на реальном каталоге.

Решение — паттерн DataLoader. Вместо того чтобы грузить связанную сущность немедленно, резолвер регистрирует в загрузчике нужный идентификатор и возвращает обещание. Загрузчик копит идентификаторы в пределах одного тика выполнения, а затем делает один пакетный запрос по всем сразу и раздаёт результаты обратно. 20 обращений за разделами превращаются в одно с условием ID IN (...).

// Псевдокод батч-загрузчика разделов
$sectionLoader = new DataLoader(function (array $ids) {
    $rows = SectionTable::getList([
        'filter' => ['@ID' => $ids],
        'select' => ['ID', 'NAME'],
    ])->fetchAll();

    // вернуть значения в том же порядке, что и $ids
    return map_by_id($rows, $ids);
});

// В резолвере товара:
$resolvers['Product']['section'] = function ($product) use ($sectionLoader) {
    return $sectionLoader->load($product['sectionId']);
};

Загрузчик создаётся на каждый запрос и живёт в контексте — так он попутно кэширует уже загруженные сущности в пределах одного обращения. Если один и тот же раздел встречается у пяти товаров, из базы он поднимется один раз. Именно связка «ленивые резолверы + DataLoader» отделяет production-ready GraphQL от учебного примера.

Без DataLoader SELECT id=1 SELECT id=2 SELECT id=3 … SELECT id=N N обращений к базе С DataLoader id=1 id=2 … 1 SELECT IN 1 обращение к базе
Схема: DataLoader копит идентификаторы за один тик и заменяет N отдельных запросов одним пакетным SELECT … IN.

Кэширование ответов GraphQL

Кэш в GraphQL устроен иначе, чем в REST, и это одна из главных причин, почему переход требует инженерной аккуратности. В REST ответ привязан к URL, поэтому его легко кэшировать на уровне HTTP и CDN. В GraphQL все запросы идут POST-ом на один URL с разными телами, и кэшировать ответ целиком почти бессмысленно — двух одинаковых запросов подряд может и не быть.

Поэтому кэшируют не ответ, а данные под ним — на нескольких уровнях:

Уровень кэшаЧто кэшируетКогда сбрасывается
Кэш резолвера / источникаСущность по ID (товар, раздел, цена)По тегам инфоблока при изменении элемента
Кэш в пределах запроса (DataLoader)Повторные обращения к одной сущностиАвтоматически по завершении запроса
Тегированный кэш БитриксаВыборки D7 с привязкой к тегамСобытиями ядра при правке данных
Персистентные запросы на GETЦелый ответ по хэшу операцииПо TTL, отдаётся через CDN

Рабочая связка для магазина на Битриксе выглядит так. Источники данных (товар, цена, остаток по ID) заворачиваются в тегированный кэш ядра: значение кладётся в кэш с тегом инфоблока, а при изменении элемента через админку или обмен с 1С тег сбрасывается автоматически, и кэш инвалидируется точечно. Поверх этого DataLoader снимает дубли в пределах одного запроса. А для публичных, часто повторяющихся выборок (главная витрина, популярный раздел) применяют персистентные запросы, которые можно отдавать через GET и кэшировать на CDN.

Отдельно предостережём от типичной ошибки — попытки закэшировать всё тело ответа по хэшу произвольного запроса. Из-за комбинаторики полей и аргументов процент попаданий будет низким, а инвалидация — кошмаром: непонятно, какой закэшированный ответ сбрасывать при изменении одного товара. Кэшируйте маленькие, адресуемые по ID куски данных с понятными тегами — это и эффективнее, и предсказуемее.

Важно: связывайте кэш резолверов с тегами инфоблоков Битрикса. Тогда обмен с 1С, меняющий цены и остатки, сам инвалидирует нужные записи — вам не придётся вручную чистить кэш или ждать истечения TTL с устаревшими данными на витрине.

Безопасность: глубина, сложность, лимиты

Гибкость GraphQL — это и его уязвимость. Клиент сам конструирует запрос, а значит, может (случайно или намеренно) построить такой, который положит сервер. Классический пример — глубоко вложенный циклический запрос: товар → его раздел → товары раздела → их разделы → и так далее. Без ограничений один POST способен породить экспоненциальный обход и исчерпать ресурсы.

Базовый набор защит, который должен стоять на любом публичном GraphQL-endpoint:

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

Запрос от клиента Валидатор глубина ≤ 10 стоимость ≤ 1000 Лёгкий → выполнить резолверы, база Тяжёлый → отклонить ошибка до базы
Схема: валидатор оценивает глубину и стоимость запроса до обращения к базе — лёгкий пропускает, тяжёлый отклоняет с ошибкой.

Авторизация на уровне полей и резолверов

Защита от перегрузки — только половина безопасности. Вторая половина — контроль доступа к данным. В магазине далеко не всё публично: цена может зависеть от группы пользователя (розница, опт, дилер), персональные данные заказа доступны только владельцу, а внутренние поля товара (себестоимость, поставщик) вообще не должны утекать наружу.

В GraphQL авторизацию удобно делать на нескольких уровнях. На уровне запроса решается, пущен ли клиент к endpoint вообще (по токену, сессии Битрикса, OAuth-приложению). На уровне резолвера — доступен ли конкретный объект этому пользователю (свой заказ против чужого). На уровне поля — можно ли отдать именно это поле (оптовая цена только авторизованному дилеру). Контекст запроса, куда кладётся текущий пользователь и его права, прокидывается во все резолверы и служит основой для этих проверок.

Практический принцип — не доверять клиенту в вопросах прав и всегда сверяться с ядром Битрикса. Группа пользователя, права на инфоблок, признак авторизации — всё это берётся из серверной сессии/токена, а не из параметров запроса. Цена для группы рассчитывается штатными механизмами каталога, а не «подставляется» на фронтенде. GraphQL здесь лишь транспорт: реальные правила доступа живут в ядре, и резолверы обязаны их уважать.

Отдельно стоит продумать сообщения об ошибках. GraphQL возвращает ошибки в структурированном виде, и заманчиво отдать клиенту подробности («у пользователя нет права X на объект Y»). Для публичного API это утечка: наружу отдавайте нейтральное «доступ запрещён», а детали пишите в лог на сервере. Тему защиты кастомного кода мы подробно разбираем в материалах рубрики Разработка и архитектура.

Персистентные запросы и APQ

Персистентные запросы (persisted queries) — приём, который решает сразу три задачи: безопасность, кэш и трафик. Идея проста: вместо того чтобы принимать любой запрос от клиента, сервер знает заранее одобренный список операций, и клиент присылает не текст запроса, а его короткий идентификатор — хэш.

Что это даёт на практике:

Разновидность подхода — Automatic Persisted Queries (APQ). Клиент сначала пробует отправить только хэш; если сервер такой операции ещё не знает, он просит прислать полный текст один раз, регистрирует его и дальше принимает уже по хэшу. Так белый список наполняется автоматически из легитимного клиента, без ручного ведения реестра, но с сохранением преимуществ по кэшу и трафику.

Для публичного магазинного API персистентные запросы — практически обязательная практика в production. Они превращают «открытый для любых запросов» GraphQL в контролируемый набор операций, который вы реально тестировали и оптимизировали. Заодно это упрощает мониторинг: у каждой операции есть имя и хэш, по которым удобно собирать метрики и находить медленные запросы.

Реализация в 1С-Битрикс

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

  1. Заведите свой модуль или компонент под API. Не размазывайте код по init.php — оформите GraphQL-слой как отдельный модуль (папка local/modules/vendor.graphql). Так его удобно версионировать, включать и переносить между проектами.
  2. Подключите библиотеку GraphQL. Через Composer добавьте webonyx/graphql-php в проект. Битрикс совместим с Composer, автозагрузку подключите в модуле. Это движок парсинга и валидации; данные вы пишете сами.
  3. Опишите схему. Соберите типы каталога, заказов и пользователя в SDL или программно. Начните с узкого среза (товар, раздел, корзина) и расширяйте по мере надобности экранов.
  4. Напишите резолверы поверх D7. Для каждого поля — тонкий резолвер, обращающийся к Bitrix\Iblock, модулю catalog и sale. Бизнес-логику держите в сервисах ядра, резолвер оставляйте обёрткой.
  5. Добавьте источники данных с DataLoader. Оберните загрузку товаров, разделов, цен и остатков в батч-загрузчики и тегированный кэш ядра. Это снимает N+1 и лишние обращения к базе.
  6. Настройте endpoint и контекст. Создайте публичную точку входа (например, /api/graphql/), где инициализируется пролог Битрикса, определяется текущий пользователь и собирается контекст запроса с загрузчиками и правами.
  7. Включите защиту. Навесьте правила глубины и стоимости запроса, тайм-аут, пагинацию и rate limiting. Для публичного API подготовьте персистентные запросы и отключите интроспекцию в проде.
  8. Свяжите инвалидацию с обменом. Убедитесь, что изменение товара в админке и обмен с 1С сбрасывают теги кэша резолверов — иначе на витрине повиснут старые цены и остатки.

Отдельно про пролог Битрикса на endpoint. GraphQL-обработчику нужны и авторизация, и доступ к модулям ядра, поэтому подключение prolog_before.php (или инициализация приложения D7) обязательно — так вы получаете текущего пользователя, его группы и права «бесплатно», штатными средствами. Не изобретайте собственную авторизацию поверх Битрикса: используйте его сессии, токены и OAuth-приложения, о которых речь в разборе REST API в Битриксе.

Если вы строите полноценную headless-витрину, где Битрикс остаётся бэкендом, а фронтенд отделён, GraphQL становится её центральным контрактом. Как устроен такой разрыв витрины и бэкенда, мы разбираем в статье про headless-коммерцию на Битриксе — GraphQL-слой из этой статьи там играет роль API-шлюза между слоями.

Важно: не открывайте GraphQL-endpoint без слоя защиты «чтобы потом донастроить». Публичный GraphQL без лимитов глубины и стоимости — это готовый вектор для DoS одним POST-запросом. Защита ставится вместе с endpoint, а не после.

Запрос с фронтенда: Vue и React

Со стороны клиента GraphQL интегрируется через специализированные клиенты (Apollo, urql, Relay) или простыми fetch-запросами. Суть одинакова: фронтенд формирует запрос с нужными полями, отправляет POST на endpoint и получает JSON той же формы. Вот как выглядит запрос карточки товара:

query ProductCard($id: ID!) {
  product(id: $id) {
    name
    price { amount currency }
    inStock
    images(limit: 1) { url }
  }
}

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

Для команд, которые уже работают с Vue или React, GraphQL хорошо ложится на компонентный подход: каждый компонент декларирует свои требования к данным рядом с разметкой, а клиент собирает их в оптимальный запрос. Как выбрать и подключить современный фронтенд-стек к Битриксу, мы разбираем в отдельном материале про современный фронтенд на Vue и React поверх Битрикса — GraphQL там становится естественным способом связать SPA с данными магазина.

Практический совет по фронтенду: не запрашивайте «про запас». Соблазн взять сразу все поля товара «чтобы точно хватило» убивает главное преимущество GraphQL и возвращает вас к over-fetching REST. Запрашивайте ровно то, что рисует конкретный экран, — тогда и трафик, и нагрузка на резолверы останутся минимальными.

Мобильные приложения и GraphQL

Мобильное приложение магазина — сценарий, где GraphQL раскрывается сильнее всего. У мобильного клиента жёсткие ограничения, которых нет у десктопной витрины: нестабильная сеть, дорогой и медленный трафик, батарея, разные версии приложения у пользователей одновременно. GraphQL адресует почти каждое из этих ограничений.

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

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

Мониторинг и метрики

GraphQL-слой нельзя запустить и забыть — за ним нужно наблюдать, причём в разрезе операций, а не только endpoint целиком. «API тормозит» — бесполезная формулировка, когда за одним URL скрываются десятки разных запросов. Вот метрики, на которые стоит смотреть.

МетрикаЧто показываетОриентир
Время выполнения по операцииСкорость каждого именованного запросаДержать хвост p95 под контролем
Число запросов к базе на операциюЕсть ли N+1 и работает ли DataLoaderЕдиницы, а не десятки на запрос
Стоимость (complexity) запросовНасколько тяжёлые запросы приходятНиже установленного лимита
Доля отклонённых запросовСрабатывание защиты по глубине/стоимостиВсплеск — сигнал атаки или кривого клиента
Hit rate кэша резолверовЭффективность кэширования данныхВысокий для товаров и разделов
Ошибки в поле errorsЧастичные и полные сбои резолверовМинимум, с алертами по всплескам

Важная особенность GraphQL — частичные ответы. В отличие от REST, где запрос либо успешен, либо нет, GraphQL может вернуть часть данных и массив ошибок по отдельным полям. Это удобно для устойчивости (упал резолвер отзывов — карточка всё равно отрисуется), но требует внимания в мониторинге: успешный HTTP-статус 200 может скрывать ошибки внутри тела. Считайте ошибки по полю errors, а не только по кодам HTTP.

Число запросов к базе на операцию — самая ценная метрика для здоровья GraphQL. Если оно растёт с размером выборки линейно или хуже, значит где-то протёк N+1 и DataLoader не срабатывает. Держите эту метрику под наблюдением в связке с временем выполнения — вместе они быстро выводят на медленные и «прожорливые» резолверы.

Частые ошибки

Мини-FAQ. Нужен ли отдельный сервер под GraphQL? Нет — endpoint живёт внутри того же Битрикса и использует его пролог, авторизацию и модули. Отдельная инфраструктура нужна лишь на очень высоких нагрузках, и это уже вопрос масштабирования, а не старта.

Чек-лист внедрения

Сведём всё в практический список, по которому удобно принимать готовность GraphQL-слоя перед запуском.

  1. Оценён сценарий. Есть несколько клиентов (SPA, мобильное приложение) с разными требованиями к данным — GraphQL оправдан, а не взят «за моду».
  2. Схема спроектирована от экранов. Типы и поля названы осмысленно, доменные сущности (цена, остаток) выделены, контракт продуман на будущее.
  3. Резолверы тонкие. Логика в сервисах ядра, резолверы — обёртки над D7 с явным select и ленивой загрузкой полей.
  4. N+1 закрыт. Все списочные связи идут через DataLoader; число запросов к базе на операцию проверено и стабильно.
  5. Кэш связан с тегами. Источники данных кэшируются тегированным кэшем Битрикса, обмен с 1С инвалидирует нужные записи.
  6. Защита включена. Стоят лимиты глубины и стоимости, тайм-аут, пагинация, rate limiting; интроспекция в проде закрыта.
  7. Авторизация на месте. Права берутся из ядра, поля с ограниченным доступом проверяются, наружу уходят нейтральные ошибки.
  8. Публичные операции персистентны. Частые запросы оформлены как persisted queries, кэшируются и мониторятся по имени.
  9. Мониторинг настроен. Собираются время по операции, число запросов к базе, стоимость, ошибки в поле errors и hit rate кэша.

Вывод

GraphQL поверх 1С-Битрикс — это не миграция и не мода, а удобный API-фасад для случаев, когда у данных магазина появляется несколько разных потребителей: SPA-витрина, мобильное приложение, партнёрские интеграции. Он снимает боль over-fetching и under-fetching, даёт единый типизированный контракт и позволяет клиенту брать ровно нужное одним запросом. Но за гибкость приходится платить инженерной аккуратностью: без DataLoader вы получите N+1, без лимитов глубины и стоимости — уязвимость, без связки кэша с тегами инфоблоков — устаревшие данные на витрине. Сделайте эти вещи правильно — и GraphQL станет прочным мостом между быстрым фронтендом и надёжным ядром Битрикса.

Частые вопросы

Зачем нужен GraphQL, если в Битриксе уже есть REST API?

REST в Битриксе удобен для интеграций «сервер-сервер», но фронтенду и мобильным он часто отдаёт либо слишком много данных, либо слишком мало — и тогда приходится делать несколько запросов подряд. GraphQL позволяет клиенту одним запросом описать ровно те поля, которые ему нужны, включая связанные сущности (товар с ценами, остатками и разделами). Это снижает объём трафика и число обращений, что особенно заметно на мобильных сетях.

Можно ли построить GraphQL-слой, не переписывая сайт на Битриксе?

Да. GraphQL-слой — это отдельная точка входа (endpoint), которая внутри обращается к данным через штатное API D7: инфоблоки, торговый каталог, заказы, пользователи. Витрина на Битриксе продолжает работать как раньше, а GraphQL добавляется поверх как API для внешних клиентов — SPA, мобильного приложения или витрины в headless-режиме. Ядро и бизнес-логику переписывать не нужно.

Как защитить GraphQL от тяжёлых и вредоносных запросов?

Основные меры: ограничение глубины вложенности запроса, расчёт «стоимости» (complexity) с лимитом на запрос, тайм-аут и пагинация вместо выборки «всего сразу». Для публичных API дополнительно применяют персистентные запросы — сервер принимает только заранее одобренный список операций по их хэшу. Плюс обычная авторизация на уровне полей и резолверов, чтобы клиент не получил данные, к которым у него нет доступа.

Что такое проблема N+1 в GraphQL и как её решать?

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

Как кэшировать ответы GraphQL, если запросы разные?

Кэшировать целиком тело POST-запроса неэффективно, потому что запросы отличаются набором полей. Практичнее кэшировать на уровне резолверов и источников данных — например, товар по ID через тегированный кэш Битрикса, который сбрасывается при изменении элемента инфоблока. Для публичных повторяющихся выборок помогают персистентные запросы: у них стабильный ключ, поэтому их можно отдавать через CDN или кэш на GET.

Подходит ли GraphQL для мобильного приложения магазина?

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

Поделиться:

Нужен GraphQL или REST-слой над вашим Битриксом?

Спроектируем схему, напишем резолверы на D7, закроем N+1, настроим кэш и защиту API под фронтенд и мобильное приложение. Оценим объём по вашему проекту.

Услуга «Разработка»

Редакция B2Bsite

Редакция студии B2Bsite. С 2014 года команда разрабатывает и поддерживает интернет-магазины и корпоративные сайты на 1С-Битрикс, проектирует API и интеграции, строит headless-витрины и обмен с 1С для среднего и крупного бизнеса.

← Все статьи блога