В технологиях наблюдается своеобразный цикл: появляется что-то новое, и вдруг все, кто им не пользуется, чувствуют себя атакованными. GraphQL появился около десяти лет назад, и с тех пор мы наблюдаем этот ажиотаж. «REST мёртв», говорили они. «GraphQL — будущее», провозглашали они. Тем временем REST API незаметно обеспечивали работу 90% интернета и занимались своими делами, не привлекая внимания.
Не поймите меня неправильно — я не говорю, что GraphQL плох. Это действительно полезный инструмент. Но где-то между шумихой и реальностью лежит более скучная, практическая истина: REST всё ещё абсолютно подходит для большинства приложений, и одержимость отрасли GraphQL создала реальные проблемы для реальных команд.
Позвольте мне объяснить почему, опираясь на реальные компромиссы, а не только на ностальгию.
Преимущество REST, о котором никто не говорит: простота
Когда вы создаёте REST API, вы не изобретаете новый язык запросов. Вы не просите свою команду фронтенда изучать проектирование схемы GraphQL. Вы не решаете новые задачи, связанные с кэшированием. Вы делаете что-то, что было испытано десятилетиями.
На практике это означает следующее:
GET /api/users/123
GET /api/users/123/orders
GET /api/posts/456
Ваш фронтенд-разработчик не должен знать о резолверах, анализе сложности запросов или ограничении глубины. Он знает, что:
GETозначает получение данных;POSTозначает создание;PUTозначает обновление;DELETEозначает удаление.
Вот и всё. Каждый в команде, от стажёра до архитектора, сразу это понимает. Нет кривой обучения. Нет неожиданных накладных расходов на резолверы. Есть только базовая семантика HTTP, которую буквально каждый разработчик использует с 2010 года.
Сравните это с GraphQL, где ваш фронтенд-разработчик должен понимать:
- Введение в схему;
- Структуру и синтаксис запросов;
- Композицию фрагментов;
- Использование псевдонимов;
- Порядок выполнения резолверов.
Для простого CRUD-приложения это перебор. Это как использовать паяльную лампу, чтобы зажечь свечу.
Слон в комнате: кэширование
Вот что действительно удивляет разработчиков, которые жили только в мире GraphQL: кэширование REST почти автоматическое.
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 18 Feb 2026 12:00:00 GMT
С REST браузеры кэшируют ваши ответы. CDN кэшируют ваши ответы. Прокси кэшируют ваши ответы. Varnish, Nginx, CloudFlare — все они знают, как обрабатывать кэширование HTTP без написания вами ни одной строки пользовательского кода.
GraphQL? Он отправляет POST-запросы к одному эндпоинту. Нет URL для кэширования. Нет семантики HTTP для использования. Теперь вы отвечаете за:
- Создание пользовательских уровней кэширования.
- Реализация анализа сложности запросов (чтобы клиенты не могли DOS вас сложными запросами).
- Сохранение запросов (чтобы заблокировать то, что клиенты фактически могут запросить).
- Управление недействительностью кэша на уровне резолвера.
Это инфраструктурные затраты. Реальные инфраструктурные затраты. Реальные расходы. Реальная операционная нагрузка.
Стартапу с 20 сотрудниками такая сложность не нужна. Компании с 200 сотрудниками, которая не одержима инфраструктурой, такая сложность не нужна. Даже компании с 2000 сотрудниками, возможно, будет лучше с простым REST API и CDN.
Проблема N+1 просто переместилась, а не исчезла
Евангелисты GraphQL любят указывать на проблему N+1 в REST. Хотите пользователя и его последние 5 заказов? Это 6 запросов:
// REST: проблема N+1 (на стороне клиента)
const user = await fetch('/api/users/123');
const orders = await fetch('/api/users/123/orders?limit=5');
// 2 запроса для получения связанных данных
GraphQL утверждает, что решает эту проблему с помощью одного запроса:
# GraphQL: один запрос
query {
user(id: 123) {
name
email
orders(limit: 5) {
id
product
total
}
}
}
Великолепно! Кроме того… GraphQL просто переместил проблему N+1 на серверную сторону. Теперь ваши резолверы выполняют N запросов. Если плохо написанный резолвер открыт для вашей базы данных, один запрос GraphQL может вызвать сканирование таблицы. Клиент конструирует сложный вложенный запрос, и внезапно вы получаете тысячи запросов к базе данных, отправляемых на ваш сервер.
С REST такой сценарий буквально невозможен. Вы контролируете эндпоинт. Вы контролируете, сколько данных раскрывается. Клиент не может случайно DOS вашу базу данных с помощью некорректного запроса, потому что эндпоинт ещё не существует.
Что безопаснее? REST.
Давайте поговорим о том, что на самом деле решил GraphQL
Справедливости ради, GraphQL действительно решает реальные проблемы — но только специфические:
Проблема 1: несколько клиентов с разными потребностями в данных Если у вас есть веб-приложение, мобильное приложение и сторонний API, они, вероятно, хотят разные поля. REST? Вы создаёте несколько эндпоинтов или возвращаете раздутые ответы. GraphQL? Клиент запрашивает то, что ему нужно.
Проблема 2: глубоко вложенные структуры данных Современные пользовательские интерфейсы сложны. Им нужны связанные данные. GraphQL элегантно справляется с этим в одном запросе.
Проблема 3: версионирование API
Версионирование REST раздражает. /v1/users, /v2/users, /v3/users. GraphQL добавляет новые поля, не нарушая существующих запросов.
Это реальные проблемы с реальными решениями. Если они у вас есть, GraphQL имеет смысл.
Но вот в чём дело: у большинства приложений нет этих проблем.
Дерево решений, которому никто не следует
Практические рекомендации по проектированию реальных API:
├─ Это простое CRUD-приложение?
│ └─ REST (серьёзно, просто REST)
│
├─ У вас есть 3+ значительно отличающихся клиентов?
│ └─ GraphQL (теперь у вас есть реальный случай использования)
│
├─ Данные глубоко вложены и сложны?
│ └─ GraphQL (здесь он действительно полезен)
│
├─ Вам нужны двусторонние обновления в реальном времени?
│ └─ gRPC или подписки GraphQL (GraphQL справляется с трудом, gRPC лучше)
│
├─ Кэширование — ваша основная забота?
│ └─ REST (даже не думайте о GraphQL)
│
└─ Вы создаёте это в ближайшие 48 часов?
└─ REST (GraphQL требует времени на старте)
Обратите внимание, к чему относятся большинство стартапов и небольших команд? К категории REST. И всё равно они создают GraphQL API, потому что думают, что «должны» это делать.
Практический пример: приложение для задач, которому GraphQL не нужен
Позвольте мне показать вам, как выглядит реальное приложение. Не гипотетическое. Не учебное пособие. Реальный производственный API, который обслуживает несколько фронтенд-приложений:
// REST API — чистый, простой, прекрасно кэшируется
app.get('/api/todos', async (req, res) => {
const page = req.query.page || 1;
const limit = req.query.limit || 20;
const todos = await db.todos
.find({ userId: req.user.id })
.skip((page - 1) * limit)
.limit(limit)
.exec();
res.set('Cache-Control', 'max-age=300');
res.json(todos);
});
app.get('/api/todos/:id', async (req, res) => {
const todo = await db.todos.findById(req.params.id);
res.set('Cache-Control', 'max-age=600');
res.json(todo);
});
app.post('/api/todos', async (req, res) => {
const todo = await db.todos.create({
title: req.body.title,
userId: req.user.id
});
res.status(201).json(todo);
});
app.put('/api/todos/:id', async (req, res) => {
const todo = await db.todos.findByIdAndUpdate(
req.params.id,
req.body,
{ new: true }
);
res.json(todo);
});
app.delete('/api/todos/:id', async (req, res) => {
await db.todos.findByIdAndDelete(req.params.id
