Схемы и типы¶
Здесь вы изучите все, что вам необходимо знать системе типов в GraphQL и как она описывает, какие данные нужно получить. Т. к. GraphQL может быть использован с любым серверным фреймворком или языком программирования, мы не будем вдаваться в делати внедрения и будем говорить только о концепции.
Система типов¶
Если вы видели запрос GraphQL ранее, вы знаете, что язык запросов GraphQL - это выбор полей в объектах. Например, в данном запросе:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
- Мы начинаем со специального "root" объекта
- Мы выбираем поле
hero
в нем - Для объекта, возвращенного в
hero
, мы выбираем поляname
иappearsIn
Т. к. форма запроса GraphQL близко совпадает с результатом, вы можете предсказать, что вернет запрос, без детальных знаний о сервере. Однако, полезно иметь точное описание данных, которые мы можем запросить - какие поля мы можем выбрать? Какие виды объектов они могут вернуть? Какие поля доступны для этих дочерних объектов? Здесь выручает схема.
Любой сервис GraphQL определяет набор типов, который полностью описывает набор возможных данных, которые вы можете запросить из сервиса. Тогда, когда приходят, они проверяются и выполняются в соответствии со схемой.
Язык типов¶
Сервисы GraphQL могут быть написаны на любом языке. Поскольку мы не можем опираться на синтаксис какого-то конкретного языка, как JavaScript например, при разговоре о схемах GraphQL, мы определим наш собственный простой язык. Мы будем использовать "GraphQL schema language" - он похож на язык запроса, и позвояет нам говорить о схемах GraphQL отрешенно от языка.
Типы объектов и поля¶
Самые простые компоненты схемы GraphQL - типы объектов, которые просто представляют вид объекта, который вы можете получить с вашего сервиса, и какие поля он имеет. В GraphQL schema language, мы можем отобразить это следующим образом:
1 2 3 4 |
|
Язык довольно просто читаем, но давайте пройдемся по нему и расширим наш запас слов:
Character
- тип объекта GraphQL, подразумевает его тип и некоторые поля. Большинство типов в вашей схеме будут типами объектов.name
иappearsIn
- поля вCharacter
. Это значит, чтоname
иappearsIn
- единственные поля, которые могут появиться в любой части запроса GraphQL, который оперирует сCharacter
.String
- один из встроенных скалярных типов - это типы, которые относятся к одному скалярному объекту и не могут иметь подвыборки в запросе. Мы пройдемся по ним немного позже.String!
значит, что поле не может быть незаполненным (null
),- сервис GraphQL обещает всегда отдавать вам значение, когда вы запрашиваете это поле. Это отмечено восклицательным знаком.[Episode]!
представляет массив объектовEpisode
. Поскольку он так же не может бытьnull
, вы всегда можете ожидать массив (пустой или с элементами), когда вы запрашиваете полеappearsIn
.
Теперь вы знаете, как выглядит тип объекта в GraphQL, и как читать основы GraphQL type language.
Аргументы¶
Каждое поле в типе объекта GraphQL может иметь 0 или более аргументов, например поле length
ниже:
1 2 3 4 5 |
|
Все аргументы именованы. В отличие от языков типа JavaScript и Python, где функции принимают список упорядоченных аргументов, все аргументы в GraphQL отправляются исключительно по имени. В этом случае, поле length
имеет один определенный аргумент, unit
.
Аргументы могут быть либо обязательными, либо опциональными. Если аргумент опционален, мы можем определить значение по умолчанию - если не будет передан аргумент unit
, по умолчанию будет использоваться его значение METER
.
Типы запрос и мутация¶
Большинство типов в вашей схеме будет просто нормальными типами объектов, но есть два особых типа:
1 2 3 4 |
|
Любой сервис GraphQL имеет тип query
и может иметь или не иметь тип mutation
. Эти типы подобны регулярным типам объекта, но являются особенными, т. к. определяют точку входа каждого запроса GraphаQL. Так что если вы видете запрос наподобие этого:
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
Это значит, что сервис GraphQL должен иметь тип Query
с полями hero
и droid
:
1 2 3 4 |
|
Мутации (Mutations) работают аналогично - вы определяете поля в типе Mutation, и они будут доступны как корневые поля мутации, которые вы можете вызвать в своем запросе.
Важно помнить, что кроме того, что быть точкой входа в схему - особый статус, типы Query
и Mutation
являются такими же типами объектов в GraphQL, и их поля работают тем же образом.
Скалярные типы¶
Тип объекта GraphQL имеет имя и поля, но в некой точке эти поля должны отвечать определенной информации. Здесь вступают скалярные типы: они представляют листья запроса.
В следующем запросе, name
и appearsIn
отдадут скалярные типы:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Мы знаем это, т. к. эти поля не имеют подчиненных полей - это листья запроса.
GraphQL из коробки содержит набор стандартных скалярных типов:
Int
: Подписанное 32‐bit целое число.Float
: Подписанное число с плавающей точкой двойной точности.String
: Строка в UTF‐8.Boolean
:true
илиfalse
.ID
: Скалярный типID
представляет уникальный идентификатор, обычно используемый для переполучения объекта или как ключ кеша. ТипID
сериализован так же, какString
; однако, определение его какID
подразумевает, что он не должен быть распознан людьми.
В большинстве внедрений сервиса GraphQL, есть так же возможность определить собственный скалярный тип. Например, мы можем определить тип Date
:
scalar Date
В этом случае уже от нашей имплементации зависит, как этот тип должен быть сериализован, десериализован и проверен. Например, вы можете определить, что тип Date
всегда должен быть сериализован в формат timestamp
, и ваш клиент должен ожидать, что этот формат будет у всех полей с датой.
Перечисляемые типы¶
Так же зовутся Enums
, типы перечисления, особый вид скалярных типов, который может содержать только значение из определенного набора значений. Это позволяет вам:
- Проверять, что любые аргументы этого типа содержат разрешенное значение
- Сообщить через систему типов, что поле будет всегда содержать одно из конечного множества значений
Вот как может выглядеть определение enum в GraphQL schema language:
1 2 3 4 5 |
|
Это значит, что где бы мы не использовали тип Episode
в нашей схеме, мы будем ожидать значение NEWHOPE
, EMPIRE
, или JEDI
.
Отметим, что имплементации сервиса GraphQL в различные языки будет иметь их собственные, зависящие от языка, пути использования enums
. В языках, которые поддерживают enums
как объект первого класса, имплементация может получить его преимущества; в языке вроде JavaScript без поддержки enum
, эти значения могут быть связаны с набором чисел (массив). Однако, эти делатили не видны клиенту, который может работать полностью в терминах строковых имен значений перечисления.
Списки и обязательные значения¶
Типы объектов, скаляры и enums
(перечисления) - не единственные виды типов, которые вы можете определить в GraphQL. Но когда вы используете типы в других частях схемы, или в определенях переменных вашего запроса, вы можете применить дополнительные модификаторы типов, которые влияют на проверку этих значений. Взглянем на пример:
1 2 3 4 |
|
Здесь, мы используем тип String
и делаем его Non-Null
, добавляя восклицательный знак после имени типа. Это значит, что наш сервер всегда ожидает вернуть непустое значение этого поля, а если вернет, это вызовет ошибку исключения в GraphQL.
Модификатор типа Non-Null
может быть так же использован, когда определяются аргументы поля, что побуждает сервер GraphQL возвращать ошибку проверки на null
переданного аргумента.
1 2 3 4 5 |
|
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Списки работают по тому же принципу: мы можем использовать модификатор типа, чтобы отметить тип как List
, что означает, что это поле вернет массив с этим типом. В языке схемы, это отображено как оборачивание типа в квадратные скобки, [
и ]
. Так же и с аргументами, где этап проверки будет ожидать массив этих значений.
Модификаторы Non-Null
and List
могут комбинироваться. Например, вы можете иметь List
с со значениями Non-Null Strings
:
1 |
|
Это значит, что список сам по себе может быть null
, но не может иметь null
члены. Например, в JSON:
1 2 3 |
|
Теперь, скажем, мы определим Non-Null List of Strings:
1 |
|
Это значит, что список сам по себе не может быть null
, но может содержать null
значения:
1 2 3 |
|
Вы можете произвольно разместить любое количество модификаторов Non-Null
и Non-Null
, в соответствии с вашими нуждами.
Интерфейсы¶
Как и многие системы, GraphQL поддерживает интерфейсы. Интерфейс - это тип абстракции, который включает определенный набор полей, которые тип должен включить для внедрения интерфейса.
Например, у вас есть интерфейс Character
, который представяет любого персонажа в трилогии Star Wars:
1 2 3 4 5 6 |
|
Это значит, что любой тип, который включает Character
, должен иметь эти поля, с такими же аргументами и типами возврата.
Например, вот несколько типов, которые могут внедрить Character
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Вы можете видеть, что оба этих типа имеют все поля из интерфейса Character
, но так же и дополнительные поля totalCredits
, starships
и primaryFunction
, особых для данного конкретного типа.
Интерфейсы полезны, когда вы хотите вернуть объект или набор объектов, но они могут быть различных типов. Например, следующий запрос генерирует ошибку:
1 2 3 4 5 6 |
|
1 2 3 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Поле hero
возвращает тип Character
, что означает, что это может быть Human
или Droid
, в зависимости от аргумента episode
. В запросе выше, вы можете запросить только поля, которые существуют в интерфейсе Character
, который не включает primaryFunction
.
Чтобы запросить поле из определенного типа объекта, вам нужно использовать такой фрагмент:
1 2 3 4 5 6 7 8 |
|
1 2 3 |
|
1 2 3 4 5 6 7 8 |
|
Более подробно об этом можно прочитать в разделе inline fragments инструкции к запросам.
Объединенные типы¶
Типы Union
очень похожи на интерфейсы, но не определяют общие поля между типами.
union SearchResult = Human | Droid | Starship
Когда возвращается тип SearchResult
в нашей схеме, мы можем получить Human, Droid или Starship. Отметим, что члены типа union
должны быть конкретными типами объекта; вы не можете создать тип union
из интерфейса или других union
.
В этом случае, если вы запрашиваете поле, которое возвращает тип union SearchResult
, вам нужно использовать фрагмент с условиями, который позволит запросить любое поле:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Типы ввода¶
Только недавно мы говорили о передаче скалярных значений, таких как enums
или strings
, как аргументы в поле. Но вы так же можете с легкостью передать целые объекты. Это полезно в частности в случае мутаций, где вы можете захотеть передать данные в целом объекте. В GraphQL schema language, типы ввода выглядят точно так же, как регулярные типы объектов, но с ключевым словом input
вместо type
:
1 2 3 4 |
|
Вот как вы можете использовать объект ввода в мутации:
1 2 3 4 5 6 7 8 9 |
|
1 2 3 4 5 6 7 |
|
1 2 3 4 5 6 7 8 |
|
Поля в объекте типа объекта ввода могут самостоятельно соотноситься с типами объектов, но вы не можете смешивать типы ввода и вывода в вашей схеме. Типы объектов ввода так же не могут иметь аргументов к полям.