От REST к GraphQL: Юрий Самсонов о том, что реально меняется, когда отказываешься от фиксированных эндпоинтов

public

Автор: Юрий Самсонов · JUG.ru


Юрий Самсонов — разработчик из Яндекса. Доклад на JPoint 2023 — не обзор GraphQL и не агитация за его повсеместное использование, а конкретный опыт внедрения: что сломалось, что не ожидали, как вышли.

REST-эндпоинты имеют предсказуемую проблему роста: по мере того как клиентов становится больше, у каждого появляются свои требования к форме данных. Один клиент хочет вложенный объект, другой — плоский список. Появляются параметры вроде ?include=user,tags,comments, эндпоинты обрастают опциями, и в какой-то момент backend-разработчик обнаруживает, что его работа — это в основном добавление новых вариаций существующих эндпоинтов под нужды клиентов. GraphQL переворачивает эту ответственность: клиент сам описывает, какие данные ему нужны, и получает ровно их — без over-fetching и under-fetching. Самсонов честно рассказывает о том, что эта свобода стоит: N+1 проблемы, сложность с кешированием, отладка запросов, которые клиент сформировал сам.

Кому смотреть: backend-разработчикам, у которых REST API начинает «расти вширь» под давлением разных клиентов, и тем, кто думает о GraphQL, но хочет услышать не маркетинг, а реальный опыт внедрения.

Из этого можно взять в работу: посчитайте, сколько полей в ответе вашего самого нагруженного эндпоинта реально используется мобильным клиентом. Если меньше трети — у вас есть over-fetching, и это стоит трафика, памяти и CPU прямо сейчас.


Почему был выбран GraphQL: в Яндексе росло количество клиентских команд со своими требованиями к API. Поддерживать несколько версий REST-эндпоинтов или один монолитный с множеством параметров стало дороже, чем перейти к гибкой схеме запросов. Это не в меньшей степени организационное решение, чем техническое.

N+1 проблема — главная техническая боль GraphQL: когда запрос требует список объектов с вложенными сущностями, наивная реализация делает N дополнительных запросов к базе для каждого из N элементов. Решение — DataLoader (батчинг запросов), но его нужно реализовывать явно для каждого типа связи. Самсонов разбирает, как они столкнулись с этим и как решили.

Кеширование: в REST кеш работает на уровне URL — один URL, один кеш. В GraphQL все запросы идут на один эндпоинт (/graphql), и стандартный HTTP-кеш не работает. Нужен application-level кеш с пониманием семантики запросов — это принципиально другая задача.

Что осталось нерешённым: rate limiting в GraphQL сложнее, чем в REST — нельзя просто ограничить по количеству запросов, потому что сложность одного запроса может варьироваться в разы. Нужна оценка «веса» запроса по глубине и ширине. Самсонов называет это областью, где они продолжают экспериментировать.