30 минут, чтобы понять основные концепции GraphQL

задняя часть Архитектура GraphQL React.js

написать впереди

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

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

  • Читатели, которые слышали о GraphQL и хотят узнать больше
  • Читатели, которые хотят систематически изучать GraphQL
  • Читатели, изучающие технологию GraphQL

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

Если вы применяли GraphQL к реальным проектам, то эта статья может вам не подойти, так как в ней нет каких-то практических выводов и опыта, о практических вещах я напишу отдельное резюме статьи позже.

Что такое GraphQL

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

Некоторые люди здесь могут сказать, что? API еще можно проверить? Разве API не для звонков? Да, именно в этом сила GraphQL, цитируя предложение из официальной документации:

ask exactly what you want.

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

  • Общайтесь с бэкендом, меняйте интерфейс (меняйте источник данных)
  • Проведите некоторую адаптационную работу самостоятельно (обработка источников данных)

Как правило, если это личный проект, изменение внутреннего интерфейса может быть выполнено по желанию, но если это проект компании, изменение внутреннего интерфейса часто является деликатным вопросом, особенно для трех терминалов (веб, andriod, ios) с одним и тем же набором внутренних интерфейсов. В большинстве случаев проблема решается вторым способом.

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

В GraphQL мы предопределяемSchemaи объявить некоторыеTypeДля достижения упомянутого выше эффекта нам необходимо знать:

  • Абстракция модели данных описывается типом
  • Логика получения данных из интерфейса описана Schema

Это может быть абстрактно, поэтому давайте объясним их один за другим.

Type

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

Тип GraphQL можно просто разделить на два типа, один из которых называетсяScalar Type(标量类型), другой звонилObject Type(对象类型).

Scalar Type

Встроенный скаляр в GraphQL содержит:String,Int,Float,Boolean,Enum, которые должны быть хорошо понятны тем, кто знаком с языками программирования.

Стоит отметить, что в GraphQL можно пройтиScalarОбъявите новый скаляр, например:

  • В prisma (библиотека для абстрагирования операций с базой данных с использованием GraphQL) иDateTimeиIDЭти два скаляра представляют формат даты и первичный ключ соответственно.
  • При использовании GraphQL для реализации интерфейса загрузки файлов вам необходимо объявитьUploadСкаляр для представления загружаемого файла

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

Object Type

Только скаляров недостаточно для абстрагирования некоторых сложных моделей данных.В настоящее время нам нужно использовать типы объектов.Например (сначала игнорируйте синтаксис, просто воспринимайте его буквально):

type Article {
  id: ID
  text: String
  isPublished: Boolean
}

Приведенный выше код объявляетArticleТип, он имеет 3 поля, которыеIDидентификатор типа,Stringвведите текст иBooleanТип публикации.

Для объявления поля типа объекта мы обычно используем скаляр, но мы также можем использовать другой тип объекта, например, если мы объявляем новыйUserтипа, следующим образом:

type User {
  id: ID
  name: String
}

На этом этапе мы можем немного изменитьArticleКод объявления типа выглядит следующим образом:

type Article {
  id: ID
  text: String
  isPublished: Boolean
  author: User
}

ArticleдобавленauthorПолеUserТип, представляющий автора этой статьи.

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

Type Modifier

Что касается типов, есть еще одна важная концепция, а именно модификаторы типов.В настоящее время существует два типа модификаторов типов, а именно модификаторы типов.ListиRequired, их синтаксис[Type]иType!, и их можно комбинировать друг с другом, например[Type]!или[Type!]или[Type!]!(пожалуйста, посмотрите здесь!положение), их значения таковы:

  • Сам список обязателен, но его внутренние элементы могут быть пустыми
  • Сам список может быть пустым, но его внутренние элементы обязательны.
  • Требуется как сам список, так и внутренние элементы

Давайте сделаем еще один шаг и изменим приведенный выше пример, если мы объявим новыйCommentтипа, следующим образом:

type Comment {
  id: ID!
  desc: String,
  author: User!
}

вы найдете здесьIDсуществует один!, это означает, что это Поле обязательно, а затем обновитьArticleобъекта следующим образом:

type Article {
  id: ID!
  text: String
  isPublished: Boolean
  author: User!
  comments: [Comment!]
}

Изменения, которые мы сделали здесь, следующие:

  • Поле id изменено на обязательное
  • поле автора изменено на обязательное
  • Добавлено поле комментариев, его тип - тип List, элемент которого имеет тип Comment

окончательныйArticleТип — это относительно простое объявление типа о модели данных статьи в GraphQL.

Schema

Теперь мы начинаем знакомитьSchema, его роль мы кратко описывали ранее, то есть он используется для описания对于接口获取数据逻辑, но это описание все еще несколько абстрактно, мы могли бы также думать о нем как о каждом независимом ресурсе в архитектуре REST.uriЧтобы понять это, но в GraphQL мы используем Query для описания того, как ресурсы получены. Следовательно, мы можем положитьSchemaПод ним понимается таблица, составленная из нескольких запросов.

Вот новая концепцияQuery, используется в GraphQLQueryЧтобы абстрагироваться от логики запроса данных, в соответствии с текущим стандартом существует три типа запросов, а именнозапрос,мутацияиподписка.

Примечание: Для удобства различенияQueryВ частности, ссылаясь на запросы в GraphQL (включая три типа),queryОтносится к типу запроса в GraphQL (относится только к типу запроса)

Query

Три основных типа запросов, упомянутых выше, таковы:Root Query(根查询)Существующие, для традиционных CRUD-проектов, нам нужны только первые два типа, а третий — для текущих набирающих популярностьreal-timeпредложенное приложение.

Мы просто воспринимаем их буквально следующим образом:

  • query (запрос): при получении данных должен быть выбран тип запроса
  • мутация (изменение): при попытке изменить данные следует использовать тип мутации
  • подписка (подписка): когда вы хотите, чтобы данные изменились, вы можете отправить сообщение, используйте тип подписки

Еще возьмем пример для иллюстрации.

Во-первых, мы используем точки зрения REST и GraphQL соответственно, чтобыArticleДля модели данных напишите ряд интерфейсов CRUD следующим образом:

Интерфейс отдыха

GET /api/v1/articles/
GET /api/v1/article/:id/
POST /api/v1/article/
DELETE /api/v1/article/:id/
PATCH /api/v1/article/:id/

GraphQL Query

query {
  articles(): [Article!]!
  article(id: Int): Article!
}

mutation {
  createArticle(): Article!
  updateArticle(id: Int): Article!
  deleteArticle(id: Int): Article!
}

Сравнивая более знакомый нам REST-интерфейс, мы можем обнаружить, что GraphQL разделяет функции Query по типу корневого запроса, а также четко объявляет тип данных, возвращаемый каждым Query.Синтаксис здесь такой же, как и в предыдущем. такие же. Следует отметить, что любое из наших заявленийQueryдолжно бытьRoot QueryПодмножество GraphQL, связанное с внутренним рабочим механизмом GraphQL.

В примере мы объявляем только тип запроса и тип мутации, если в нашем приложении есть список комментариев.real-timeЕсли требования соблюдены, в REST мы можем напрямую передать длинное соединение или предоставить некоторые аутентифицированные интерфейсы для получения длинного URL-адреса соединения, например:

POST /api/v1/messages/

После этого длинное соединение будет подталкивать нам новые данные, в GraphQL мы объявим их более декларативно, следующим образом

subscription {
  updatedArticle() {
    mutation
    node {
    	comments: [Comment!]!
    }
  }
}

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

Resolver

Если мы объявим в схеме только несколько запросов, то мы сделаем только полдела, потому что мы не предоставляем логику для данных, возвращаемых соответствующим запросом. Чтобы заставить GraphQL работать, нам нужно понять еще одну основную концепцию:Resolver(解析函数).

В GraphQL у нас есть соглашение, согласно которому Query и соответствующий преобразователь имеют одно и то же имя, чтобы их можно было сопоставить в GraphQL.articles(): [Article!]!Этот запрос, имя его Resolver должно называтьсяarticles.

Прежде чем представить Resolver, пришло время понять внутреннюю работу GraphQL в целом, скажем, теперь мы будем использовать то, что мы объявили.articlesQuery, мы могли бы написать следующий запрос (опять же, пока игнорируя синтаксис):

Query {
  articles {
  	 id
  	 author {
  	 	name
  	 }
  	 comments {
      id
      desc
      author
    }
  }
}

Когда GraphQL анализирует этот оператор запроса, он выполняет следующие шаги (сокращенная версия):

  • Сначала выполняется первый слой анализа, а текущийQueryизRoot Queryтипquery, при этом требуя, чтобы его имя былоarticles
  • постараюсь использовать позжеarticlesизResolverПолучить данные синтаксического анализа, синтаксический анализ первого слоя завершен
  • После этого синтаксический анализ второго уровня выполняется для возвращаемого значения синтаксического анализа первого уровня.articlesтакже содержит три подQuery, соответственноid,authorиcomments
    • id является скалярным типом в типе Author, и синтаксический анализ завершается
    • автор - тип объекта Пользователь в типе Автора, попробуйте использоватьUserизResolverПолучить данные, текущее поле анализируется
    • После этого синтаксический анализ третьего уровня выполняется над возвращаемым значением синтаксического анализа второго уровня.authorтакже содержитQuery, name, так как это скалярный тип, синтаксический анализ завершается
    • Комментарии выше...

Мы можем обнаружить, что общий процесс синтаксического анализа GraphQL заключается в том, что после обнаружения запроса попытайтесь использовать его Resolver для получения значения, а затем проанализируйте возвращаемое значение.Этот процесс является рекурсивным до тех пор, пока тип анализируемого поля не будетScalar Type(标量类型)до того как. Весь процесс парсинга можно представить как очень длинную Resolver Chain.

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

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

function(parent, args, ctx, info) {
	...
}

Значения параметров следующие:

  • родитель: возвращаемое значение текущего предыдущего преобразователя
  • args: функция, переданная в запросе (например, в приведенном выше примереarticle(id: Int)серединаid)
  • ctx: промежуточные переменные, которые постоянно передаются в цепочке разрешения преобразователя (аналогично контексту в архитектуре промежуточного программного обеспечения).
  • информация: объект AST текущего запроса

Стоит отметить, что внутренняя реализация Resolver — полностью черный ящик для GraphQL. Это означает, что то, как Resolver возвращает данные, какие данные и куда возвращать данные, полностью зависит от самого Resolver, исходя из этого на практике многие люди часто используют GraphQL как промежуточный слой, а данные получают через Resolver.Чтобы инкапсулировать, реализация внутреннего сбора данных может быть основана на RPC, REST, WS, SQL и других различных методах. В то же время, исходя из этого, при миграции некоторых систем, не использующих GraphQL (например, REST), инкрементная миграция может быть очень хороша.

Суммировать

Вот и все. Прежде всего, спасибо за ваше терпение при чтении этого. Хотя тема заключается в том, чтобы ознакомиться с основными концепциями GraphQL за 30 минут, время ожидания истекло, но я полагаю, что вы уже знакомы с ядром. концепции в GraphQL. Но то, что оно включает в себя, гораздо богаче этого, и оно все еще находится в стадии быстрого развития.

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

Хотите узнать больше о самом GraphQL

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

серверная часть

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

Если вы хотите использовать сам GraphQL для разработки системы, рекомендуется узнать о программе под названиемprismaСам фреймворк GraphQL построен на основе GraphQL, имеет хорошую совместимость с некоторыми экологическими фреймворками GraphQL, а также подходит для основных языков программирования, может использоваться как сам ORM, так и как промежуточный уровень, взаимодействующий с используется база данных.

сторона клиента

Если вы склоняетесь к клиентской стороне, вам нужно больше узнать о graphql-client.На этот раз я узнал об apollo, среде grapql-client с открытым исходным кодом, которая совместима с различными основными технологическими стеками переднего плана, такими как Версия Angular, React и т. д., которую приятно использовать.

В то же время вам также необходимо понимать некоторые дополнительные концепции запросов, такие как Connection, Edge и т. д., используемые в запросах на подкачку.

На этом все.Если есть ошибки прошу поправить.