GraphQL vs REST vs gRPC vs tRPC: как выбрать парадигму API
Когда команда стартует новый проект, один из ранних архитектурных вопросов — какой подход выбрать для API между сервисами и для общения фронтенда с бэкендом. Двадцать лет назад выбора не было: SOAP в enterprise, в новых проектах — REST. Сегодня на рынке как минимум четыре зрелых парадигмы: REST, GraphQL, gRPC, tRPC — каждая с собственной философией, типом задач, ограничениями.
Решение влияет на годы вперёд. API-парадигма определяет, как будет устроен фронтенд, как пишутся клиентские SDK, как тестируется интеграция, как версионируются изменения. Менять подход дороже, чем выбрать правильный с самого начала. Разбираем устройство каждой технологии, их сильные и слабые стороны, конкретные критерии выбора под разные типы проектов.
REST: классика, доминирующая в индустрии
REST (Representational State Transfer) — архитектурный стиль, описанный Роем Филдингом в его диссертации 2000 года. Основные принципы: ресурсы идентифицируются через URI, операции выполняются стандартными HTTP-методами (GET, POST, PUT, DELETE), серверы stateless, ответы могут кэшироваться. JSON стал де-факто стандартным форматом сериализации, заменив XML в 2010-х.
Главное преимущество REST — повсеместность. Любой инструмент работает с REST: браузеры, curl, Postman, любой HTTP-клиент в любом языке. Концепция ресурсов интуитивно понятна большинству разработчиков. Документация через OpenAPI/Swagger стандартизирована. Кэширование на уровне HTTP даётся бесплатно через стандартные заголовки.
| Аспект REST | Описание |
|---|---|
| Транспорт | HTTP/HTTPS, обычно через стандартные методы |
| Формат данных | Обычно JSON, реже XML или другие форматы |
| Схема | Опциональна, обычно через OpenAPI |
| Версионирование | В URL (/v1/, /v2/) или в заголовках |
| Типизация | Слабая, на уровне документации |
| Кэширование | Простое через HTTP-заголовки |
Слабые стороны REST становятся видимыми на сложных приложениях. Получение связанных данных требует нескольких запросов: загрузить пользователя, потом его заказы, потом товары в каждом заказе — три HTTP round trip. Над- или недо-выборка данных: endpoint возвращает фиксированный набор полей, клиенту приходится получать лишние данные или делать дополнительные запросы. Слабая типизация: без OpenAPI/JSON Schema контракт между клиентом и сервером держится только на доброй воле разработчиков.
GraphQL: один endpoint и точные запросы
GraphQL появился в Facebook в 2012 году, в open-source вышел в 2015. Главная идея — клиент сам описывает, какие данные ему нужны, запросом. Один endpoint, гибкий язык запросов, типизированная схема, иерархическая модель данных. В отличие от REST с его фиксированными endpoint’ами под каждый ресурс, GraphQL даёт клиенту полную свободу выбора полей и связанных сущностей.
Сильная сторона GraphQL — устранение проблем over-fetching и under-fetching. Клиент в одном запросе получает ровно те поля, которые ему нужны, плюс связанные сущности. Запрос вида «пользователь с его последними заказами с товарами в каждом» — один HTTP-запрос с одним response. Сильная типизация через схему: клиент знает, что вернётся, ещё до запроса.
Платой за гибкость становится сложность реализации. Сервер должен корректно обрабатывать произвольные запросы, защищаться от слишком тяжёлых (n+1 проблема при ленивой загрузке, deep nesting), правильно кэшировать, ограничивать complexity. Все эти задачи решены в зрелых GraphQL-фреймворках, но требуют понимания особенностей.
gRPC: бинарный протокол для микросервисов
gRPC — высокопроизводительный RPC-фреймворк от Google, использующий HTTP/2 как транспорт и Protocol Buffers (protobuf) как формат сериализации. Появился в 2015 году, изначально предназначался для общения между микросервисами в инфраструктуре Google. Сегодня — стандарт high-performance internal API.
Основная сила gRPC — производительность. Protobuf — бинарный формат, в разы компактнее JSON. HTTP/2 поддерживает мультиплексирование, streaming, server push. Контракт API задаётся в proto-файле, на основе которого автогенерируется код клиента и сервера для десятков языков. Type safety, низкая латентность, малый объём передаваемых данных — три ключевые характеристики.
Слабые стороны — ограниченная поддержка в браузерах (нужен grpc-web с прокси), сложность отладки бинарных запросов (без специальных инструментов невозможно посмотреть payload), необходимость использовать proto-файлы и системы их распространения. Для внешних публичных API gRPC применяется редко; его ниша — внутрисервисные коммуникации в крупных распределённых системах.
| Характеристика | gRPC |
|---|---|
| Транспорт | HTTP/2 |
| Формат данных | Protocol Buffers (бинарный) |
| Схема | Обязательная, через .proto файлы |
| Поддержка streaming | Полная (client, server, bidirectional) |
| Типизация | Строгая, на стороне клиента и сервера |
| Производительность | Высокая, бинарный протокол |
tRPC: end-to-end типизация для TypeScript-стеков
tRPC — относительно новая технология (запущена в 2021), быстро набравшая популярность в экосистеме TypeScript. Главная идея — тип-безопасный RPC между TypeScript-клиентом и TypeScript-сервером без необходимости в схемах или код-генерации. Контракт API выводится из типов TypeScript функций на сервере, и клиент автоматически знает эти типы.
Сильные стороны tRPC — мгновенная type safety без дополнительных шагов. Изменили сигнатуру функции на сервере — клиент сразу видит ошибку компиляции. Никаких proto-файлов, ни OpenAPI-схем, ни code generation — типы работают напрямую через TypeScript. Это резко ускоряет разработку и снижает количество runtime-ошибок интеграции.
Ограничение очевидно: tRPC работает только в TypeScript-стеке. Backend и frontend должны быть на TypeScript, и желательно в одном monorepo. Для команд, использующих Python или Go на бэкенде, tRPC не применим. Для full-stack TypeScript-команд он становится одним из самых производительных вариантов разработки.
Сравнение по ключевым параметрам
| Параметр | REST | GraphQL | gRPC | tRPC |
|---|---|---|---|---|
| Типизация | Слабая (через OpenAPI) | Сильная (схема) | Сильная (proto) | Полная (через TS) |
| Производительность | Средняя | Средняя | Высокая | Высокая |
| Поддержка в браузерах | Полная | Полная | Через grpc-web | Полная |
| Сложность реализации | Низкая | Средняя | Высокая | Низкая (в TS) |
| Кэширование | Простое | Сложное | Не из коробки | Не из коробки |
| Эволюция API | Через версии | Через депрекацию полей | Через proto-эволюцию | Через TS-типы |
| Лучший use case | Публичные API, простые сервисы | Гибкие клиенты, мобильные приложения | Внутренние микросервисы | Full-stack TS-проекты |
n+1 проблема в GraphQL и DataLoader
Главная техническая боль GraphQL — n+1 проблема. Запрос вида «список из 100 пользователей с их заказами» при наивной реализации создаёт 1 запрос на пользователей и 100 запросов на заказы каждого. Результат — деградация производительности на любых запросах со связанными сущностями.
Решение — DataLoader-паттерн. Внутри одного GraphQL-запроса вызовы к связанным данным группируются (batching) и выполняются одним запросом к источнику. Загрузка заказов для 100 пользователей превращается в один SQL-запрос с WHERE user_id IN (…). DataLoader-библиотеки существуют для всех популярных языков; их использование де-факто обязательно в продакшен-реализациях GraphQL.
Без DataLoader или аналогичного решения GraphQL-сервер в продакшене падает под нагрузкой. Это не недостаток GraphQL как технологии, а особенность, требующая знания при реализации.
Streaming и subscriptions
Real-time и streaming — область, где парадигмы серьёзно различаются.
REST классически синхронный: запрос-ответ. Long polling и Server-Sent Events дают примитивные возможности push-уведомлений, но это workaround, а не нативная функция. Для полноценного real-time’а с REST используется WebSocket поверх отдельного протокола.
GraphQL поддерживает subscriptions — push-уведомления от сервера к клиенту при изменении данных. Реализация обычно через WebSocket, схема описывает доступные subscriptions так же, как queries и mutations. Подходит для real-time дашбордов, чатов, нотификаций.
gRPC поддерживает streaming на уровне протокола: client streaming (клиент шлёт поток), server streaming (сервер шлёт поток), bidirectional (оба направления). Это самая мощная модель из четырёх, идеальная для финансовых тиков, потоков телеметрии, real-time коммуникаций.
tRPC поддерживает subscriptions через WebSocket-адаптер, концептуально похоже на GraphQL subscriptions, но с автоматической типизацией TypeScript.
Эволюция API и обратная совместимость
Один из критических вопросов — как развивать API без слома существующих клиентов. Подходы радикально различаются.
REST использует версионирование URL (/v1/, /v2/). Простой подход: новая мажорная версия — новый префикс. Минус — поддержка нескольких параллельных версий API дольше, чем хотелось бы, удваивает работу команды.
GraphQL декларативно избегает версионирования. Изменения в API делаются через депрекацию полей: старое поле помечается @deprecated, новое поле добавляется параллельно, через год-два старое поле удаляется. Подход требует дисциплины (нельзя резко удалять поля), но избавляет от параллельной поддержки версий.
gRPC через proto-эволюцию: добавление новых полей с новыми номерами не ломает старых клиентов, удаление поля — ломает, поэтому удалённое поле обычно помечается reserved. Подход хорошо работает в долго живущих внутренних API.
tRPC опирается на TypeScript-типы: изменения сразу видны на этапе компиляции клиента. В монорепо-окружении это даёт мгновенную обратную связь, в системах с раздельным деплоем — может требовать осторожности с порядком выкатывания.
Когда что выбирать: типичные сценарии
Сделать однозначный универсальный выбор невозможно, но есть очевидные рекомендации под типовые ситуации.
- Публичный API для внешних разработчиков — REST. Самая широкая поддержка, понимание разработчиками, инструменты документирования. GraphQL тоже работает (GitHub, Shopify), но требует образованной аудитории.
- Mobile-приложение с переменными требованиями к данным — GraphQL. Экономия трафика через точные запросы, удобство для разработчиков мобильных приложений.
- Внутренние микросервисы в крупной инфраструктуре — gRPC. Производительность, типобезопасность, streaming.
- Full-stack TypeScript-проект в монорепо — tRPC. Максимальная скорость разработки, end-to-end type safety.
- Простой CRUD-сервис — REST. Зрелые фреймворки, минимум overhead, любой инструмент работает.
- Real-time торговая платформа — gRPC с streaming или WebSocket поверх любого протокола.
- API для аналитических дашбордов с гибкими фильтрами — GraphQL. Сложные иерархические запросы с подходящей точностью.
Гибридные подходы: использование нескольких парадигм
В реальных системах редко применяется одна парадигма. Большие компании используют разные подходы для разных слоёв архитектуры.
Типичная архитектура зрелой компании: gRPC для общения между внутренними микросервисами; GraphQL или REST как BFF (Backend for Frontend) для веб- и мобильных клиентов; REST для публичного API сторонним разработчикам. Каждый слой использует ту парадигму, которая лучше подходит к его специфике.
В TypeScript-стеках популярна комбинация: tRPC для общения внутри monorepo (фронт-бэк, серверные функции), REST или GraphQL для интеграции с внешними сервисами и партнёрами. Такой подход даёт максимальную скорость внутренней разработки, не теряя совместимости с внешним миром.
OpenAPI и спецификации
Даже в REST современная практика — описание API через OpenAPI (бывший Swagger). Это формальная спецификация, на основе которой генерируется документация, клиентские SDK на разных языках, mock-серверы для тестирования.
OpenAPI закрывает один из главных недостатков «голого» REST — отсутствие строгого контракта. С качественной OpenAPI-спецификацией REST приближается по type safety к GraphQL и gRPC. Минус — спецификация должна поддерживаться в актуальном состоянии, что требует дисциплины.
Современные фреймворки (FastAPI в Python, NestJS в Node.js, Ktor в Kotlin) автоматически генерируют OpenAPI из аннотированного кода, что снимает проблему «спецификация устарела». Такой подход даёт лучший из двух миров: простоту REST и type safety современных подходов.
BFF-паттерн: backend for frontend
В сложных системах часто появляется BFF-слой — backend, специально заточенный под конкретный фронтенд (веб, мобильный, embedded). BFF агрегирует данные из нескольких микросервисов, форматирует под нужды клиента, обеспечивает кэширование и оптимизацию.
Технологически BFF может быть реализован на любой парадигме. Типичный паттерн: BFF разговаривает с внутренними gRPC-сервисами для получения данных, отдаёт клиенту REST, GraphQL или tRPC в зависимости от типа клиента. Такое разделение позволяет внутренним сервисам быть простыми (один протокол, одна задача) и при этом обслуживать сложные внешние интерфейсы.
Часто задаваемые вопросы
GraphQL заменит REST?
Не заменит. GraphQL и REST имеют разные сильные стороны. REST остаётся доминирующим для публичных API, простых CRUD-сервисов, систем с упором на кэширование. GraphQL занимает нишу гибких клиентских интерфейсов с переменными требованиями к данным. Будущее — сосуществование обеих парадигм.
Стоит ли мигрировать существующий REST API на GraphQL?
Только если есть конкретные проблемы, которые GraphQL решит лучше: множественные round trip на одну операцию, over/under-fetching, потребность в гибких клиентских запросах. Миграция ради моды — плохая идея. Часто разумнее добавить GraphQL слоем поверх существующего REST для конкретных use case, чем переписывать всё.
gRPC подходит для публичного API?
Редко. Сложности с поддержкой в браузерах, необходимость в специальных инструментах для отладки, малое количество разработчиков, знакомых с protobuf. Для публичных API почти всегда выгоднее REST или GraphQL. gRPC — для внутренних сервисов или для крупных корпоративных интеграций с подготовленными партнёрами.
Что выбрать для нового стартапа на TypeScript: tRPC или GraphQL?
Если монорепо с фронтом и бэком на TypeScript — tRPC даст лучший developer experience и скорость разработки. Если планируется поддержка мобильных приложений, не на TypeScript, или открытие API сторонним разработчикам — GraphQL дает большую гибкость и масштабируемость по технологическому стеку.
Можно ли смешивать gRPC и REST в одном сервисе?
Можно и часто делается. Сервис обслуживает gRPC для внутренних запросов и REST для внешних. Современные фреймворки (gRPC-Gateway для Go, ConnectRPC для нескольких языков) автоматически генерируют REST-обёртку поверх gRPC-сервиса. Один и тот же proto-файл служит контрактом для обоих интерфейсов.
Что такое Connect и стоит ли его использовать?
Connect — относительно новый протокол от Buf, позиционируемый как «gRPC, работающий в браузерах из коробки». Использует proto-схемы как gRPC, но поверх обычного HTTP с поддержкой JSON и protobuf. Поддерживает streaming, type safety, code generation. Хорош для проектов, которым нужны типизация и контракты gRPC, но без сложности grpc-web.
Заключение
Выбор API-парадигмы — стратегическое решение, влияющее на архитектуру системы на годы вперёд. Универсального ответа нет: каждая парадигма решает определённый набор задач лучше других, и оптимальный выбор зависит от типа продукта, технологического стека, размера команды, требований к производительности и совместимости.
REST остаётся бесспорным лидером для публичных API и простых сервисов благодаря зрелости экосистемы и универсальной поддержке. GraphQL расцветает в проектах с гибкими клиентами и сложными иерархическими данными. gRPC доминирует во внутренних микросервисных коммуникациях крупных распределённых систем. tRPC занимает специфическую, но мощную нишу full-stack TypeScript-разработки.
Зрелые системы редко используют только одну парадигму. Современная архитектура — гибрид, где каждый слой работает на подходящем для него протоколе, а интеграционные точки между парадигмами обрабатываются BFF-слоями или специальными прокси. Понимание сильных и слабых сторон каждой технологии — основа осознанного архитектурного выбора, окупающаяся каждый раз, когда система масштабируется или меняет требования.