Антон Ларичев

Введение
Когда команда начинает проектировать новый бэкенд, рано или поздно встаёт вопрос: строить API на REST или на GraphQL. Оба подхода решают одну и ту же задачу — дать клиенту доступ к данным сервера, — но делают это принципиально по-разному. Выбор влияет на архитектуру, производительность, скорость разработки и даже на структуру команды. В этой статье разберём, как устроены оба подхода, сравним их по ключевым критериям и сформулируем понятные ориентиры для выбора.
Что такое REST
REST (Representational State Transfer) — архитектурный стиль, где данные представлены в виде ресурсов, доступных по URL. Каждый ресурс поддерживает стандартный набор HTTP-методов.
GET /users/42
GET /users/42/posts
POST /posts
PATCH /posts/17
DELETE /posts/17
Сервер возвращает фиксированную структуру данных для каждого эндпоинта. Если клиенту нужны данные из нескольких ресурсов, приходится делать несколько запросов подряд.
// получаем пользователя, а затем его посты отдельным запросом
const user = await fetch('/users/42').then(r => r.json());
const posts = await fetch(`/users/${user.id}/posts`).then(r => r.json());
Что такое GraphQL
GraphQL — язык запросов и рантайм, где клиент сам описывает, какие поля и связи ему нужны, а сервер отдаёт ответ ровно такой формы. Вместо множества эндпоинтов используется один вход (обычно /graphql).
query {
user(id: 42) {
name
email
posts {
title
createdAt
}
}
}
За один запрос клиент получает и пользователя, и его посты — без лишних round-trip'ов.
// один запрос вместо двух отдельных
const { data } = await fetch('/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: USER_WITH_POSTS, variables: { id: 42 } })
}).then(r => r.json());
Сравнение по критериям
Over-fetching и under-fetching
REST-эндпоинт часто отдаёт больше или меньше данных, чем нужно конкретному экрану. GraphQL решает это в корне: клиент запрашивает только нужные поля, что особенно важно для мобильных клиентов с ограниченным трафиком.
Количество запросов
REST для связанных сущностей требует нескольких запросов (или создания специальных агрегирующих эндпоинтов). GraphQL агрегирует данные на уровне резолверов, отдавая всё за один HTTP-запрос.
Кэширование
REST отлично работает со стандартным HTTP-кэшированием: ETag, Cache-Control, CDN понимают URL-структуру из коробки. GraphQL обычно использует один POST-эндпоинт, поэтому HTTP-кэширование почти не работает — приходится кэшировать на уровне клиента (Apollo Client, Relay) или добавлять persisted queries.
Версионирование
REST версионируют через префиксы вроде /v1/, /v2/, что со временем приводит к дублированию кода. GraphQL эволюционирует без версий: новые поля добавляются, старые помечаются @deprecated, а схема остаётся единой.
type User {
name: String!
# старое поле, оставлено для обратной совместимости
fullName: String @deprecated(reason: "Используйте name")
}
Производительность бэкенда
В REST легко оптимизировать конкретный эндпоинт под конкретный запрос к базе данных. В GraphQL произвольная вложенность полей может привести к проблеме N+1 запросов, если не использовать батчинг (например, DataLoader).
// без батчинга — N+1 запросов к базе на каждый пост
const loader = new DataLoader(async (ids) => {
const users = await db.users.findByIds(ids);
return ids.map(id => users.find(u => u.id === id));
});
Порог входа и инструментарий
REST проще объяснить новому разработчику и проще отладить через curl или Postman. GraphQL требует понимания схемы, резолверов и типовой системы, зато даёт самодокументируемый API через интроспекцию и инструменты вроде GraphiQL.
Когда выбрать REST
REST хорошо подходит, если API простой и ресурсно-ориентированный, важна поддержка HTTP-кэширования и CDN, команда небольшая и не хочет тратить время на настройку GraphQL-слоя, а клиенты предсказуемо потребляют одни и те же данные.
Когда выбрать GraphQL
GraphQL оправдан, если у продукта несколько разных клиентов (веб, iOS, Android) с разными требованиями к данным, экраны часто меняются и состав нужных полей нестабилен, важно снизить количество запросов на мобильных сетях, а команда готова инвестировать в схему, резолверы и защиту от сложных запросов.
Частые ошибки
Первая ошибка — выбирать GraphQL «потому что модно», без реальной потребности в гибких запросах: это добавляет сложность без выгоды.
Вторая — игнорировать проблему N+1 в резолверах GraphQL, что приводит к деградации производительности под нагрузкой.
Третья — пытаться сделать REST-эндпоинты «универсальными» через десятки query-параметров вместо того, чтобы признать: клиенту нужна гибкость, и это сигнал в пользу GraphQL.
Четвёртая — не ограничивать глубину и сложность GraphQL-запросов, что открывает возможность для DoS через специально сконструированный тяжёлый запрос.
// ограничение глубины запроса защищает сервер от чрезмерно вложенных query
const depthLimit = require('graphql-depth-limit');
const server = new ApolloServer({
schema,
validationRules: [depthLimit(5)]
});
Заключение
GraphQL и REST — не взаимоисключающие технологии, а инструменты под разные задачи. REST остаётся простым и предсказуемым выбором для ресурсно-ориентированных API с акцентом на кэширование. GraphQL раскрывается там, где данные потребляют разные клиенты с разными потребностями и важна гибкость запросов. Правильное решение зависит не от трендов, а от реальных характеристик проекта: количества клиентов, требований к кэшированию, сложности данных и готовности команды поддерживать выбранный подход.






Комментарии
0