[Перевод] Визуализация концепций GraphQL

внешний интерфейс Программа перевода самородков GraphQL

Визуализируйте концепции GraphQL

Мы будем использовать диаграммы для визуализации ментальной модели GraphQL.

GraphQL часто объясняется нами как «унифицированный интерфейс для доступа к данным из разных источников». Хотя это объяснение правильное, оно не раскрывает природу и мотивацию GraphQL и почему он называется «GraphQL» — просто потому, что вы видите звезды и ночное небо, не означает, что вы видите «звездную ночь» ( один из шедевров художника Ван Гога).

Я думаю, что настоящее ядро ​​Graphql лежит в применении графов данных. В этой статье я введем график данных приложения, обсудим, как GraphQL выполняет операции запросов на графике данных приложения, и как использовать структуру дерева запросов GraphQL, чтобы кэшировать их результаты запроса.

Обновление от 07.02.2017: Теперь вы можете увидеть содержание этой статьи в видео ниже.

Ментальная модель для GraphQL — Дхайват Пандья

график данных приложения

Многие данные в современных приложениях могут быть представлены с помощью графов с узлами и ребрами, где узлы представляют объекты, а ребра представляют отношения между объектами.

Например, мы создаем систему каталогов книг для библиотеки. Проще говоря, у нас в каталоге куча книг и авторов, и у каждой книги есть как минимум один автор. В дополнение к этому есть соавторы, когда автор написал книгу или книги в соавторстве с другими.

Если мы визуализируем эти отношения в виде графика, они выглядят так:

Диаграмма представляет отношения между нашими различными частями данных и объектами, которые мы пытаемся представить, такими как Книга и Автор. Почти все приложения работают с таким графом: они считывают данные с графа и записывают в него. На этом графике появляется GraphQL.

В GraphQL мы можем извлечь дерево из графа данных приложения.

Вы можете быть сбиты с толку, когда услышите это, но давайте объясним, что это значит. По сути, дерево — это граф с начальной точкой (корнем) и атрибутами. Одним из его свойств является то, что его нельзя провести по краю узла пальцем, а затем обратно к тому же узлу, то есть он не имеет петель.

Обход графа данных приложения с помощью GraphQL

Давайте рассмотрим пример запроса GraphQL, чтобы понять, как он «извлекает дерево» из графа данных приложения. Это код запроса GraphQL, соответствующий приведенному выше графу данных нашего приложения системы каталога книг, как показано ниже:

query {
  book(isbn: "9780674430006") {
    title 
    authors {
      name
    }
  }
}

После того, как сервер проанализирует запрос, он вернет этот результат запроса:

{
  book: {
    title: “Capital in the Twenty First Century”,
    authors: [
      { name: ‘Thomas Piketty’ },
      { name: ‘Arthur Goldhammer’ },
    ]
  }
}

Вот как это выглядит на графике данных приложения:

путь запроса

Давайте посмотрим, как эти данные извлекаются из графика с помощью запроса GraphQL.

В GraphQL мы можем определитькорневой тип запроса(Назовем это RootQuery), этот тип определяет, где должен начинаться запрос GraphQL при обходе графа данных приложения. В нашем примере мы начинаем с узла «Книга», который использует свой номер ISBN, который является полем запроса.«книга (isbn: …)»Выбрано. Затем запрос GraphQL проходит по графу, отслеживая ребра, отмеченные каждым вложенным полем. В нашем запросе он передается в запросе"заглавие"Поле переходит от узла «Книга» к узлу, содержащему строку заголовка книги. Он также работает, отслеживая узел «Книга», отмеченный значком"авторы"Край поля получает узел Author и получает "name".

Чтобы увидеть, как этот результат строит дерево, просто переместите узлы, чтобы они больше походили на дерево:

Для каждой части информации, возвращаемой запросом, существует связанный с ней путь запроса. Путь состоит из полей запроса GraphQL, по которым мы извлекаем эти данные. Например, название книги «Капитал».путь запросаследующим образом:

RootQuery → книга (ISBN: «9780674430006») → название

поля в нашем запросе GraphQL (т.е.book,authors,name) указывает, какие ребра в графе данных приложения следует выбрать, чтобы получить желаемый результат. Вот откуда GraphQL получил свое название:GraphQL — это язык запросов, данные, которые проходят по древовидному графу для генерации результата запроса.

Кэширование результатов GraphQL

Чтобы создать действительно быстрое и плавное приложение, в котором пользователи не тратят много времени на ожидание загрузки анимации, мы хотим использовать кэширование для уменьшения количества запросов между клиентом и сервером.Оказывается, древовидная структура GraphQL отлично подходит для кэширования на стороне клиента.

В качестве простого примера предположим, что на вашей странице есть код, который извлекает следующий результат запроса GraphQL:

query {
  author(id: "8") {
    name
  }
}

Позже другие части страницы снова запросят этот же запрос. Если нам абсолютно не нужны последние данные, мы можем использовать данные, которые у нас уже есть, чтобы ответить на второй запрос! Это означает, что кеш должен иметь возможность разрешать запросы, не отправляя их на сервер, что ускоряет работу нашего приложения. Но недостаточно просто кэшировать точные запросы, которые мы получили ранее, мы можем сделать лучше.

Давайте посмотрим, как клиент Apollo кэширует результаты GraphQL. По сути, результат запроса GraphQL представляет собой дерево информации, сформированное из графа данных на стороне сервера. Чтобы не перезагружать их каждый раз, когда они нам снова понадобятся, мы хотим иметь возможность кэшировать эти деревья результатов. Для этого сначала сделаем ключевое предположение:

Клиент Apollo предполагает, что каждый путь в графе данных приложения (указанный запросом GraphQL) указывает на стабильный блок информации.

Если в некоторых случаях это не так (например, когда информация, на которую указывает конкретный путь запроса, очень часто меняется), мы можем использоватьидентификатор объектачтобы клиент Apollo не делал таких предположений, о которых мы поговорим позже. Однако в целом, когда речь идет о кэшировании, это разумное предположение.

Тот же путь, тот же объект

Это последнее введенное предположение «один и тот же путь, один и тот же объект» чрезвычайно полезно. Например, предположим, что у нас есть два запроса, которые запускаются один за другим:

query particularAuthor {
  author(name: "Thomas Piketty") {
    name
    age
  }
}

query authorAndBook {
  book(isbn: "9780674430006") {
    title
  }

  author(name: "Thomas Piketty") {
    name
    age
  }
}

Просто взглянув на запрос, вы можете увидеть, что второму запросу не нужно обращаться к серверу, чтобы получить имя автора. Эту информацию можно найти в кеше по результатам предыдущего запроса.

Клиент Apollo использует эту логику для удаления части запросов на основе данных, уже находящихся в кэше. Он может поддерживать этот запрос сравнения из-за предположения о пути. он предполагает путьRootQueryauthor(id: 6)nameВ обоих запросах была получена одна и та же информация. Конечно, если вы не хотите использовать это предположение, вы можете использоватьforceFetchопция, кеш будет полностью перезаписан.

Это предположение полезно, поскольку путь запроса также включает параметры, которые мы используем в GraphQL. Например:

Rootquery → Автор (ID: 3) → Имя

не эквивалентен

RootQuery → автор (id: 6) → имя

Таким образом, клиент Apollo не будет предполагать, что они представляют одну и ту же информацию, и пытаться объединить результаты одного с другим.

Используйте идентификаторы объектов, когда предположений о пути недостаточно

Оказывается, мы можем сделать лучше, чем просто следовать пути запроса от корня. Иногда вы можете получить доступ к одному и тому же объекту через два совершенно разных запроса, предоставив объекту два разных пути запроса.

Например, предположим, что у каждого из наших авторов есть несколько соавторов, тогда мы получаем доступ к некоторым объектам «Автор» через это поле:

query {
  author(name: "Arthur Goldhammer") {
    coauthors {
      name
      id
    }
  } 
}

Но мы также можем получить доступ к автору напрямую из корневого узла:

query {
  author(id: "5") {
    name
    id
  }
}

Предположим, что автор по имени «Артур Голдхаммер» и автор с идентификатором 5 являются соавторами книги. Затем мы дважды сохраним в кеше одну и ту же информацию (то есть информацию об авторе с ID 5, Томасе Пикетти).

Тогда древовидная структура кеша в кеше выглядит так:

Теперь проблема заключается в том, что оба запроса ссылаются на одну и ту же информацию в графе данных приложения, но клиент Apollo еще не знает об этом. Чтобы решить эту проблему, Apollo Client предлагает вторую концепцию: идентификаторы объектов. По сути, вы можете присвоить уникальный идентификатор любому запрашиваемому объекту. Потом,Клиент Apollo будет считать, что все объекты с одинаковым идентификатором объекта представляют одну и ту же информацию..

Как только клиент Apollo узнает об этом, он может лучше организовать кеш:

Это означает, что идентификаторы объектов должны быть уникальными во всем приложении. Таким образом, вы не можете использовать идентификатор SQL напрямую, потому что тогда идентификатор SQL автора может быть равен 5, а идентификатор SQL книги может быть равен 5. Но это легко исправить: для генерации уникального идентификатора объекта достаточно преобразовать возвращенный GraphQL__typenameПросто добавьте к идентификатору, сгенерированному серверной частью. Таким образом, идентификатор SQL5Автор может иметьAuthor:5или аналогичный идентификатор объекта.

Поддерживайте согласованность результатов запроса

Продолжая последние два запроса, над которыми мы только что работали, давайте рассмотрим, что произойдет, если некоторые данные изменятся. Например, что, если вы получите другой запрос и обнаружите, что автор с идентификатором 5 изменил свое имя? Между тем, что происходит с частью пользовательского интерфейса старого имени, используемого этим автором с идентификатором 5?

А вот и большая игра: они обновляются автоматически. Это приводит нас к еще одной функции, предоставляемой клиентом Apollo:Если значение любого узла наблюдаемого дерева запросов изменяется, запрос обновляется новыми результатами..

Итак, в этом примере у нас есть два запроса, оба из которых зависят от автора, чей идентификатор объекта — «Автор: 5». Поскольку оба дерева запросов ссылаются на свойство автора, любые обновления информации об авторе будут распространяться на оба запроса:

Если вы используете клиент Apolloreact-apolloилиangular2-apolloТакой пакет интеграции представлений не нужно настраивать: ваш компонент просто получит новые данные и автоматически перерендерится. Если вы не используете пакет интеграции представлений, то основной методwatchQueryЭто также можно сделать, это дает вам объект-наблюдатель, который будет обновляться при каждом изменении хранилища.

Иногда для ваших приложений, используйте на всех идентификаторе объекта CONTENT, неразумно, или вы не хотите иметь дело с ними непосредственно в коде, но все равно нужно обновлять конкретную информацию в кэше. Мы предлагаем удобную и мощную API можно решить эту проблему, например,updateQueriesилиfetchMore, что позволяет включать новую информацию в эти деревья запросов с очень точным контролем.

Суммировать

Основа любого приложения находится в графе данных приложения. В прошлом, когда нам приходилось отправлять собственные HTTP-запросы к конечным точкам REST для импорта и экспорта данных в граф данных приложения, кэширование на стороне клиента было очень сложным, поскольку выборка данных зависела от клиентского приложения. И теперь GraphQL предоставляет нам много информации, которую мы также можем использовать для автоматического кэширования данных.

Если вы понимаете эти 5 простых концепций, вы сможете понять, как реагировать и кешировать (то есть всю ту магию, которая делает ваше приложение быстрым и плавным) работают в Apollo Client. Здесь мы повторяем это снова:

  1. Запрос GraphQL означает способ получить дерево из графа данных приложения. Мы называем эти деревьяДерево результатов запроса.
  2. Кэшированный клиент Apolloдерево результатов запроса. Для этого он применяет два допущения:
  3. Тот же путь, тот же объект- Один и тот же путь запроса обычно указывает на одну и ту же информацию.
  4. Используйте идентификаторы объектов, когда предположений о пути недостаточно- Если двум результатам запроса присвоен один и тот же идентификатор объекта, они представляют один и тот же узел или информацию.
  5. Если какие-либо кэшированные узлы в дереве результатов запроса обновляются, клиент Apollo обновит запрос новыми результатами.

В общем, знания вышеизложенного должно быть достаточно, чтобы стать экспертом в клиенте Apollo и кэшировании GraphQL. Считаете, что эта статья слишком информативна? Не волнуйтесь — мы продолжим публиковать больше концептуальной информации, если сможем, чтобы каждый мог понять цель GraphQL, откуда он получил свое название и как четко объяснить все аспекты кэширования результатов GraphQL.

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.