HabHub icon indicating copy to clipboard operation
HabHub copied to clipboard

Что не так с GraphGL

Open nin-jin opened this issue 4 years ago • 4 comments

Денормализованная выдача

Выдаётся всегда дерево в денормализованной форме. Если в графе есть циклы, то выдача может легко увеличиться на порядок за счёт дублирования поддеревьев.

Представление данных в виде слепков

Это несовместимо с с бесконфликтными алгоритмами слияния, так как теряет мета-информацию об изменениях.

Нестандартизированные параметры

GQL - это просто RPC. offset, limit, filter, sort и прочее реализуется всеми по разному. Из-за чего нельзя написать универсальный инструмент работы с разными АПИ.

DevTools браузера мало полезны

Все запросы POST-ом (или экранированным GET), все ответы с кодом 200. Соответственно браузер никак не помогает всё это дебажить.

HTTP кеширование не работает

RPC в принципе не кешируется. В лучшем случае кеширование реализуется клиентской библиотекой. Кеширование на прокси идёт лесом.

Много сложностей

Все эти типы, фрагменты, мутации и пр на собственном ограниченном языке, требующем писать много кода для простых вещей. Дженерики? Плиморфизм для мутаций? Переиспользование кода между запросами и мутациями? "Вам это не нужно". Без библиотек и спец-инструментов использовать очень сложно.

Нет загрузки файлов

Разве что через упаковку их в BASE64 и посылки текстом.

Потенциальный DOS

Легко простым запросом выгрузить на клиент всю базу, что положит сервер. Дизайн запросов такой, что сложно сделать ограниченное число запросов к базе на запрос к северу. А неограниченное число запросов к базе быстро убьёт производительность. Расстановка индексов становится нетривиальной задачей.

Кривой nullable

Все поля nullable по умолчанию. При этом невозможно отличить null от отсутствия поля, что важно при мутациях, чтобы не стирать данные, если поле не передано.

nin-jin avatar Sep 22 '19 16:09 nin-jin

Про загрузку файлов можно выкинуть. По крайней мере если исходить из парадигмы использования GraphQL over HTTP(S) https://blog.apollographql.com/file-uploads-with-apollo-server-2-0-5db2f3f60675

izatop avatar Sep 22 '19 17:09 izatop

HTTP кеширование не работает

GraphQL не про интерфейс и протокол, это про структуру данных. Как реализуют клиенты кэширование вообще отдельная тема. При необходимости можно даже HTTP кэш накинуть. Но в целом это решается через механизмы кэширования на клиенте или в WW.

izatop avatar Sep 22 '19 17:09 izatop

Потенциальный DOS

Есть уже способы работы со сложными системами на GraphQL чтобы учитывать query complexity, например https://github.com/slicknode/graphql-query-complexity

izatop avatar Sep 22 '19 17:09 izatop

Накину про серверсайд gql на ts:

  1. Отдельные реализации типов для аргументов-объектов (GraphQLInputObjectType), в которых не работают юнионы. Обсуждали 4 года https://github.com/graphql/graphql-spec/issues/488 и родили 15й стандарт: https://github.com/graphql/graphql-spec/pull/825 который еще 5 лет будут обсуждать, т.к. идейный вдохновитель ушел из facebook).
  2. GraphQLEnumType в typescript выводится как any: https://github.com/graphql/graphql-js/blob/main/src/type/definition.ts#L1362 class GraphQLEnumType /* <T> */ {, сложно использовать в аргументах, тем более что values может маппить как угодно входные значения
  3. Проблема курицы и яйца относительно типов ts на сервере, когда на ts пишется query, у него есть gql рантайм тип аргументов, но ts-тип этих аргументов не выводится сразу и в resolver приходится писать any. graphql-js преобразует gql-рантайм типы в schema.graphql, graphql-code-generator генерит из нее ts типы и уже эти сгенеренные типы вместо any пишутся в ресолвере.
  4. В случае юниона в респонсе, если в query перечисленны не все типы в юнионе, то сгенерится обопщенный ts-тип, где __typename: string: https://github.com/relay-tools/relay-compiler-language-typescript/issues/190
  5. Рекурсия - боль, например, что б в gql сгенерить такое: type Offer = { id: string; related: Offer[] }, надо отдельно вынести все поля без related в GraphQLObjectTypeConfig, создать OfferBase с этими типами без related и Offer с related.
  6. Типы не расширяются, т.к. описываются как объекты, а не классы, в публичном интерфейсе не представлен config типа, что б расширить, приходится его описывать отдельно и через спред генерить новый тип
  7. Автовывод ts-типов слабо задействован, постоянно приходится объявлять тонну генериков: new GraphQLObjectType<Snippet | Card, GraphqlContext, Args>
  8. В массивах элементы по-дефолту nullable. Надо постоянно писать new GraphQLList(new GraphQLNonNull(SomeType))
  9. если сделать в ресолвере Promise.resolve(new Error) (заметьте, не reject), то gql сконвертит это в throw. https://github.com/graphql/graphql-js/blob/29bf39faa670effd3c1561a1512ec7767658a63b/src/execution/execute.ts#L616
  10. Про дедупликацию json-а: есть стороннее решение: https://github.com/gajus/graphql-deduplicator, однако в апстриме подобного нет и вряд ли будет в ближайшем будущем (т.к. это потребует изменить протокол) https://github.com/graphql/graphql-js/issues/150#issuecomment-206719291
  11. Неоднозначность в обработке ошибок. Если в query type описан, как non-nullable: new GraphQL.GraphQLNonNull(SomeType), то при его падении, весь запрос, который включал эту и другие квери, валится. При этом, без NonNull, вернется, то, что удалось загрузить и отдельно поле с errors.

zerkalica avatar Dec 01 '21 16:12 zerkalica