Я всю жизнь писал только бэк и подкапотщину - будь это классический КРУД, хайлоад, CLI, [вставьте свое]... И для любых сетевых взаимодействий чаще всего люди думают именно прикладными вещами - GRPC, REST, Kafka, не задумываясь об этом глубже - супер удобные инструменты с защитами от дураков и прочими радостями
Но тут спохватился я писать фронт - подключать свое же к себе же. И в этот момент я понял, насколько же это сложно, муторно и, главное, НЕУДОБНО взаимодействовать REST'ом
ЗАЧЕМ ОН НУЖЕН?? - У нас нет удобного контракта общения (eg Proto, Avro) кроме Swagger, который нужно поддерживать с обеих сторон. Да и к тому-же, сложность взаимодействия с JSONом с ОБЕИХ СТОРОН - одна постоянно маршаллит, защищается, ищет поля, в то время другая боится резких обновлений, что строчка получения поля может превратиться в что-то вроде
resp?.body?[i]?.creds?.card?.number
В этот момент у меня возник вопрос: если gRPC решает эти проблемы, почему мы не используем его на фронте?
Попытки решения отсутствия контрактов
не буду вдаваться в подробности, большинство их +- трогало или знают, распишу плюсы и минусы подходов
Codegen/OpenApi/Swagger
Запихну это все в одно и напишу кратко - ЭТО НИКОГДА НИЧЕГО НЕ ГАРАНТИРУЕТ.
Фронт резко открыл код, сделал запрос, а там уже разраб, который ушел в отпуск, уже все поля красиво поменял, Сваггер не обновил и никому ничего не сказал.
Вы скажете - «Ну проблема в плохой организации работы» - как бы да, но у всех бывают дни, когда чо-то идет не так, мы все люди))
GraphQL
Известная голубая компания ЛицоКнига когда-то решила проблемы разработчиков интерфейсов - строгая схема, типизация, codegen, introspection
Фронтендеры были относительно счастливы (хотя и своих бед с GQL хватало), но кому явно досталось - бэк
Разработчики серверной части лишились КОНТРОЛЯ
клиент сам выбирает, что ему нужно и в каком количестве
сегодня это было
{ user { id name } }а завтра станет
{ user { posts { comments { author { posts { comments { … } } } } } } }по итогу ты опять созваниваешься с фронтом и говоришь «Это дергай, а это старайся не дергать»
отсутствует прямое соответствие моделям данных
резолверы, маппинг, аггрегации, не дай бог юнион тайпы - вокруг всего теперь танцую Я, а не ОН
N+1
Почти везде, почти всегда
Да, эти проблемы решаются - DataLoader закрывает N+1, persisted queries и depth limiting ограничивают клиента. Но каждое из этих решений - это дополнительный слой сложности на бэке, которого не было с REST
Про неудобство в некоторых языках я вообще молчу (гляньте пакет на го)
gRPC-Web
Так вот же оно! К чему статья??
Надеюсь, что я не многих удивлю, но это - ненастоящий gRPC
Это - лютейшего вида trade-off! Да, с виду у нас все есть: Protobuf, codegen, контракт. А что под капотом?
Отсутствие полноценного HTTP/2
Да, все верно. Хоть браузеры давно поддерживают даже 3ю версию протокола, но полного контроля нам никто не давал. А это значит, что мы теряем
Опять Контроль - мы НЕ управляем соединением, НЕ управляем фреймами, НЕ управляем стримами. Клиент даже не знает, какую версию HTTP изволит использовать браузер, это выглядит как обычный fetch
fetch(“/api”)Одни из самых главных фишек HTTP/2 - управление мультиплексированием и бинарными фреймы.
Браузер → прокси работает по HTTP/1.1, зато прокси → бэк использует полноценный HTTP/2. Выигрыш есть, но только на конце - там, где нам он нужен меньше всего
Просто с места ничего не заработает - нужна инфраструктура, которые это будет поддерживать (как минимум Proxy, который это будет правильно принимать и понимать)
Итог: gRPC перестаёт быть транспортом и превращается в формат данных. Мы приобретаем лишь гемор без плюсов. Как говорил один классик:
Можно. А зачем?
А почему так-то?
Тут и стало понятно - проблема не в REST, не в GraphQL и даже не в gRPC
Главная проблема - САМ БРАУЗЕР
Ответ почему так получилось крайне прост - БЕЗОПАСНОСТЬ, а возможно даже параноидальность
Каждый раз заходя на любой сайт мы запускаем код на своем устройстве: с любого источника, от любого автора, с доступом к сети
Если мы дадим ПОЛНЫЙ ДОСТУП к HTTP/2-3, мы соответственно даем также доступ к
Любым TCP соединениям
Любым долгоживущим соединениям
Управлению HTTP/2 фреймам с плохим умыслом
А с таким набором инструментов на любом ПК мы можем
сканировать внутреннюю сеть пользователя
ломиться в
localhostобходить firewall
устраивать DDoS чужими руками
А почему с HTTP/1.1 нет проблем?
Тут два главных столпа
Sandbox
JS работает в своей песочнице, в которой нет доступа ни к сокетам, ни к файловой системе (ну почти), к процессам и вообще ограничен контроль над сетью
Абстракция
Нам дают маленький набор инструментов (и тот ограничен, как к пр. WebSocket). Браузер все решает САМ
Тогда в принципе зачем браузеру нужны HTTP/2-3?
Примерно тут уже в голове небольшой диссонанс - если все фичи заблокированы, то тогда зачем они в принципе существуют?
А все крайне просто - HTTP/2-3, QUIC и прочие радости существуют не для кода, исполняемого в браузере, а для самого браузера. Мы не можем пользоваться этим самим, а он все сам решает
мультиплексирование
сжатие заголовков
бинарный формат
и многое другое, но все ведет к одному - только для ускорения самому, и не более
Панацея (или что с этим делать)
Редко, но можно найти в использовании красивые лаконичные решения
tRPC
если у тебя монорепо TypeScript, это почти идеальный контракт без кодогенерации. Работает поверх обычного HTTP, типы шарятся напрямую через импорт
Connect protocol (от Buf)
адстройка над gRPC, которая умеет работать с обычным fetch. Официально поддерживается в браузере, имеет нормальный codegen
WebTransport
экспериментальный API, который даёт браузеру доступ к HTTP/3 потокам в sandboxed режиме. Пока сырой, но это именно то направление, куда движется индустрия
Что в итоге?
Альтернативы существуют - tRPC, Connect, WebTransport. Но REST никуда не уйдёт в ближайшее время, потому что универсальность важнее удобства. Браузерные ограничения - это симптом, а корень глубже: мы до сих пор не договорились, кто отвечает за контракт (да, мы тоже в этом виноваты)
Но все идет вперед. WebTransport потихоньку стабилизируется и даёт браузеру то, чего у него никогда не было - доступ к потокам поверх HTTP/3 в sandboxed режиме. Connect protocol от Buf набирает комьюнити, а TypeScript-монорепозитории с tRPC уже сейчас показывают, как выглядит мир без ручного поддержания контрактов (хоть и крайне редко)
Возможно, через пять лет мы будем смотреть на голый REST так же, как сейчас смотрим на XML-RPC. А возможно - нет, и curl останется главным инструментом отладки ещё очень долго
Также буду благодарен за подписку на мой тгк, если вам близки такие мини статьи и факты из моего личного опыта работы)
