
Одна из самых основных проблем в работе с gRPC - необходимость наружу вытаскивать отдельно REST API для web клиента, но, надо ли отдельно его писать, или можно как-то унифицировать и эту историю?
И вот начал я копать эту тему, и чем глубже копал, тем больше удивлялся. Оказывается, за последние почти 10 лет было целых ТРИ ЧЕТЫРЕ серьезных попытки затащить gRPC в веб. И знаете что самое смешное? Самая первая попытка, сделанная в 2015 году японкой-одиночкой (в команде с коллегами), до сих пор остается самым адекватным решением. А Google со всеми своими миллиардами и армией разработчиков так и не смог ничего нормального придумать. Но обо всем по порядку.
Ах, да, меня зовут Эдгар Сипки, я все также евангелист gRPC && OpenSource :) Кстати, мой канал, там я гораздо чаще пишу (а скоро еще и начну снимать очень много крутого контента про gRPC и Go), ну и конечно один из основателей инструмента EasyP
Ссылка на доклад в конце статьи, если захочется просто посмотреть
Почему вообще gRPC — это круто, и почему он вам нужен
DOR FIRST FOREVER - Давайте сразу договоримся — если вы пишете код, потом к нему комментарии, а из комментариев генерируете Swagger, вы создаете себе проблемы на ровном месте. Почему? Да потому что ваш код может не соответствовать комментариям, комментарии могут врать, а фронтендер в итоге получит 500-ку там, где по документации должна быть 200-ка. И начинается: "А почему у меня не работает?" — "А ты точно правильно запрос шлешь?" — "Да, вот смотри, по доке все правильно!" — "Ой, а мы забыли документацию обновить..."
gRPC решает эту проблему радикально — вы СНАЧАЛА пишете контракт в виде proto-файла, описываете там все ваши сервисы, методы, типы данных, а ПОТОМ из этого генерируется код. И фронтендер, и бэкендер работают с одним и тем же контрактом. Никаких расхождений в принципе быть не может.
Но это еще не все. В gRPC из коробки есть стримы. Причем забавный факт — даже обычные unary-запросы (один запрос — один ответ) на самом деле тоже стримы. Просто они открывают соединение, отправляют данные, получают ответ и сразу закрываются. И вот именно эта особенность стала корнем всех проблем с браузерами, но об этом позже.
Еще один плюс — размер сообщений. Protobuf не хранит ключи в теле сообщения, поэтому одни и те же данные в Protobuf весят примерно на 25% меньше, чем в JSON. Кажется, что это мелочь? А теперь представьте, что у вас миллионы запросов в секунду. Эти 25% превращаются в гигабайты сэкономленного трафика и мегаватты электричества на серверах.

Но, если он так хорош, почему в web его нет?

Казалось бы, такой крутой протокол должен был захватить мир. И знаете что? Он почти это сделал, но, не в web :(
Основная проблема до безобразия простая — браузеры не умеют работать с HTTP/2 стримами из JavaScript. Вообще никак. Нет такого API, через которое можно было бы из JS управлять HTTP/2 стримами.
Вторая проблема - вы экосистему OpenAPI видели? Как с ней конкурировать хотите?

И вот тут начинается самое интересное — как все пытались найти тот самый велосипед, который таки решит проблемы.
Основных попыток было 4, каждая из которых была разной степени паршивости (или же качества)
gRPC-Gateway
gRPC-Web
Twirp
ConnectRPC
gRPC-Gateway исторически родился самым первым, и если честно, меня это очень удивило, так как я был уверен, что первые кто мог попробовать засунуть gRPC в WEB явно должны быть ребята из Google (но может они и первые, но не выложили свои наработки).
После них уже зарелизился gRPC-Web от самого Google, который, к несчастью, не возымел того успеха, что gRPC-Gateway. Третья попытка была за Twirp и пока что она меня больше всего удивила (провалом), а последняя (на самом деле не совсем) попытка была от ребят, что сделали buf.build , ребята зарелизили ConnectRPC, но и тут все не так однозначно, фактически ребята прост переделали Twirp.

Попытка №1: gRPC Gateway

2 апреля 2015 года в прод вышел gRPC-Gateway, Yuki Yugui Sonoda (не рискнул переводить ФИ на русский в боязни ошибиться) выкатила сервис, но вот что важно, разрабатывала она его в рамках своей, на тот момент, компании, где основная идея была унифицировать описание межсервисной и клиентской спеки, что и привело к тому, как сейчас устроен gRPC-Gateway
Кстати, взял небольшое интервью (ну как интервью, в линкеде спросил) и она еще рассказала, что когда проект возымел успех, с ней связались ребята из Google и предложили как раз воспользоваться описанием спеки из своей механики, что уже как бы говорит, что google имели схожий проект, но так и не зарелизили его в OpenSource
Но, давайте по теме, почему gRPC-Gateway зашел так хорошо? Давайте посмотрим классическую структуру gRPC сервера

Но их идея была гениальна в своей простоте — не надо пытаться засунуть gRPC в браузер, давайте сделаем адаптер! У вас есть обычный gRPC-сервер, который прекрасно работает со всеми вашими микросервисами. Рядом с ним автоматически генерируется прокси, который принимает обычные REST-запросы от фронтенда, конвертирует их в gRPC и отправляет на ваш сервер, а дальше ответ проделывает обратный путь.

И в этом и крылся основной успех проекта - он не стал заставлять переделывать WEB под gRPC, а также не стал заставлять что-то изучать frontend разработчикам, они/она сделали простой адаптер, который решил проблему удобным обрзаом - просто генерацией прокси, как это работает? Если кратко, мы берем просто нашу спеку с proto

Дальше нам нужно добавить в proto-файлы специальные аннотации, указывающие, какие HTTP-методы и пути соответствуют вашим gRPC-методам. Генерируете код — и вуаля, у вас есть и gRPC-сервер для микросервисов, и REST API для фронтенда, и даже автоматически сгенерированная Swagger-документация! Причем вся бизнес-логика пишется один раз, а все gRPC-интерсепторы продолжают работать, потому что прокси общается с сервером через обычный gRPC-клиент (но это настраиваемо)

Решение не идеальное — это все еще не настоящий gRPC в браузере, а REST с протобафами.
И знаете самое классное? Работают стримы! Ну как работают, это буквально просто конвертация в websocket соединение :D

Дальше просто вызываем библиотеку wsproxy при сборке сервера.

Недостатки
Но, давайте будем честны, не бывает малины без капли пеницилина

Самая первая проблема - загрузка файлов не работает из коробки, приходится писать отдельные HTTP-хендлеры

Иииииии, все? Ну и есть миф про то, что нельзя работать с http специфической историей, но это именно что миф, так что явно не проблема, нуууу, и это не gRPC :D Давайте будем объективны, это удобный и классный адаптер, который решил конкретную проблему, унифицировать, а не встроиться в другие реалии, и именно этим он так выигрывает

И вот тут, наверное я остановлюсь, а то материал уже получился гигантским, а есть еще другие фреймворки :)
Продолжение следует….. (после релиза след части станет кликабельной)
P.S. Ах, да, если вы хотите пораньше посмотреть/послушать, то можно попросту посмотреть на YouTube сам доклад :)
Комментарии (4)
positroid
29.08.2025 07:36Эх, на самом интересном месте, хотел посмотреть сравнение с grpc-web, потому что это то решение, которое предложил и реализовал ии в моем пет проекте для решения обозначенной проблемы (причем реализовал без затруднений, по этому делаю вывод что вариант гугла как минимум широко распространен).
Сам с grpc ранее не работал и до реализации даже не знал что нет нативной возможности ходить напрямую из браузера.
ZergsLaw Автор
29.08.2025 07:36А вот gRPC-Web на самом деле реально хуже, чем просто gateway , не в плане техологии, а в плане юзабилити
Но обо всем как раз напишу во второй части :)
Yakwilik
29.08.2025 07:36Интересно. Не знал, что стримы тоже можно проксировать на фронт. Даже двунаправленные. Круто
cmyser
Используем в работе grpc gateway, очень удобно