Чем GraphQL отличается от REST и когда его стоит брать

Выбираем под свои задачи

Чем GraphQL отличается от REST и когда его стоит брать

Когда команда проектирует архитектуру для нового продукта, первый спор почти всегда сводится к вопросу: REST или GraphQL. Разработчикам предстоит выбрать — остаться на привычных эндпоинтах или перевести клиент-серверное взаимодействие на схему с единственной точкой входа. 

Расстановка сил такая: REST по-прежнему самый распространённый стиль API: по данным Postman его используют 86% разработчиков (2023), а в 2025 году — уже 93%. GraphQL тоже активно используется: по данным Hygraph, его применяют 61,7% разработчиков, а Postman фиксирует около 33% команд, которые работают с GraphQL.

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

ВАМ ПРИШЛО ПРИГЛАШЕНИЕ 💌
Приходите к нам в соцсети поделиться своим мнением и почитать, что пишут другие. А ещё там выходит дополнительный контент, которого нет на сайте — шпаргалки, опросы и разная дурка. В общем, вот тележка, вот ВК — велком!

Как устроен REST

Архитектура REST API строится на ресурсной модели. Каждая сущность (товар, отзыв, склад) — это отдельный ресурс, который живет по своему уникальному адресу (URL). Действие, которое мы хотим совершить над ресурсом, задается HTTP-методами (GET для чтения, POST для создания), а результат операции описывается статус-кодом ответа.

Чтобы собрать наш экран карточки товара (допустим, с ID 42), бэкенд предоставит следующий набор эндпоинтов:

  • GET /api/v1/products/42 — основные данные товара.
  • GET /api/v1/products/42/stock — остатки.
  • GET /api/v1/products/42/reviews — список отзывов.
  • GET /api/v1/products/42/recommendations — рекомендации.

Клиент делает первый запрос к ресурсу товара:

GET /api/v1/products/42 HTTP/1.1
Host: api.shop.com
# Ответ сервера (200 OK):
{
  "id": "42",
  "name": "Механическая клавиатура",
  "price": 12000,
  "weight": 800,
  "supplier_id": "sup_99"
}

Ключевая идея REST в том, что  каждый кусок данных живёт в своём отдельном запросе. Чтобы собрать одну страницу, клиенту приходится ходить по нескольким эндпоинтам и самостоятельно склеивать результат.

Как устроен GraphQL

Подход GraphQL кардинально отличается: он строится вокруг строгой типизации. Разработчики заранее описывают граф данных (схему), в которой указаны все типы и связи между ними. Сервер предоставляет клиентам ровно один адрес (например, POST /graphql). Клиент сам пишет запрос, перечисляя только те поля, которые ему нужны, а специальная функция на сервере (резолвер) собирает эти данные. Сервер возвращает ответ, форма которого в точности повторяет форму запроса.

Сначала на сервере описывается схема на языке SDL (Schema Definition Language):

type Product {
  id: ID!
  name: String!
  price: Int!
  stock: Int
  reviews: [Review!]
  recommendations: [Product!]
}

type Review {
  id: ID!
  text: String!
  authorName: String!
}

type Query {
  product(id: ID!): Product
}

Затем клиент отправляет единый запрос, чтобы получить и товар, и отзывы к нему:

# Запрос клиента:
query GetProductScreen {
  product(id: "42") {
    name
    price
    reviews {
      text
      authorName
    }
  }
}

# Ответ сервера:
{
  "data": {
    "product": {
      "name": "Механическая клавиатура",
      "price": 12000,
      "reviews": [
        { "text": "Отличный ход клавиш", "authorName": "Иван" }
      ]
    }
  }
}

Главное отличие на одном экране

Чтобы понять, чем отличается GraphQL от REST, посмотрим на процесс сборки нашего экрана карточки товара.

ШагRESTGraphQL
1Запрос к /products/42 (ждем ответ).Отправляем один большой запрос к /graphql.
2Запросы к /stock, /reviews, /recommendations (можно параллельно).Сервер собирает все данные по графу связей.
3Клиент склеивает 4 JSON-ответа у себя.Сервер отдает один готовый JSON под нужды экрана.

При таком сценарии REST неизбежно страдает от двух симптомов:

  1. Over-fetching (избыточная выборка): эндпоинт /products/42 возвращает вес товара (weight) и ID поставщика (supplier_id), которые нам на экране не нужны, но мы вынуждены тратить на них трафик.
  2. Under-fetching (недостаточная выборка): первого запроса не хватает для отрисовки интерфейса, и клиент вынужден идти за добавкой (отзывами и остатками), тратя время на установку новых сетевых соединений.

Разница хорошо видна в цифрах: в 2026 году при переходе на GraphQL компании фиксируют экономию 20–30% мобильного трафика. Но за гибкость приходится платить вычислительной мощностью: медианная задержка (latency) формирования ответа на бэкенде у REST составляет около 12 мс, тогда как у GraphQL — около 15 мс из-за необходимости парсить текст запроса.

Большая скидка — 16% на все курсы Практикума

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

До 30 сентября на все курсы действует скидка 16%, она применится автоматически при оплате. Потом цены станут выше, поэтому не откладывайте!

Если не хватает уверенности в асинхронном Python — берите «Мидл Python-разработчик»; сервис уже вырос из одного репозитория и пора резать его на части, а не переписывать с нуля — «Микросервисная архитектура»; а если зона ответственности давно включает не только код, но и мониторинг с разбором инцидентов — «SRE — обеспечение надёжности систем».

Кэширование

Кэширование API — это территория, где REST выигрывает по умолчанию.

Поскольку в REST каждый ресурс имеет свой уникальный URL (например, GET /products/42), то браузерный кэш, прокси-серверы и сети доставки контента (CDN) могут кэшировать ответы из коробки, опираясь на стандартные HTTP-заголовки (Cache-Control, ETag)..

В GraphQL все запросы летят на один адрес (POST /graphql), а тело запроса каждый раз разное. HTTP-кэш не понимает, что внутри, и пропускает запросы насквозь к бэкенду. Для GraphQL приходится изобретать обходные пути.

Уровень кэшаРаботает в RESTРаботает в GraphQL
Браузер / HTTP-кэшДа, нативно (GET-запросы)Нет (все запросы POST на один URL)
CDN (Cloudflare и др.)Да, из коробкиТолько через Persisted Queries (сохраненные запросы с хэшами)
Кэш в клиенте (ОЗУ)Требует ручной логикиИз коробки в Apollo/Relay (кэширование по ID полей)
Серверный кэшНа уровне эндпоинта/RedisТребует настройки слоя кэширования внутри каждого резолвера

Ошибки и коды ответов

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

В REST код ответа несет смысловую нагрузку. Если товар не найден, сервер вернет:

HTTP/1.1 404 Not Found
{ "error": "Product not found" }

В GraphQL HTTP-статус почти всегда равен 200 OK, даже если внутри произошла катастрофа. В ответе появляется специальный массив “errors”, при этом частично заполненные данные могут соседствовать с упавшими полями: например, товар нашелся, а сервис отзывов недоступен.

{
  "data": {
    "product": { "name": "Клавиатура", "reviews": null }
  },
  "errors": [
    { "message": "Failed to fetch reviews", "path": ["product", "reviews"] }
  ]
}

Как это влияет на работу:

  1. Системы мониторинга не могут просто смотреть на 5xx ошибки HTTP — они обязаны парсить тела JSON-ответов.
  2. Сетевые ретраи (повторные попытки) на клиенте нужно настраивать кастомно, так как код всегда будет 200.
  3. Логирование усложняется: нужно вытаскивать path из массива ошибок, чтобы понять, какой резолвер упал.
  4. В схеме GraphQL рекомендуется создавать специальные типы-юнионы для бизнес-ошибок (например, ProductResult = Product | NotFoundError), чтобы обрабатывать их как часть данных, а не системных сбоев.

Нагрузка на сервер и проблема N+1

Этот раздел важен потому, что в GraphQL производительность часто зависит не от самого запроса, а от того, как написаны резолверы на сервере. Именно здесь чаще всего появляются скрытые проблемы с нагрузкой.

Разберём типичный сценарий. Допустим, мы запрашиваем список из 100 товаров, и для каждого товара нужны отзывы. В GraphQL это выглядит удобно на клиенте, но на сервере может привести к неочевидному числу обращений к базе.

Наивная реализация работает так: сначала выполняется один запрос за товарами, а затем для каждого товара отдельно запрашиваются его отзывы. В итоге вместо 2 запросов к базе получается 1 + 100 — это и есть классическая проблема N+1 запросов. То есть мы один раз получили список товаров, а потом «дёрнули базу» ещё N раз за связанными данными.

До оптимизации код резолвера выглядит так:

// Вызовется 100 раз!
Review: {
  author: async (review) => {
    return await db.query(`SELECT * FROM users WHERE id = ${review.author_id}`);
  }
}

Решение — использование утилиты DataLoader. Он собирает все запрошенные ID в рамках одного запроса и выполняет их одним обращением к базе:

// Вызовется 1 раз для всех авторов:
Review: {
  author: async (review, context) => {
    return await context.userLoader.load(review.author_id); // DataLoader батчит запросы
  }
}

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

Но даже при использовании DataLoader GraphQL не становится «бесплатным» по производительности. Сам движок добавляет дополнительную нагрузку — парсинг запроса, проверку схемы и сборку вложенного JSON могут давать ощутимый оверхед по сравнению с простыми REST-обработчиками.

Безопасность

Угрозы для безопасности API в разных архитектурах требуют разных методов защиты. 

Угрозы GraphQL и защита:

  • Тяжелые вложенные запросы: злоумышленник может написать запрос, где товар ссылается на отзывы, отзывы на авторов, авторы на их товары, зациклив граф так, что база данных ляжет. Защита: включение ограничения максимальной глубины запроса (Query Depth Limit) и подсчет «стоимости» запроса (Query Cost Analysis).
  • Утечка структуры API: по умолчанию GraphQL-схема открыта для изучения (интроспекция). Защита: полное отключение интроспекции на продакшен-сервере.
  • Сложность авторизации: проверять права нужно не на входе в эндпоинт, а внутри каждого отдельного резолвера (проверка прав на уровне полей).

Угрозы REST и защита:

  • Перебор (brute-force) и DDoS: злоумышленник бьет по конкретным URL. Защита: классическое ограничение запросов (Rate Limiting) по эндпоинтам.
  • Утечка лишних полей: эндпоинт отдает массив DTO (Data Transfer Objects), в котором случайно затесались приватные поля (например, хэши паролей пользователей в отзывах). Защита: жесткое разграничение DTO-моделей.

Эволюция схемы и инструменты

Версионирование API и сопровождение документации в REST и GraphQL организованы по-разному.

Версионирование

В REST стандартный путь — изменение URL (/v1/products меняется на /v2/products). Приходится параллельно поддерживать два контракта. В GraphQL версионирования в классическом виде нет: новые поля просто добавляются в схему, а старые помечаются директивой @deprecated. Бэкенд мониторит статистику запросов и удаляет старое поле только тогда, когда клиенты перестают его запрашивать.

Документация

В мире REST чаще всего используют спецификация OpenAPI (Swagger), по которой генерируется визуальная страница документации. GraphQL самодокументируемый по своей природе. Благодаря встроенной песочнице GraphiQL, фронтендер может сразу изучать типы, читать комментарии к полям и тестировать запросы в одном окне.

Типобезопасность и кодогенерация

Оба подхода позволяют генерировать типизированных клиентов для фронтенда. В REST это делается из YAML-файла OpenAPI. В GraphQL плагины, например, GraphQL Code Generator, читают схему и сами генерируют TypeScript-типы и React-хуки для конкретных запросов клиента.

Тестирование

Для REST пишутся интеграционные и контрактные тесты. В GraphQL-мире в CI-пайплайн обязательно встраивают инструменты (вроде GraphQL Inspector), которые проверяют каждую новую схему на обратную совместимость, чтобы бэкендер случайно не удалил поле, которое ожидает мобильное приложение.

Что ещё есть кроме REST и GraphQL

Выбор не ограничен двумя инструментами. Рассмотрим другие альтернативы REST, которые закрывают свои специфичные ниши:

ПодходНишаЧем платят
gRPCВнутренняя межсервисная связь (backend-to-backend), потоковая передачаОтсутствие человекочитаемого формата (бинарный Protobuf), сложность отладки
tRPCМонорепозитории на TypeScript (фронт и бэк в одной кодовой базе)Жесткая привязка исключительно к TypeScript/JavaScript-экосистеме
Вебхуки / СобытияАсинхронный обмен данными (реакция на смену статуса заказа)Сложность отслеживания потерянных сообщений, гарантии доставки
Федерация схем (Apollo)Объединение множества микросервисов с GraphQL в один общий граф Высокая архитектурная сложность, наличие единой точки отказа (суперграф-роутера)

Матрица выбора

Чтобы решить, что выбрать для API, нужно оценить архитектурный контекст.

Сценарий (тип проекта)РекомендацияОбоснование
Публичный API для внешних разработчиков (B2B)RESTПонятный стандарт, легко интегрировать с любым языком без сторонних библиотек
Мобильное приложение на медленной сетиGraphQLНет проблемы under-fetching, все нужные данные доставляются за один сетевой запрос
SPA-приложение со сложными экранами (дашборды)GraphQLФронтенд сам решает, какие данные ему нужны для отрисовки конкретного виджета
Отдача статичного контента (медиа, статьи)RESTЭффективно кэшируется на уровне CDN из коробки
Внутренняя связь между микросервисамиgRPC / RESTДля server-to-server GraphQL избыточен (парсинг запросов тратит ресурсы CPU)
Монорепозиторий стартапа (всё на TS)tRPCИдеальная типизация без генерации схем и лишнего бойлерплейта
Продукт с десятком разнородных клиентов (Web, iOS, Android, TV)GraphQLБэкенду не нужно писать новые эндпоинты под специфику каждого нового клиентского приложения

Перед стартом разработки команда должна ответить на 4 вопроса. Кто конечный потребитель API? Насколько критичен размер передаваемого JSON? Нужен ли строгий HTTP-кэш? Готовы ли бэкендеры тратить время на написание DataLoader для каждого связанного поля?

Как совместить оба подхода

Если у вас уже есть работающий проект, полная миграция на GraphQL с переписыванием бэкенда — это огромный риск. Чаще всего команды используют гибридный подход.

Порядок безопасного переезда выглядит так:

  1. Бэкенд оставляет старые REST-эндпоинты нетронутыми.
  2. Поверх них поднимается отдельный Node.js или Go сервис — GraphQL шлюз.
  3. Шлюз принимает GraphQL-запросы от фронтенда, а под капотом сам делает обычные REST-запросы к старому бэкенду.
  4. Фронтенд переводит на новую схему только один сложный экран (например, корзину). Команда замеряет разницу в скорости и стабильности.
  5. Постепенно остальные экраны переносятся на новый формат.

[ Фронтенд ] ---> (GraphQL запрос) ---> [ GraphQL Шлюз ] ---> (REST запросы) ---> [ Микросервисы ]

Если GraphQL кажется слишком сложным для внедрения, архитекторы часто используют паттерн BFF (Backend for Frontend). Вместо одного универсального API разработчики создают отдельные бэкенд-сервисы под каждого клиента (один API для веба, отдельный урезанный API для iOS), которые склеивают данные нужным образом, оставаясь в рамках REST-парадигмы.

Чек-лист перед выкаткой GraphQL в прод

Если вы всё же решили, что вам нужен граф, пройдитесь по этим пунктам до релиза:

  • Пакетная загрузка (DataLoader) настроена для всех связанных сущностей (решена проблема N+1).
  • Ограничение максимальной глубины запроса (Query Depth) включено.
  • Ограничение стоимости запроса (Query Complexity Cost) рассчитано и активировано.
  • Интроспекция (возможность выкачать схему) отключена для production-окружения.
  • Сохраненные запросы (Persisted Queries) настроены для работы с CDN (если требуется кэширование).
  • Авторизация и проверка прав доступа настроены на уровне конкретных полей, а не только на уровне корневого контроллера.
  • Инструменты мониторинга парсят JSON-ответы и собирают метрики по конкретным GraphQL-операциям.
  • Конвенция по формату ошибок внутри массива errors зафиксирована с фронтенд-командой.
  • Проверка обратной совместимости схемы встроена в CI-пайплайн.

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

Умер ли REST?

Нет, REST продолжает оставаться доминирующим архитектурным стилем для 80% систем в мире. Он идеально подходит для микросервисного общения, интеграций между корпоративными системами и отдачи простого плоского контента, уступая место графовым решениям лишь в нагруженных клиентских интерфейсах.

Можно ли отдавать файлы через GraphQL?

По спецификации протокол работает только с текстовыми данными (JSON). Для загрузки и отдачи бинарных файлов разработчики используют либо сторонние спецификации (например, graphql-multipart-request-spec), либо выносят работу с медиа на отдельные классические REST-эндпоинты.

Нужен ли GraphQL небольшому проекту?

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

Как GraphQL работает с кэшем на CDN?

Для использования Cloudflare или других CDN применяется механизм Persisted Queries. Клиент не отправляет текст запроса, а шлет его заранее вычисленный хэш через обычный HTTP GET-запрос. Сервер и CDN распознают этот хэш и могут кэшировать ответ на уровне сети.

Что быстрее на медленной мобильной сети?

На нестабильных мобильных сетях (3G/Edge) графовый подход выигрывает безоговорочно. Установка каждого нового TCP/TLS соединения занимает сотни миллисекунд, поэтому забрать все нужные данные за один большой запрос всегда быстрее, чем ждать ответа от трех-четырех разных REST-эндпоинтов.

Заключение

Вопрос «REST или GraphQL» не имеет универсального ответа. Архитектура определяется не трендами, а числом, разнородностью ваших клиентов и структурой данных. Если у вас множество независимых экранов, которые требуют сложной агрегации данных, графовый подход сократит время разработки фронтенда и спасет мобильный трафик. Если вы пишите понятный B2B сервис или публичное API для партнеров — строгие эндпоинты будут лучшим выбором. Главный совет: не переписывайте всю систему просто ради моды. Поднимите промежуточный шлюз, переведите на него один реальный экран, сделайте замеры сети и CPU, и только после этого принимайте решение о масштабировании.

Советуем дополнительно почитать

Что такое API — самое базовое объяснение того, что вообще делает API, если раздел про REST и GraphQL кажется слишком специфичным для старта.

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

Создаём API на FastAPI — пошаговое построение собственного REST API с нуля, если после статьи хочется не только выбрать подход, но и сразу его попробовать.

Go-разработчик: кто это, чем он занимается и как им стать — про профессию и стек, который статья называет типичным выбором для gRPC и GraphQL-шлюзов.

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

Бонус для читателей

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

Вам может быть интересно
hard