Про GraphQL на клиенте

в 13:58, , рубрики: api, Apollo, graphql, graphql-client, высокая производительность, Разработка веб-сайтов

На текущий момент GraphQL все больше распространяется в энтерпрайзе. И это не удивительно - изящный синтаксис запросов, типизация, ускорение разработки и это далеко не все его плюсы использования.

Наша небольшая команда уже больше года использует его во всех проектах, и скажу вам: мы испробовали большинство популярных библиотек на клиентской стороне и с ними не все так гладко как хотелось бы.

> Думаю, что стоит сделать небольшую ремарку относительно того, кому подойдет эта статья. Если для вас критично держать размер конечного бандла добро пожаловать под кат.

Но обо всем по порядку.

Про GraphQL на клиенте - 1

Типичный стек

Apollo Client - это не просто библиотека запросов, но и библиотека управления состоянием, с встроенным кэшированием. По заявлению разработчиков помогает структурировать код экономичным, предсказуемым и декларативным способом, который соответствует современной практике разработки. Основная библиотека @apollo/client обеспечивает встроенную интеграцию с React.

GraphQL Code Generator - утилита для генерации готового для использования кода. Она имеет плагин для @apollo/client, что позволяет без лишних затрат времени сгенирировать хуки для ваших запросов.

Для крупных приложений дополнительно используются стейт-менеджеры, такие как Mobx, Redux, Recoil и т.п.

Что мы получаем по итогу: сгенирированные хуки для запроса данных и дополнительное хранилище для данных приложения. Казалось бы, что могло пойти не так?

Что под капотом

Допустим у нас есть какой-нибудь запрос:

query getMe {
  me {
    id
    balance
  }
}

Соответственно, для него генерируется:

const defaultOptions = {};

export const GetMeDocument = gql`
query getMe {
  me {
    id
    balance
  }
}
`;

export function useGetMeQuery(baseOptions?: Apollo.QueryHookOptions<GetMeQuery, GetMeQueryVariables>) {
  const options = {...defaultOptions, ...baseOptions}
  return Apollo.useQuery<GetMeQuery, GetMeQueryVariables>(GetMeDocument, options);
}

В запросе на сервер отправится такая структура:

{
  "operationName": "getMe",
  "query": "query getMe { me { id balance } }",
  "variables": {}
}

Фактически наш запрос сформированный на языке GraphQL в исходном виде помещается в JSON в поле query... Уже чувствуете подвох?

Что такое gql

gql в сгенерированном коде, это функция из библиотеки graphql-tag от Apollo, которая строит синтаксическое дерево из вашего запроса в рантайме и отдает ее Apollo Client, который в свою очередь обратно собирает её в строку. Проще говоря, это нужно для синтаксической валидации запроса.

Только вот при генерации кода graphql-codegen уже проверил наш запрос по схеме с сервера, соответственно мы выполняем двойную работу, да к тому же с оверхеадом в рантайме.

Дополнительно предоставляется поле `operationName` из нашего запроса. В спецификации GraphQL сказано, что оно нужно для кэширования и логирования на сервере, но загвостка в том, что на сервере мы разбираем `query`, чтобы понять что клиенту нужно вернуть, соответственно мы можем получить его непосредственно из самого запроса. В реализации кэширования предполагается, что все поля запроса будут хэшированы, и им будет назначен соответствующий ответ. Из этого делаем вывод, что `operationName` не такое уж и важное поле, благо по спецификации мы можем передавать в него `null`.

Получается в общем-то нам не нужен graphql-tag, и мы можем передавать запрос as-is.

Роль Apollo Client

Apollo Client несомненно предоставляет широкие возможности для запросов, включая кэширование, но польза этого самого кэширования нивелируется тем, что полученные данные мы так или иначе кладем в каком-нибудь виде в стейт-менеджер. Соответственно, все для чего он нужен - это отправить сформированную JSON на сервер, получить данные и обновить компонент.

Картинка с сайта ru.meming.world
Картинка с сайта ru.meming.world

Какой ценой

По данным bundlephobia.com:

Библиотека

min+gzip

graphql

8.9kB

graphql-tag

1.1kB

@apollo/client

32kB

итого

42kb

Подумайте только, мы добавили 42kB рантайма для того, чтобы получить с сервера данные. С другими популярными библиотеками, такими как urql или react-query ситуация схожая.

Пища для размышления

GraphQL — отличный инструмент, но нам надо переосмыслить его использование, например, по максиму использовать возможности кодогенерации и минимизировать рантайм на клиенте. Но об этом как-нибудь в другой раз.

Автор: Антон Петров

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js