GraphQL vs REST: over-fetching и under-fetching
Главная боль REST — клиент получает либо слишком много данных (over-fetching), либо слишком мало и вынужден делать кучу запросов (under-fetching). GraphQL устраняет обе.
«REST отдаёт то, что придумал бэкенд. GraphQL отдаёт то, что попросил фронтенд». В этом сдвиге власти — половина всей разницы.
Представь экран профиля в мобильном приложении: нужно показать имя пользователя и заголовки его последних постов. На REST это часто выглядит так: сначала идёшь на /users/42 и получаешь весь объект пользователя (имя, email, дата рождения, адрес, настройки — всё, хотя нужно только имя). Это over-fetching: приехало много лишнего, особенно больно на мобильном интернете. Затем выясняется, что постов в ответе нет, и ты идёшь на /users/42/posts — это уже второй запрос. Если на экране ещё и аватар автора каждого поста, появляется третий, четвёртый круг. Это under-fetching: один эндпоинт не дал всего нужного, и клиент добирает данные дополнительными запросами.
GraphQL решает обе проблемы одним приёмом: клиент описывает точную форму данных в одном запросе, а сервер собирает связанное дерево за один заход. Никаких лишних полей и никаких лишних кругов по сети.
Наглядное сравнение
REST (over-fetch + under-fetch) GraphQL (ровно нужное)
----------------------------------- ----------------------------
GET /users/42 POST /graphql
-> { name, email, bday, query {
address, settings, ... } user(id:"42") {
(90% полей не нужны!) name
GET /users/42/posts posts(last:2){ title }
-> [ {...}, {...} ] }
(ещё один круг по сети) }
GET /posts/1/author ... -> один ответ, ровно поля
(N+1 кругов)
По разным замерам GraphQL сокращает размер ответа на 30–50% именно за счёт отсутствия over-fetching — для мобильных и edge-сценариев это заметно.
Как работает под капотом
В REST маршрутизация идёт по URL: путь + HTTP-метод однозначно выбирают обработчик, который возвращает заранее заданную структуру. В GraphQL URL всегда один (/graphql), а «что вернуть» определяет тело запроса. Сервер разбирает запрос, сверяет со схемой и вызывает резолверы только для запрошенных полей. Поэтому два разных клиента, бьющие в один и тот же эндпоинт, могут получать совершенно разные по форме ответы.
Смоделируем оба подхода на чистом JS. «REST-эндпоинт» всегда отдаёт весь объект, «GraphQL» — только запрошенное:
const db = {
name: "Аня", email: "[email protected]", bday: "2006-05-01",
address: "Казань", settings: { theme: "dark" }
};
// REST: всегда весь объект (over-fetching)
function restUser() { return db; }
// GraphQL: только выбранные поля
function gqlUser(fields) {
return fields.reduce((acc, f) => (acc[f] = db[f], acc), {});
}
console.log("REST :", restUser());
console.log("GraphQL:", gqlUser(["name"]));
Попробуй сам ▶ — сравни объём двух ответов. REST тащит всё, GraphQL — один ключ.
Честное сравнение: где что лучше
| Критерий | REST | GraphQL |
|---|---|---|
| Эндпоинты | Много (по ресурсам) | Один |
| Выбор полей | Фиксирован сервером | Задаёт клиент |
| Связанные данные | Часто N+1 кругов | За один запрос |
| HTTP-кэш | Из коробки (GET+URL) | Сложнее (POST, один URL) |
| Кривая входа | Низкая | Выше (схема, резолверы) |
Частые ошибки
- «REST устарел, всё переписываем на GraphQL». Нет. По данным на 2024 год около 83% публичных API всё ещё на REST. Для простых, хорошо кэшируемых, публичных API REST часто удобнее.
- Забыть про HTTP-кэширование. REST бесплатно кэшируется на уровне CDN по URL. В GraphQL запросы обычно POST в один URL — приходится думать о кэше на клиенте и о persisted queries.
- Считать, что GraphQL автоматически быстрее. Меньше трафика — да, но без DataLoader легко получить ту же N+1, только внутри сервера.
Best practices
- Выбирай GraphQL, когда у тебя много разных клиентов (web, mobile, партнёры) с разными потребностями в данных и много связанных сущностей.
- Оставляй REST для простых CRUD-сервисов, файловых загрузок и публичных API, где важно агрессивное HTTP-кэширование.
- Их можно совмещать: GraphQL-шлюз поверх существующих REST-сервисов — частый и рабочий паттерн миграции.
Итоги
REST страдает от over-fetching (лишние поля) и under-fetching (лишние запросы), потому что форму ответа диктует сервер. GraphQL отдаёт власть клиенту: один эндпоинт, точная выборка полей, связанные данные за один запрос. Зато REST проще и лучше дружит с HTTP-кэшем. Выбор зависит от задачи, а не от моды.