написать впереди
в предыдущей статье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 в целом, скажем, теперь мы будем использовать то, что мы объявили.articles
Query, мы могли бы написать следующий запрос (опять же, пока игнорируя синтаксис):
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 и т. д., используемые в запросах на подкачку.
На этом все.Если есть ошибки прошу поправить.