Сюрпризи і пастки JSON API, GraphQL і gRPC: як зробити правильний вибір

💡 Усі статті, обговорення, новини про Front-end — в одному місці. Приєднуйтесь до Front-end спільноти!

Привіт, мене звати Андрій Мойсол. Я iOS Developer у стартапі AlphaNovel від венчур-білдера SKELAR. Ми створюємо маркетплейс романтичних новел і коміксів для читачів і письменників з усього світу.

Сьогодні хочу поговорити про найпоширеніші технології взаємодії між клієнтом і сервером, а саме: JSON API, GraphQL і gRPC. Розберу, в чому плюси і мінуси кожного метода, які підводні камені чекають під час реалізації та чи варто переходити з одного на інший в проєкті.

У цій статті більшість прикладів будуть на Swift, як мови для розробки під iOS, та на Golang, щоб продемонструвати сторону бекенда. Та не лякайтесь, якщо не знаєте жодну з мов, приклади будуть достатньо прості для розуміння і можуть бути застосовані на будь-якій мові та для будь-якої платформи.

Як працює HTTP

Для початку треба зрозуміти, як узагалі дані передаються від клієнта до сервера. Найпоширенішим протоколом прикладного рівня є HTTP (Hypertext Transfer Protocol), абсолютна більшість вебсайтів та мобільних застосунків використовують його для обміну даними з сервером. За допомогою нього можна передавати будь-яку текстову інформацію та бінарні дані.

Зазвичай клієнт і сервер — це просто два комп’ютери, які мають публічну IP-адресу. Через протокол транспортного рівня TCP (Transmission Control Protocol) клієнт відправляє серверу request, який реалізує протокол HTTP. Сервер обробляє відправлену інформацію і відправляє клієнту назад response, також під протоколом HTTP.

Базова структура HTTP-запиту складається з status line, headers та body.

Перший рядок описує метод запиту (GET, POST, PUT, DELETE тощо), URL та версію HTTP. На сьогоднішній день найпоширенішими версіями є HTTP 1.1 та HTTP 2.0. Майже всі браузери на пристрої підтримують новішу версію, та через деякі сервіси все ще використовується HTTP 1.1.

Далі йдуть headers — параметри запиту, найважливіші для нашої теми це Content-Type, Content-Length. Вони описують розмір і тип body — найважливішої частини запита.

Body — це місце де записується основні дані запита. Хоча для деяких HTTP-методів, наприклад, GET, він не використовується (замість цього використовуються Query Parameters), більшість мобільних застосунків послуговуються ним із HTTP-методом POST, щоб відправити дані на сервер.

Сервер обробляє запит, розуміючи з хедеру Content-Type, який тип body йому прийшов, і відправляє схожий за структурою HTTP response.

Відмінність від request у першому рядку, тут він відправляє версію HTTP та статус-код, який свідчить, чи відбулася помилка, чи все відбулося успішно. По хедерам Content-Type та Content-Length клієнт може розпарсити body, якщо воно є, і отримати дані від сервера.

Ось так ми розібрали, як спілкуються клієнт і сервер у форматі request-response. Зазначу, що можна зробити upgrade HTTP-запита до Websocket API — ще один тип з’єднання, у якому сервер може без ініціативи клієнта відправляти йому повідомлення, але у цій статті розбирати realtime-спілкування ми не будемо.

Усі три методи спілкування, які ми розбираємо, так чи інакше будуть працювати на протоколі HTTP, тому базові знання HTTP необхідні для подальшого розуміння.

JSON API

Зараз розберемо, мабуть, найбільш популярний метод серед Mobile-розробників — JSON API. Під цим поняттям я маю на увазі звичайний HTTP-запит, body якого є текст в форматі JSON.

Front-end

Усе, що треба для сетапу цього методу спілкування: будемо в хедері Content-Type відправляти application / json, метод HTTP на POST і body закодоване в JSON. Сервер організовує так звані endpoints, кожен з яких виконує свою функцію. Якщо сервер потребує авторизації клієнта, дуже часто використовують JWT-токени, які передають в хедер Authorization.

Цей спосіб комунікації є найпростішим з точки зору клієнта, бо не потребує ніякого сетапу, усе це можна зробити, використовуючи структури та методи стандартної бібліотеки.

Усе, що нам потрібно, це:

  • зробити структури request та response;
  • закодувати request в JSON;
  • створити HTTP-запит на конкретний endpoint та передати закодований JSON та необхідні хедери;
  • у хендлері обробити транспортну помилку;
  • обробити HTTP-помилку (status code);
  • розкодувати response в структуру і обробити його;
  • відправити запит.

Як ми бачимо, рішення займає 60 строчок, якщо використовувати стандартну бібліотеку. Тому більшість використовує зручні бібліотеки-обгортки, як-от Alamofire та SwiftyJSON. Також можна помітити, що response-модель має поле для помилки. Цей механізм дозволяє бекенду чітко казати, в чому помилка, замість того, щоб використовувати HTTP status code. Хоча дуже часто можна натрапити на комбінацію з status core та модельки.

Використані бібліотеки

Built-in

  • Foundation

Third party

🚫

Back-end

На стороні сервера JSON API також легко реалізується за допомогою стандартних бібліотек. Ми пишемо handler для якогось endpoint, в якому приймаємо request та ResponseWriter. Декодуємо дані з request та записуємо дані в ResponseWriter. Приклад простого endpoint можна побачити нижче.

  • створюємо моделі request та response;
  • перевіряємо HTTP-метод;
  • декодуємо request, відправлений клієнтом;
  • виконуємо дію з даними;
  • кодуємо response та відправляємо назад клієнту;
  • створюємо endpoint і назначаємо йому handler;
  • запускаємо сервер.

На практиці використовують фреймворки та бібліотеки, як-от Gin та FastHttp.

Використані бібліотеки

Built-in

  • encoding
  • log
  • net

Third party

🚫

Документація

Ось і приходить час на дев-рефайменті писати API-контракт між клієнтом і бекендом. Зазвичай використовують або текстову документацію, або Swagger. Клієнт може в будь-який момент подивитись цю документацію, зрозуміти, які моделі request та response йому потрібно відправляти і на які endpoints — звучить легко і просто. Але на плечі бекенд-розробника лягає тягар у вигляді написання та підтримки API-документації.

Підсумок

Як ми бачимо, JSON API — просте і швидке рішення для початку написання нового проєкту. Та у нього є недолік у вигляді документації, оскільки бекенд-розробнику треба руками або в напівавтоматичному режимі підтримувати документацію.

Також можна помітити, що, як і клієнт, так і бекенд будуть мати в своїй кодовій базі дуже багато request та response структур, що може захламити проєкт, та призвести до того, що модельки будуть не синхронізовані, оскільки часто бекенд і клієнт пишеться на різних мовах і в різних кодових базах.

Ще одним мінусом є те, що якщо клієнту потрібно отримати частину даних, то потрібно або робити окремий endpoint, або використовувати наявний endpoint, який повертає повну модель даних, що не є оптимальним.

Обидві з цих проблем вирішує GraphQL, до якого ми зараз і переходимо.

GraphQL

GraphQL — це мова запитів для API, розроблена Facebook. Вона дозволяє клієнтам визначати точну структуру запитаних даних, що робить можливим отримання всіх необхідних даних одним запитом.

Головна проблема, яка була у JSON API — окремі моделі request / response для клієнта і бекенда.

На відміну від традиційних REST API, де структура відповіді визначається сервером, GraphQL надає клієнтам повний контроль над запитуваною інформацією, зокрема вибір полів та вкладені запити. Бекенд і фронтенд мають одну базу опису API на одній мові. Це дозволяє дуже швидко синхронізувати моделі даних та не робити одну й ту саму роботу двічі.

Приклад моделей на GraphQL:

GraphQL поділяє запити на Query та Mutation. Відповідно запити в Query віддають дані без змін в стейті бекенда, а запити в Mutation навпаки змінюють стейт.

Усі запити можуть приймати параметри. Щоб зробити параметр обов’язковим, можна після типа вказати !. Також є enums та union types, що дозволяє дуже легко робити інтерфейси з різними видами UI.

Під капотом GraphQL працює на HTTP, але завжди використовує POST-запити. Тому GraphQL працює всюди, де працює звичайне JSON API.

Оʼкей, тепер ми пишемо наше API на одній мові — GraphQL, але як же працювати із цим? Для цього є багато бібліотек як для бекенда, так і для фронтенда. Почнемо з першого.

Back-end

Використовувати будемо бібліотеку gqlgen. Запишемо наше GraphQL API в окремий файл та за допомогою команди generate згенеруємо весь код за нас 🙂. Створються чотири нові файли:

models_gen.go — тут згенерувались усі модельки, потрібні нам для роботи.

generated.go — тут генерується допоміжний код, на практиці цей файл завжди ігнорується.

resolver.go — файл, у якому всього одна структура Resolver. Тут ми маємо можливість зробити DI, оскільки інстанс Resolver буде доступним в імплементації запитів.

Додамо для прикладу стейт — массив коментарів.

І останній файл schema.resolvers.go — тут ми й будемо імплементувати наші запити. Тут створюються resolvers для Query та Mutation. Імплементуємо CreateComment та Comments.

Усе, що залишилось, це запустити сервер з нашим Resolver.

У цьому прикладі сервер буде доступний на порту 8080 під шляхом /query. Та ще буде запущений GraphQL Playground під шляхом /.

Playground — дуже крутий бонус до написання API, так як нам тепер не треба писати Swagger. GraphQL на основі схеми сам за нас зробить красивий UI і документацію.

Перейшовши на localhost:8080, ми можемо побачити всю структуру API та навіть протестувати її прямо в браузері (тепер і Postman не потрібен 🙂)

Можна побачити, що під час виклику мутації createComment ми у відповідь отримаємо модель коментаря, але ми перераховуємо всі поля цієї моделі. Навіщо?

Ось тут ми і переходимо до головної фішки GraphQL — можливість обирати поля, які нам потрібні саме в конкретному контексті. Тому ми плавно переходимо до клієнта.

Використані бібліотеки

Built-in

  • encoding
  • log
  • net
  • fmt
  • context

Third party

  • gqlgen/graphql

Front-end

GraphQL API під капотом також працює через HTTP та JSON в якості формату даних. Тому теоретично можна використовувати такі самі підходи для відправлення запитів на клієнті. Але є багато бібліотек, які спрощують відправлення запитів і, найголовніше, генерують моделі на основі моделей GraphQL. Поглянемо, як це виглядає на прикладі бібліотеки Apollo GraphQL. Вона пропонує генерацію кода, кешування запитів та ярусну структуру запитів, що дозволяє нам робити middlewares, наприклад, для авторизації.

Додаємо бібліотеку в проєкт, запускаємо ініціалізацію конфігураційного файла через CLI. Додаємо в проєкт файл зі схемою GraphQL.

Далі в окремих файлах .graphql треба описати операції, які буде використовувати клієнт у конкретному контексті.

Як ми бачимо, запити можуть приймати динамічні параметри. Також можна створювати фрагменти — зріз полів оригінальної моделі, таким способом можна перевикористовувати одні й ті самі фрагменти в багатьох запитах.

Далі запускаємо команду generate через CLI. Генерується локальна бібліотека з кодом запитів і моделями.

Приклад згенерованого фрагменту:

Далі все дуже просто, створюємо інстанс клієнта та викликаємо запити.

Використані бібліотеки

Built-in

  • Foundation

Third party

  • apollo-ios

Підсумок

GraphQL вирішує обидві проблеми з JSON API. У нас є можливість мати на клієнті й на бекенді єдиний source of truth для опису API, а також ми можемо, залежно від контексту, запитувати тільки ту інформацію, яка нам потрібна, без зміни бекенда. Також не забуваємо, що GraphQL схема — strongly typed, що унеможливлює неправильний request чи парсинг response.

Ще великим плюсом GraphQL є subscriptions — вони працюють на WebSockets та також використовують загальну схему.

Що в мінусах? З мого досвіду, GraphQL — одне з найкращих рішень для створення API, однак треба бути обережним. Наприклад, дуже часто можна переборщити з кількістю фрагментів на стороні клієнта, і в коді буде дуже багато згенерованих моделей для багатьох різних контекстів, що створює проблему для сприйняття. Тому дуже важливо використовувати мінімальну кількість фрагментів.

Ще одним мінусом GraphQL та JSON API можна вважати розмір request та response. Якщо ваш проєкт повинен працювати швидко і не може собі дозволити великий трафік, то варто подивитись в сторону gRPC.

gRPC

gRPC — реалізація Remote Procedure Call від Google. Це спосіб передачі даних у бінарному вигляді за допомогою Protocol Buffers. Працює на HTTP 2.0, у якому хедери займають менше місця.

Також підтримує Bi-directional streaming з коробки.

Protocol Buffers — зручний формат для опису API-контракта, також однаковий для клієнта та сервера, що дуже схоже на GraphQL-схему. Але немає вибору полів клієнтом, як це є в GraphQL.

Найчастіше gRPC використовується разом з HTTP/3, тому клієнти повинні його підтримувати. Деякі старі браузери та версії OS не мають цієї підтримки, та працюють з HTTP/2, і навіть з HTTP/1. Якщо треба підтримувати ці старі клієнти, то використовують gRPC-Web, який трансформує старі формати в HTTP/3.

Подивимось на приклад реалізації бекенда і фронтенда.

Back-end

Створюємо в проєкті .proto-файл та описуємо сервіс коментарів. Структури називаються message, а кожне поле повинно мати свій індекс. Сам сервіс описується в блоці service, а запити ключовим сломов rpc.

Після цього, використовуючи CLI-утиліту protoc, генеруємо із цього файлу код моделей та сервера.

Згенеруються моделі, які легко конвертуються у бінарний вигляд. Усе, що залишилось — створити сервер та імплементувати rpc-функції. Також будемо для прикладу збирати коментарі в масиві.

Як бачимо, структура і дії дуже схожі на GraphQL.

Використані бібліотеки

Built-in

  • context
  • fmt
  • log
  • net

Third party

Front-end

Як можна здогадатись, на клієнті сетап буде схожим на GraphQL. Треба взяти proto-файл схеми і через CLI protoc згенерувати моделі та структуру gRPC-клієнта.

Зауважу, що HTTP 2.0 наразі підтримується не всіма клієнтами на Web, тому для роботи з gRPC саме на Web треба використовувати проксі у вигляді gRPC Web, який трансформує HTTP 1.1 в HTTP 2.0. На мобільних девайсах все працює з коробки.

Отримаємо два файли — один з моделями, інший зі структурою клієнта. Приклад моделі:

Після цього створюємо з’єднання з gRPC-сервером і викликаємо процедуру.

Використані бібліотеки

Built-in

  • Foundation

Third party

  • grpc-swift
  • SwiftNIO

Підсумок

gRPC крута технологія, яка дуже часто використовується для взаємодії між мікросервісами на стороні бекенда, тому логічно було б використовувати його і для клієнтської взаємодії. Запити дуже швидкі, малі за розміром. Як і в GraphQL, тут є генерація коду з proto-файлу, що забезпечує синхронізацію.

Мінусом gRPC є його тестування, якщо в GraphQL є зручний playground, де можна і запускати запити і дивитись документацію, то у gRPC такого зручного середовища немає.

Що обрати

Ось ми поверхово розібрали що являють собою три найпопулярніші методи комунікації у світі бекенда і фронтенда, але що ж обрати?

Звичайно ж, для кожного проєкту буде свій найкращий вибір, але я спробую дати невеличкий roadmap.

JSON API

GraphQL

gRPC

Легкий сетап

🟢

🟡

🟡

Швидкодія

🟡

🟡

🟢

Realtime комунікація з коробки

🔴

🟢

🟢

Автоматична документація / sandbox

🔴

🟢

🟡

Автоматичне генерування коду

🔴

🟢

🟢

Синхронізація back-end- та front-end-моделей

🔴

🟢

🟢

JSON API підійде в таких кейсах:

  • MVP;
  • маленьке API;
  • немає необхідності у швидкодії та малому розмірі даних;

gRPC буде кращим варіантом, якщо:

  • бекенд уже використовує gRPC у якості взаємодії між мікросервісами;
  • середнє-велике API;
  • є необхідність у швидкодії;
  • ваша інфраструктура побудована на сервісах Google;
  • realtime-спілкування;
  • хочеться уникнути помилок і генерувати код.

GraphQL:

  • середнє-велике API;
  • хочеться легко і швидко тестувати бекенд через GraphQL Playground;
  • є потреба запитувати тільки ті дані, які необхідні в цьому контексті;
  • realtime спілкування;
  • хочеться уникнути помилок і генерувати код;

Куди далі

Сподіваюсь, стаття дозволила вам краще зрозуміти, які технології в яких випадках кращі і з якими легше працювати. До речі, не забудьте поділитися в коментарях своїм досвідом роботи з цими методами комунікації.

👍ПодобаєтьсяСподобалось39
До обраногоВ обраному21
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Дуже дякую Вам за цей цікавий докладний огляд та порівнянь різних технологій!)

Варто додати, що gRPC фреймворк має github.com/...​/doc/server-reflection.md, що значно спрощує дебаг і має купу інтеграцій ( наприклад з тим же postman). Також він дозволяє гнучко конфігурувати клієнтів зі сторони сервера через наприклад DNS, ретраї, дедлайни і хеджинг.

Хороша стаття, коротко і з прикладами.

Рекомендую краще ознайомитися з тим що таке OpenAPI специфікація і який тулінг для неї існує, бо деякі речі написані про «JSON API» не відповідають дійсності. Генерація коду існує давно — можете подивитися github.com/...​PITools/openapi-generator або github.com/RicoSuter/NSwag. Про документацію — не зрозумів висновків. Навіть якщо ви опишете тільки схему АПІ без жодного рядку опису — і «плейграунд» буде доступний, і код зможете генерити. До того ж, в деяких платформах можна не писати файл зі специфікацією окремо, а досягати тих самих результатів анотаціями на методах в коді вашого сервісу: підключили бібліотеки (той же Свагер), анотували методи/контролери, і у вас вже є плейграунд на спеціальному URL у вашому сервісі. В чому тоді такий варіант з документацією і «плейграундом» програє GraphQL?

Дякую за коментар! Мій поінт був у тому, що на багатьох платформ все таки прийдеться писати OpenAPI документацію руками (навіть анотації це також ручне описування і не в усі мови програмування вони підходять), на що витрачається час розробника. Стосовно генерації коду, так, тут поінт вірний👍. Але насправді у світі Mobile розробки не зустрічав генерацію з OpenApi документації.

Щодо документації та синхронізації моделей все одно не розумію різницю :) В GraphQL ви описуєте загальну модель для клієнта і сервера, в gRPC (Protobuf) — теж, і з OpenAPI можна зробити так само. Можете підтримувати загальний файл моделі для API у форматі OpenAPI специфікації, і генерити клієнтські проксі та серверні стаби автоматично. І в цьому випадку я не бачу різниці між технологіями в аспектах документації, автогенерації, та загальної моделі між клієнтом та сервером. Те що багато девелоперів пишуть бойлерплейт для REST API — досить часто проблема необізнаності.

З GraphQL головна проблема — його важко реалізовувати на бекенді, щоб не було от такого:

Pavlo Soia: часто буває так, що додаєш одне додаткове поле до респонзу і відповідь приходить не за кілька секунд, а за 30-60 секунд

Наївна реалізація GraphQL має всі ті ж недоліки що й наївна ORM

є потреба запитувати тільки ті дані, які необхідні в цьому контексті;

це давно вирішується введенням параметрів HTTP запиту
include, exclude, fields для даних,
mode, case, scenario — для повідомлення бекенд про контекст.

причому значення цих параметрів можуть бути більш високого рівня абстракції аніж просто поля. Наближені до потреб конкретного домену, аплікації, більш декларативні, аніж перелік пропертей і їх формату у об’єктів. Умовно кажучи — еволюційно буде створений DSL для API проекту :)

Недолік звісно є — треба просити бекенд імплементувати нові значення параметрів, а GraphQL тіпа — все вже відразу є, як фронтенд хоче, то нехай собі і отримує і не виїдає мозок бекендерам.

це давно вирішується введенням параметрів HTTP запиту
include, exclude, fields для даних,
mode, case, scenario — для повідомлення бекенд про контекст.

і шо ви далі пропунуєте робите з цим include/exclude fields назвами і контекстом у вашом коді?

і шо ви далі пропунуєте робите з цим include/exclude fields назвами і контекстом у вашом коді?

те саме що з любим кодом.

не зрозумів питання :)

ви вище описуєте якесь рішення яке давно вже деся використовується кимось щоб зробити проекцію данних у відповіді(отримати данні згідно список полів які потрібні клієнту) — я от і питаю, як воно має виглядати(ваша імплементація) як приваблива альтернатива graphql імплементаціям. у випадку з останнім, ви підключаєте тип клієнт отримаю проекцію данних що можуть знаходитися у рсубд наприклад без необхідності писати вибірку данних.

У вас є якась альтернатива яка вже давно десь кимсь використовується без graphql і інших рішень що автоматично роблять отримання данних — як вона виглядає.

У вас є якась альтернатива яка вже давно десь кимсь використовується без graphql і інших рішень що автоматично роблять отримання данних — як вона виглядає.

не в мене, а задовго до graphql вона була.
нижче надали вже згадку про неї — jsonapi.org

у випадку з останнім, ви підключаєте тип клієнт отримаю проекцію данних що можуть знаходитися у рсубд

і маєте всі типові проблеми як з ORMом.

додаєш одне додаткове поле до респонзу і відповідь приходить не за кілька секунд, а за 30-60 секунд
я от і питаю, як воно має виглядати(ваша імплементація)

як напишите — так і виглядатиме.

все й далі не розумію питання.
я переважно бекендер. зручності фронтеда мені зрозумілі, але дуже зрозуміла — їх ціна на бекенду. і зрозуміло звідки швиденько береться оте: «не за кілька секунд, а за 30-60 секунд»

А прибивання цвяхами структури відповіді бекенду до схеми РСУБД, то взагалі в холодний піт кидає.
Хоча — більшість так і робить. Бо — ефект підручника. Такі в підручнику для першачків приклади.

не в мене, а задовго до graphql вона була.
нижче надали вже згадку про неї — jsonapi.org

GraphQL: Initial Release, September 14, 2015
JSON: API: 1.0 final released. 2015-05-29

Facebook started GraphQL development in 2012
JSON:API 2013-05-03: Initial release of the draft.

okay.. більш влучним прикладом була б Odata, але це теж згодиться, аби щось невпопад ляпнуть.

і маєте всі типові проблеми як з ORMом.

ок, а з

це давно вирішується введенням параметрів HTTP запиту
include, exclude, fields для даних,
mode, case, scenario — для повідомлення бекенд про контекст.

ви їх не будете мати?

JSON: API: 1.0 final released. 2015-05-29

це дата спроби формалізації.

бо так вже робили роками до цього.
і виникло питання у ком’юніті — то може якось стандартизуємо?

ви їх не будете мати?

ні, не буду.

точно коли так робив — не мав проблем. того й робив, щоб не мати їх.

ні, не буду.

ну це і було питання оригінальне — розкажіть як ви наприклад зробите вибірку данних або сортування, якщо список полей приходить з UI, тим більше що це ж вже давно робиться десь і схоже дісно якась гарна альтернатива «загальновідома».. :)

розкажіть як ви наприклад зробите вибірку данних або сортування, якщо список полей приходить з UI,

що саме розказати? як пишеться програмний код, чи що?

і схоже дісно якась гарна альтернатива «загальновідома».

так, вона загальновідома хто давно в програмуванні, або бував на таких проектах.

школярам так, невідома. в підручниках багато чого не пишуть.
як то в старій статті було коротенько написано

Специальность — каменщик
RSDN.ru " article " career " BrickScience
Специальность — каменщик. Автор: Dr. Joseph M. Newcomer Перевод: Андрей Лягусский Источник: «A Brick Science Degree». Опубликовано: 25.12.2004

Swagger, який потім був переіменований в OpenAPI, почали розробляти в 2010 році (en.wikipedia.org/...​iki/OpenAPI_Specification), і про нього більшість девелоперів в курсі.

Наприклад є jsonapi.org який чітко описує такі структури.

Це ви пропунуєте спосіб описання контракту для апі, як вирішення проблеми

є потреба запитувати тільки ті дані, які необхідні в цьому контексті;

?

Що що... Далі вигадувати аналог функціональності, що вже є у GraphQL. Але без підтримки існуючого тулінгу, інтроспекцій, типізації. А може й усім цим, але витратити людино-роки на імплементацію.

А може й усім цим, але витратити людино-роки на імплементацію

які ще роки?
роки на розробку GraphQL — так.
А для розробки конкретних речей для конкретного проекту — ніяких років у цьому моменті не треба.
не більше ніж резолвер для своїх даних.

Але без підтримки існуючого тулінгу, інтроспекцій, типізації

для API на HTTP — стільки всього понаписано...
якого тулінга не вистачає? що є в GraphQL

Роки на GraphQL вже витрачени, причому іншіми людьми.

А де є якийсь готовий тулінг для цих include/exclude. Воно якось не дуже ідіоматично для якогось REST, чи може якогось іншого підходу. Навіть як, скажимо, банально описати цю схему з використанням того ж openapi? Хіба що описати всі поля що повертаються як опціональні і десь у текстовому описі написати — що ось це повертається якщо воно є у include і немає у exclude. А як описати — якщо якісь дані і так є опціональними (ну, скажемо вони лежать у колонці бази яка може бути null) але наявність такого поля також залежить від того чи є воно в тих include/exclude.
Але ж це невірно. По факту — схема даних що повертається є динамічною — вона залежить від вхідних параметрів. У GraphQL — це явно описано у стандарті.
Ще один кейс — що робити якщо для обчислення даних для якогось поля з тих, наявність якого залежить від того чи є вони у include, потрібен ще додатковий параметр? Як описати що якщо у include є значення abc — то додатково потрібно передати abcParam1 та abcParam2?
Як описати що написати include=abc.def можна тільки якщо також є include=abc?

Перш ніж почати обробляти запит на сервері — непогано б спершу валідувати що нам прийшло щось що взагалі має сенс в рамках нашго домену. Якщо в нас є якийсь більш менш розвинений idl — на якому можна описати подібні обмеження — то така валідація відбудеться ще до того як дані потраплять в наш код на обробку. Якщо такого немає — то подібні валідації потрібно буде писати у власному коді — а це час на розробку, підтримку, потенційна можливість наробити там помилок.

Ну і те саме — для відповіді від серверу — якщо у коді серверу десь помилка і він не повернув якесь поле, яке мав би, виходячи з запиту (воно є у include і якщо воно там є — то воно не опціональне) — то непогано було б вивести помилку і нічого не робити, причому з достатніми деталями про те що саме пішло не так. У випадку GraphQL — знов таки — подібні помилки будуть відстежені ще у лібі. У власному варіанті — будь ласка напиши таку валідацію сам.

Як приклад — можемо порівняти GraphQL та gRPC. У GraphQL — якщо щось неопціональне — то це описано явно. У gRPC (protobuf версій 3) всі нескалярні поля — неявно optional. І реально видно — що коди обробки обростає такими перевірками — що якесь поле за бізнес моделлю є обов’язковим — то воно реально було передано. І якщо його немає — то потрібно сигналізувати помилку і далі нічого не обробляти. У GraphQL — наш обробник — вже типізований, поле яке обов’язково — вже точно має значення, кейс коли його немає — оброблений у коді ліби.

Ну тобто штуки типу того ж GraphQL починають стріляти коли наші вимоги вже не влазять у простий REST (ну тобто у нас щось типу DSL у API). Зрозуміло, що GraphQL — теж не все перекриє, бо хотілки замовників — нескінчені. Але краще ж стартувати з чогось, ніж з голого HTTP API.

А де є якийсь готовий тулінг для цих include/exclude

А він не потрібний якийсь особливий.
Існуючий — прекрасно працює з аргументами у урлі.

І, і сам робив, і читав, що свій тулінг швидко з’являється, замість універсального.
Причому — такий вузько спеціалізований тулінг пишеться не важко.
Важко писати — універсальний. А спеціалізований — набагато простіше.

Залежить звісно від тулінгу, і причин чому універсальний не підходить.

Воно якось не дуже ідіоматично для якогось REST, чи може якогось іншого підходу

Як і паджинція.

Про сам «ідіоматичний REST» вже писав. Хочете упоротись щоб потім вирішувати типові проблеми — то мені для цього 3 ендпойнти смикати??? Вперед, за православіє і Бога Єдинаго!
Причому якщо ваш API публічний — то вам все одно напишуть оте: — то як в вашому довбаному API отримати ... — 3 ендпойнти смикати???

Навіть як, скажимо, банально описати цю схему з використанням того ж openapi?

Там не має можливості описувати аргументи в урлі?

А як описати — якщо якісь дані і так є опціональними (ну, скажемо вони лежать у колонці бази яка може бути null)

По перших — з чого це згадується колонка бази данних. Це протікання абстракції обов’язкове? Не прибивайте цвяхами доменні моделі і персистентні — і не буде цієї проблеми.
По другє — якщо у відправляємих даних не можуть бути null — то це відповідальність відправлюючого. Нехай відправляє те що обіцяв.
По третє —
type: string
nullable: true

але наявність такого поля також залежить від того чи є воно в тих include/exclude.

Якщо воно може бути — надіслано, то воно й описується.
Якщо в include/exclude вказано поле про яке ніде в опису API не згадується... то це помилка запиту.

По факту — схема даних що повертається є динамічною — вона залежить від вхідних параметрів

Скільки б вона не була дінамічною — вона не може надати можливість запитувати об’єкти які ніде не описані, з полями — які теж невідомі бекенду.

Ще один кейс

І так стопіцот разів.

А підхід розрахований не на створення універсального рішення для всіх кейсів, а для реалізації конкретних кейсів, у конкретному проекті.
І не вимагає в include/exclude описувати поля об’єктів. Там можуть буть назви груп полів і їх формату відразу. Щоб не писати оте

abc.def

І для вкладених колекцій теж. І є інші можливості для складних кейсів, тіпа аргументів mode, case

Але точно — не треба вирішувати проблеми всіх кейсів світу. У вас є — конкретний проект, конкретного домену, от це API і створюємо.
А не інструмент для створення API :)

Перш ніж почати обробляти запит на сервері — непогано б спершу валідувати що нам прийшло щось що взагалі має сенс в рамках нашго домену

Взагалі то валідація вхідних даних must have у пристойному проекті, щоб це згадувати.

Ну і те саме — для відповіді від серверу — якщо у коді серверу десь помилка і він не повернув якесь поле

Це робиться набагато рідше. Бо зазвичай то тести повинні відловити.
Ну і якщо поля немає — то зазвичай сувора помилка якась.
Але — ніхто не забороняє написати валідацію відповіді.

то подібні валідації потрібно буде писати у власному коді — а це час на розробку, підтримку, потенційна можливість наробити там помилок.

Ніхто не заважає наробити таких же помилок у резолверах для GraphQL

Ну тобто штуки типу того ж GraphQL починають стріляти коли наші вимоги вже не влазять у простий REST

у простий REST мало що влазить.
Тому й давно виник спосіб що загадав.

Сам я давно відвмовився від «простого REST». Він зі світу рожевих поні.
Тому — або згаданий мною підхід, або GraphQL, або якийсь варіант RPC і т.п. «простий REST» в мому списку відсутній.

Зрозуміло, що GraphQL — теж не все перекриє, бо хотілки замовників — нескінчені

Наврядчи вони вийдуть за межі домену. І наврядчи кожна хотілка потребуватиме змін в API.

Головне ось що:

Коли нам треба якась функціональність, постійно виникає питання:
Чи взяти для цього лібу, фреймворк
Чи написати її самим.

І загальної відповіді на це не має. Бо взяти лібу, а особливо фреймворк — це взяти не просто код, а взяти ідеологію, філософію цієї ліби. І виконувати й надалі вимоги взятого. І всі її — недоліки теж тягнути придется надалі.

Вибір треба робити — по конкретній ситуації.
Без конкретики — будемо розглядати усі кейси світу.

type: string
nullable: true

А тепер те саме, але якщо наявність цього філда — залежить від того чи він є у include в запиті. Вони і так мусять бути nullable — у схемі, бо можуть бути присутні або ні в заледності від того що прийшло в запиті. А от сказати що якщо він є у include — то він обов’язково не null, або навпаки — сказати що навіть якщо він є у include — то це не гарантує що він прийде, бо може бути null — вже нема де. Хіба що у текстовий коментар.
В GraphQL — таке можливо, в gRPC та openapi — ні. Значить така валідація — перекочовує у кастомний код. Це я до того — що GraphQL розроблявся не на пустому місці — у тому числі щоб вирішувати такі проблеми. Тобто по стандарту — видно — які конкретні кейси воні вирішували, які — ні.

Взагалі то валідація вхідних даних must have у пристойному проекті, щоб це згадувати.

Ну от частину проблема валідації в GraphQL і вирішили — щоб не робити цю частину кожного разу. Але це все нескінчено можна обговорювати — це та сама суперечка про статичну vs динамічну типізацію у мовах, але у контексті API. Чим динамічніша система типів — тим більше тестов та рантайм перевірок потрібно писати. Що розвиненіша система типів (чи схема rpc у контексті API) — то більше цього можне перекласти на компілятор/лібу rpc (але так, доведеться витратити деякий ресурс на те щоб ці переваги використати).

залежить від того чи він є у include в запиті.

ні. include exlclude це взагалі про інше, про додаткові колекції даних, а не про керування полями.

Вони і так мусять бути nullable — у схемі,

Або у конвенції — кожне поле не відповідающе за ідентифікацію об’єкта даних може бути відсутнім, або null

Ну от частину проблема валідації в GraphQL і вирішили — щоб не робити цю частину кожного разу

Переважна більшість проектів і близько немає проблем які вирішували у FB і тому створили собі GraphQL.

Далі, поле fields може вказувати не назву поля, а групу полів в залежності від наявних сценаріїв у конкретному проекті.
Тобто воно може вказувати на bounded context. Відповідати не не питання які поля надати, виключити
а на питання — навіщо треба об’єкти такого-то доменного типу, для якого використання.

Далі, можна навіть порахувати скільки «коштує» передача зайвих даних у конкретному проекті, щоб вважати це проблемою яку слід вирішувати. У FB це реальна проблема, тому там треба такє тонкє керування відповіддю сервера. а у проекті X? Треба її вирішувати, пораховані «збиткі» від неї?

Чим динамічніша система типів — тим більше тестов та рантайм перевірок потрібно писати.

Проблема типів на рівні кодування сама маленька у складному проекті.

то більше цього можне перекласти на компілятор/лібу rpc

Сама головна проблема — помилкі у логіці відповіді. Коли якись дані не приходять, або приходять зайві. І це перекласти на компілятор чи лібу можна тільки у мріях академічних функціональщиків.

але так, доведеться витратити деякий ресурс на те щоб ці переваги використати

І чи будуть вони взагалі, ці переваги, навіть якщо безкоштовні.

Загалом стаття хороша. Нажаль не стикнувся з такою коли вперше на проекті з’явився graphql. Пройшовся тоді по бвгвтьом граблям.

GrahpQL це мабуть одна з найгірших хєровин яку можна принести в проект.

Дякую за коментар! Чи можете більше розкрити свою думку? На моєму досвіді з GraphQL все було дуже добре

Чи можете більше розкрити свою думку?

Можу.

Моя думка не базується на метриках, чи дослідженнях, а є виключно суб«єктивною і заснованою на моїх роздумах щодо концепції графкл і трошки на практиці.

Отже, чому графкл це всрата шляпа.

По-перше, тому що для неї придумали жсоно-подібний-не-жсон. Це убого. Документи графкл зі складністю вищою за хеловорлд важкочитабельні

По-друге, тому що сама концепція цієї технології починаючи з самої ідеї ущєрбна* і однобока — давайте ми зробим протокол який буде розрахований виключно на комфорт і потреби фронта бо на бек розробників нам пахую, а як він буде працювати на бекенді нас не їбе вапще, їбіться самі якось самі придумайте.

По-третє, ущєрбність самої думки про відсутність схеми і «я получаю тіко те що я хочу». Пахне якимось інфантилізмом в стилі узьких наротних вологих казок про іванушку-дурачка якому в житті все щука зробила. Графкл — це аналог такої щуки тільки в світі протколів «я фронтенд дівєлопер, я ніхочу в дизайн взаємодії бека і фронта я хочу плюшевого тіраназавра все разом одним запитом тутже сразу дайтє мнє»

Концепція технології зводиться до того, щоб максимально спростити роботу фронтенда ціною пахую ваще різкого і неконтрольованого ускладнення логіки роботи бекенда та і ще раз пахую можливого викривлення схем зберігання даних для адаптації під ускладнену логіку обробки запиту.

На моєму досвіді з GraphQL все було дуже добре

Це може бути з двох причин, або А) якщо ви чисто мобайл-фронтенд, то у вас однобокий досвід, отриманий з тієї сторони, до якої графкл повернутий рожевим боком з понями і радугою. А на бекенді досвід часто зводиться до такого, як вказали нижче:

А часто буває так, що додаєш одне додаткове поле до респонзу і відповідь приходить не за кілька секунд, а за 30-60 секунд.

і така ситуація виникає не тому що у бекендерів руки криві, а через концепцію технології, яка змушує бекенд працювати за контрінтуітивними сценаріями.
...або Б) у вас адіщенськи примітивна схема даних

* русизм але я не можу підібрати українського слова з таким же спектром емоцій

На мобайл клієнті використання графкла ніяк не спрощує роботу. А в моєму кейсі тільки додає додаткового гемору

По-перше, тому що для неї придумали жсоно-подібний-не-жсон

Походу я мазохіст. Мені подобається цей не-жсон. Лаконічний і зрозумілий.

По-друге і по-третє, ми зробим протокол який буде розрахований виключно на комфорт і потреби фронта

З моєї практики якраз бекенд стає більш лаконічним і простіше перевикористовувати компоненти. Так наприклад, у нас є user, product, message. У них всіх є media. Для REST це /users/{useId}/media, /products/{productId}/media, /messages/{messageId}/media. Для GraphQL це буде поле media в моделях user, product, message. Потім нам треба буде зробити ендпоїнт зі списками /users /products /messages. Тут починаються пляски з тим як дістати media для них. Робити кучу реквестів? Добавляти поле в модель? А якщо десь буде список де media не потрібен? Робити ще одну пачку endpоint-ів? В GraphQL того факту що модель має поле media достаньо. Коли піде реквест, то викачається список юзерів, з них візьмиться mediaId і одиним запросом із списком media ids сходить в базу. Це все не покидаючи бека. Тільки після цього уже об’єднаний респонс буде відправлений на фронт. Якщо нам треба список без медія, то ми її не питаємо і медія не грузиться.

Для REST це /users/{useId}/media, /products/{productId}/media, /messages/{messageId}/media

а не треба було упаруватись чистотою REST :)

це ж класика — спочатку зробити незручно, а потім дивуватись — а чого ж воно так незручно, га?
зробити собі проблему, а потім мужньо страждати —

як дістати media для них. Робити кучу реквестів?

Я тут описав канонічний приклад) Навіть якщо не упоруватися в чистоту, які варіанти? Кожен раз тягнути медію навіть якщо вона не потрібна? Зробити декілька ендпоїнтів під кожен кейс?

Навіть якщо не упоруватися в чистоту, які варіанти?

відмовитися від упоротості REST

і додати у запити необхідні параметри для отримання — декількох колекцій відразу.
на беку — драфтовий варіант обробки — викликаєм написаний код для отримання обробки цих трьох колекцій.

Зробити декілька ендпоїнтів під кожен кейс?

нафуя?

«додайте до вашої функції — аргументи»

function getSomething()

ну то додайте function getSomething(args, opts, params)

а не робіть її копіпасту
function getSomethingSpecial()

Схоже що з таким підходом ми реалізуємо GraphQL через пару років)

ні. бо проект наврядчи виросте до потреб такого узагальнення його домену і даних, щоб потребувати інструмент такої універсальності як GraphQL

а от до варіанта аля DSL так, може й вирости.

> які варіанти?

/products/?with-media=true

Потім нам треба буде зробити ендпоїнт зі списками /users /products /messages. Тут починаються пляски з тим як дістати media для них.

Пляски починаються тому, що сама задача потребує певних свідомих компромісів. Одним запросом це можна вигрести тільки в випадку прикладів рівня хеллоу ворлд. Якщо ж списки юзерів/продуктів потенційно великі, і настільки ж потенційно великі списки медіа до кожного, то про «один запит» можна забути безвідносно того це gql чи олдскул.

Якщо нам треба список без медія, то ми її не питаємо і медія не грузиться.

Що на практиці означає, що кешування на бекенді в загальному випадку можна практично забути (адже кожний окремий запит може вимагати майже рандомну комбінацію атрібутів)...

настільки ж потенційно великі списки

дефолтно ставиться максимальний розмір. треба більше — паджинація.

Що на практиці означає, що кешування на бекенді в загальному випадку можна практично забути

на практиці кешування лягає ще й як класно. звісно, воно робиться не на ендпойнт.
а на ті функції які отримують данні.

дефолтно ставиться максимальний розмір. треба більше — паджинація.

Для топ левел колекції це no-brainer незалежно від того це GQL чи рест, а як щодо вкладених? Скажімо, в нас є мілліон юзерів, в кожного в середньому по кількасот медіа (але лімітів немає, скільки зможеш стільки й завантажуй). Окей, з пагінацією юзерів все зрозуміло — припустимо, макс. дозволяємо 100, як щодо медіа? Використовуємо той самий ліміт? Фіг там, бо розмір пейлоаду улітає в космос. Обмежуємо якимось притомним числом (скажімо, по дефолту повертаємо останні 10 медіа, треба більше — окремий запит)? Окей, але це абсолютно нівелює аргумент про «один запит» — ми маємо приблизно такі ж самі костилі закреслено компроміси, які мали б за умови правильно спроектуваних типу-рест ендпоінтів. Чи в випадку GQL є якісь красивіші рішення?

кешування лягає ще й як класно. звісно, воно робиться не на ендпойнт.
а на ті функції які отримують данні

Витягти дані — це тільки частина задачі, потім їх ще потрібно зібрати в агрегати й серіалізувати (не дуже дешева операція так-то). Для класичних рест ендпоінтів можна кешувати як агрегати (бо ж контракт прибитий цвяхами), так і навіть серіалізований пейлоад іноді.

а як щодо вкладених?

Залежить від обставин, але — вкладені теж мають обмеження на розмір.
Викачування всієї БД зазвичай заборонене, тому що не повинно існувати такої потреби :)

озволяємо 100, як щодо медіа? Використовуємо той самий ліміт?

може й інший. може й — спец.елемент, останнім, який вказує на можливість дозавантаження. і т.і.
Залежить від проекту. Але як то кажуть:
Якщо вам треба відобразити 100к рядків у гріді на UI — то щось у вас з UI не так.

це тільки частина задачі, потім їх ще потрібно зібрати в агрегати й серіалізувати (не дуже дешева операція так-то)

Якщо ви отримуєте щось унікальне — то завжди буде cache miss

Від способа запиту цього унікального — це не залежить.

Якщо кешування серіалізованної відповіді є проблемою, то так, може тоді що смикати три ендпойнти — дешевше вийде для системи.
Або — змінювати формат відповіді — щоб можна було її збирати з серіалізованих значеннь.
Треба рахувати.

Всякі варіанти бувають.

Але

Для класичних рест ендпоінтів можна кешувати

я частіше зтикався з проблемою що — клієнту важче самому агрегувати 3 відповіді, чим бекенд те зробить.

Це причина навіть на бекенді — коли ми навантажємо БД, створюємо ХП, бо вийде швидше ніж аплікація витягне трьома запитатами що їй треба, і буде робити filter map reduce сам, а не сервер БД це зробить.

Не від великого заводоволення формують самі величезні SQL запити і пишуть на PL/SQL.

Ну а кешування... якщо в вас 10 користувачів — то чим воно допоможе знизити навантаження на серіалізацію?
А якщо 10к відразу — то як так виходить що вони всі шлють унікальні запити?

адже кожний окремий запит може вимагати майже рандомну комбінацію атрібутів

І це також одна із найважливіших і в той же час найгірших особливостей графкл

У них всіх є media.

Вичерпна характеристика структури і сутності даних (ніт) :)

У них всіх є media. Для REST це /users/{useId}/media, /products/{productId}/media, /messages/{messageId}/media.
Для GraphQL це буде поле media в моделях user, product, message

Ну це якщо руки криві і голови на плечах немає, чистота тут ні до чого.
І по всіх твоїх питаннях, а саме:

Робити кучу реквестів?
Добавляти поле в модель?
якщо десь буде список де media не потрібен?

Треба виходити з природи сутності медіа, яку ти ввів, але не описав. І з природи взаємодії користувача із цими «медіа», не знаючи юзкейсів і сценаріїв, що воно то «медіа» значе, я не можу робити будь-яких висновків про те, як будуть виглядати операції з ними.

В GraphQL того факту що модель має поле media достаньо.

Ну да, да, це якраз яскрава ілюстрація про той ущєрбний підхід, про який я говорив:

я ніхочу в дизайн взаємодії бека і фронта я хочу плюшевого тіраназавра все разом одним запитом тутже сразу дайтє мнє

Тобто, ти не хочеш думати про взаємодію сутностей і напрацьовувати не-конфліктну взаємовигідну схему обміну даних фронта і бека, поділену на юзкейси і сценарії, тобі глибоко пофіг що там на бекенді, тобі треба всього зразу, щастя задарма для всіх і щоб одним запитом.

Коли піде реквест, то викачається список юзерів, з них візьмиться mediaId і одиним запросом із списком media ids сходить в базу. Це все не покидаючи бека.

Ти оце так описав це мені нагадало жартівливий відосик з діпфейком де безугла вчить Залужного як воювати «ну там самольотікі літять бах бах піхоти біжуть кулеметами тра-та-та отак треба робити контрнаступ».
На практиці все шушуть трошки складніше получається. Точніше не получається, а получаються милиці, як вже тут писали «додав поле в респонс і чекаєш 30 секунд». Бо структура запиту і резолвери які розпідарашені по кускам бекенду не адаптовані до такої псевдо-гнучкості.

І це ще я не розписував, що вся проблематика про юзерів, продкути, месаджі і меді трохи висмоктана з пальця і елементерно вирішується рестом, але при умові, що сценарії використання даних продумані.

Спробую вгадати: на проекті натягнули GraphQL штучно. Наприклад, на сервіс, який раніше був REST/JSON/SOAP щось таке?

Зараз використовуємо GraphQL. Мобільні додатки на нашій стороні, бекенд на стороні навіть не клієнта, а третьої компанії. Важка комунікація, відсутність доки і схема десь на 30к рядків. То виглядає все так, що просто логіку з бека скинули на сторону мобільних апок. А часто буває так, що додаєш одне додаткове поле до респонзу і відповідь приходить не за кілька секунд, а за 30-60 секунд. Ну і ще момент — в GraphQL всі відповіді з кодом 200, навіть якщо помилка. Тому хендлити це все потрібно по-іншому. Так, звичайно мій кейс — це поганий дизайн бека і всі проблеми звідси. Але і до цього як працював з нормальним беком на GraphQL, то мені як андроід розробнику це не було комфортно, більше подобається REST. Про grpc тільки читав, використовувати не доводилося.

Дякую за коментар!
Так, у GraphQL трішки інший підхід до оброблення помилок. Він не використовує HTTP коди, а просто в response відправляє поле к помилкою. Не вважаю це мінусом, це просто інший підхід.
Наталь є проблема, що не усі правильно проектують GraphQL API, а роблять його на кшталт JSON API, тому і виникають подібні проблеми.

GraphQL:
(1) хочеться легко і швидко тестувати бекенд через GraphQL Playground;
(2) є потреба запитувати тільки ті дані, які необхідні в цьому контексті;
(3) realtime спілкування;

Трохи додам із власного досвіду.
(1) GraphQL Playground це доволі примітивний інструмент. У пошуках нормального я свого часу зупинився на Insomnia. Це такий собі аналог Postman, але з прокачаною підтримкою GraphQL. Цілком можливо що зараз Postman теж підтягнувся.
(2) Щоб ця перевага працювала, потрібен правильний дизайн API і бека який за ним стоїть. А це, як виявилося, не так просто і не на всі задачі нормально лягає. Бо якщо ні, то ми матимемо такий собі ускладнений варіант JSON API.
(3) Підписки з коробки це цікаво і приємно, але далеко не завжди потрібно.

так, постман вже нормально працює з graphql

Дякую за коментар!
Стосовно GraphQL Playground, насправді, є різні реалізації плейграунду. На мою думку Apollo зробили його набагато кращим, в роботі тільки його і використовував.
Стосовно дизайну API. Так, правильний дизайн для GraphQL API дуже важливий і його треба вміти проєктувати. Якщо ж просто повторювати JSON API, то вийде багато бойлерплейту, який можна було б і не проєктувати, але навіть в такому випадку перевага з непотрібністю написання документації є вирішальною, як на мене.
Стосовно підписок. Насправді, як iOS Dev скажу, що дуже часто тобі простіше розробити фічу, використовуючи підписку, бо так не треба про думати флоу і рефрешити дані на кожну зміну в навігації (або робити event bus). Вебсокет в такому випадку є рішенням, але його треба сетапити, підтримувати і обробляти проблеми з коннекшеном. Тому думаю, що це жирний плюс до GraphQL.

Дуже компактно і якісно описано, однак синтаксис свіфту залишається таємницею моментами.

Зазвичай клієнт і сервер — це просто два комп’ютери, які мають публічну IP-адресу. Через протокол транспортного рівня TCP (Transmission Control Protocol) клієнт відправляє серверу request, який реалізує протокол HTTP. Сервер обробляє відправлену інформацію і відправляє клієнту назад response, також під протоколом HTTP.

HTTP/3 is the third major version of the Hypertext Transfer Protocol used to exchange information on the World Wide Web, complementing the widely-deployed HTTP/1.1 and HTTP/2. Unlike previous versions which relied on the well-established TCP (published in 1974),[2] HTTP/3 uses QUIC, a multiplexed transport protocol built on UDP.
en.wikipedia.org/wiki/HTTP/3

Підписатись на коментарі