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

Введение
Выбор между GraphQL и REST — один из ключевых архитектурных вопросов при проектировании современного API. REST доминировал последнее десятилетие благодаря простоте и стандартизации, но GraphQL предложил гибкий подход к работе с данными, где клиент сам решает, что ему нужно. В этой статье разберём сильные и слабые стороны обоих подходов, покажем примеры кода и сформулируем стратегию миграции с REST на GraphQL.
Ключевые различия
REST построен вокруг ресурсов и HTTP-методов: каждый endpoint возвращает фиксированную структуру данных. GraphQL — это единая точка входа, где клиент описывает запрос на специальном языке схем.
Пример REST-запроса для получения пользователя и его постов:
GET /api/users/42
GET /api/users/42/posts
Клиент делает два отдельных запроса и получает все поля, даже если нужны только имя и заголовки постов. В GraphQL тот же сценарий выглядит компактнее:
query {
user(id: 42) {
name
posts {
title
}
}
}
Один запрос — точные данные, без избыточности и без серии round-trip к серверу.
Когда выбирать REST
REST остаётся отличным выбором, если у вас простая ресурсная модель, публичный API с кэшированием через CDN или команда, которой важна предсказуемость и низкий порог входа. HTTP-кэширование работает из коробки: достаточно правильно настроить заголовки.
// Простой REST-обработчик на Express
import express from 'express';
const app = express();
app.get('/api/users/:id', async (req, res) => {
const user = await db.users.findById(req.params.id);
// Кэшируем ответ на 60 секунд через стандартный HTTP-механизм
res.set('Cache-Control', 'public, max-age=60');
res.json(user);
});
REST также проще отлаживать: curl, Postman и логи nginx показывают всё, что нужно. Для микросервисов с чёткими границами это часто оптимальный вариант.
Когда выбирать GraphQL
GraphQL раскрывается на проектах с богатыми клиентами: мобильные приложения, дашборды, сложные SPA. Если у вас десятки экранов с разными требованиями к данным, GraphQL избавит от endpoint-зоопарка вида /users/me/short, /users/me/full, /users/me/with-stats.
Пример резолвера на Apollo Server:
import { ApolloServer } from '@apollo/server';
const typeDefs = `
type User {
id: ID!
name: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
}
type Query {
user(id: ID!): User
}
`;
const resolvers = {
Query: {
// Резолвер запрашивает только верхний уровень — посты подгрузятся отдельно
user: (_, { id }) => db.users.findById(id),
},
User: {
posts: (parent) => db.posts.findByUserId(parent.id),
},
};
Клиенту больше не нужно знать о структуре бэкенда — он описывает форму ответа и получает ровно её.
Стратегия перехода с REST на GraphQL
Полная миграция за один релиз почти всегда плохая идея. Практичный путь — постепенное внедрение GraphQL-слоя поверх существующего REST.
Шаг 1. Поднимаем GraphQL как фасад над REST-сервисами:
const resolvers = {
Query: {
user: async (_, { id }) => {
// Внутри резолвера ходим в старый REST-endpoint
const response = await fetch(`https://api.internal/users/${id}`);
return response.json();
},
},
};
Шаг 2. Переводим один экран клиента на GraphQL и измеряем эффект — latency, количество запросов, объём передаваемых данных.
Шаг 3. Постепенно переносим бизнес-логику из REST-контроллеров в GraphQL-резолверы, оставляя REST только для внешних потребителей или legacy-клиентов.
Шаг 4. Когда покрытие достигает ~80%, переоцениваем: возможно, REST уже не нужен, либо он становится узким техническим слоем для интеграций.
Частые ошибки
Первая ошибка — переносить REST-мышление в GraphQL: создавать резолверы вида getUserById, getUserByEmail, getUserWithPosts. GraphQL предполагает декларативную схему, а не набор процедур.
Вторая — игнорировать проблему N+1. Наивный резолвер User.posts сделает отдельный запрос к БД на каждого пользователя. Решение — DataLoader:
import DataLoader from 'dataloader';
const postsLoader = new DataLoader(async (userIds: readonly string[]) => {
// Один запрос за всеми постами вместо N отдельных
const posts = await db.posts.findByUserIds([...userIds]);
return userIds.map((id) => posts.filter((p) => p.userId === id));
});
Третья — отсутствие лимитов на глубину и сложность запроса. Без них клиент может запросить дерево из 10 вложенных уровней и положить сервер. Используйте graphql-depth-limit или query complexity analysis.
Четвёртая — слепо доверять кэшированию. В отличие от REST, GraphQL обычно ходит через POST, и стандартное HTTP-кэширование не работает. Понадобятся persisted queries или Automatic Persisted Queries (APQ).
Заключение
GraphQL и REST — не конкуренты, а инструменты для разных задач. REST остаётся надёжным выбором для простых ресурсных API и публичных сервисов с агрессивным кэшированием. GraphQL побеждает там, где клиенты разнообразны, требования к данным меняются часто, а количество endpoint-ов выходит из-под контроля. Переходить стоит постепенно: начните с фасада над REST, измерьте эффект на одном экране и масштабируйте подход, опираясь на реальные метрики, а не на хайп.






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