Перейти к основному содержимому

Синтаксис запросов

В разработке

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

Для наиболее эффективного взаимодействия с GraphQL API необходимо понимать структуру и синтаксис запросов. Для полного понимания всех возможностей и ограничений языка рекомендуется ознакомиться со спецификацией GraphQL. В данной же статье описаны основные компоненты запросов и примеры их применения, основанные на схеме АЛОР Брокер GraphQL API.


Структура запроса

Основные компоненты

Структура запросов к GraphQL API оперирует двумя основными сущностями: операциями и полями.

  • Операция (Operation) — метод взаимодействия с данными, хранящимися в системе.
  • Поле (Field) — единица данных, запрошенная у системы или указатель пути к ней.
Пример запроса
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
currencyInformation {
nominal
settlement
}
}
}

Где:

  • query — тип операции
  • instrument, basicInformation, currencyInformation и прочие — поля, указывающие путь к запрашиваемым данным
примечание

Параметры symbol, exchange и board — обязательные аргументы поля instrument, без которых запрос к нему не может быть выполнен. Подробнее об аргументах в разделе Дополнительные компоненты.

Подробнее о каждом из компонентов:

Операции

GraphQL API торговой системы АЛОР Брокер поддерживает только один тип операций — query.

Сообщения этого типа операций обеспечивают однократное взаимодействие с сервером и используются для получения данных от системы. В качестве транспортного протокола для этого типа операций используется HTTP. По функционалу операции этого типа схожи с GET-запросами к HTTP API, но выполняются при этом с помощью POST-запросов.

Для обозначения типа операции добавьте его на корневой уровень запроса.

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

Поля

Поля — основа запроса в GraphQL. Полями описываются как сами данные, к которым обращён запрос, так и путь до них. Для удобства можно разделить их следующим образом:

  • Поле данных — поле, непосредственно указывающее на данные, которые можно запросить у системы
  • Поле пути — указатель пути до поля данных согласно структуры схемы
Пример запроса
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
currencyInformation {
nominal
settlement
}
}
}

В запросе выше присутствуют оба типа полей:

  • instrument, basicInformation и currencyInformation — поля пути, имеющие собственные вложения и указывающие путь к данным согласно структуры схемы
  • description, shortName и прочие — поля данных, определяющие сами данные для возвращения сервером

В схеме поля могут быть представлены как type, object или interface (или их содержимое) в зависимости от роли поля в структуре данных.

Так, например, запрос выше включает в себя interface Instrument, type BasicInformation, type CurrencyInformation и их содержимое.

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

Дополнительные компоненты

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

  • Именованная операция — возможность присвоить операции уникальное имя. Именование операций не только помогает отличать различные операции друг от друга в системе, но и требуется для использования некоторых других возможностей языка, например, назначения переменных.
  • Аргумент (Argument) — дополнительный параметр запроса, уточняющий поиск данных или ограничивающий их выдачу.
  • Псевдоним (Alias) — заданное отправителем запроса имя поля, объекта или интерфейса, под которым система вернёт запрошенные данные вместо определённого системой.
  • Переменная (Variable) — инструмент параметризации запроса, позволяющий указывать и передавать значения аргументов не напрямую в теле операции, а с помощью отдельного словаря переменных.
  • Фрагмент (Fragment) — инструмент, дающий возможность переиспользовать поля, объекты и интерфейсы в нескольких запросах без необходимости прописывать их для каждого запроса отдельно.
  • Директива (Directive) — инструмент динамического управления запросами, определяющий выполняемые в рамках операции действия и условия в соответствии с переданными данными.
Обязательный дополнительный компонент

В некоторых случаях аргументы могут быть обязательным параметром для выполнения запроса. Если в схеме у объекта, интерфейса или операции прописаны аргументы с восклицательным знаком ! после формата значения — этот аргумент обязателен для заполнения и без него запрос не будет выполнен.

Пример запроса
query customQueryName($customSymbolName1: String!, $customSymbolName2: String!, $getBasicInfo: Boolean!, $getCurrencyInfo: Boolean!) {
CustomInstrument1: instrument(symbol: $customSymbolName1, exchange: "MOEX", board: "TQBR") {
basicInformation @include(if: $getBasicInfo) {
...basicInfoSelectionSet
}
currencyInformation @include(if: $getCurrencyInfo) {
...currencyInfoSelectionSet
}
}
CustomInstrument2: instrument(symbol: $customSymbolName2, exchange: "MOEX", board: "TQBR") {
basicInformation @include(if: $getBasicInfo) {
...basicInfoSelectionSet
}
currencyInformation @include(if: $getCurrencyInfo) {
...currencyInfoSelectionSet
}
}
}
fragment basicInfoSelectionSet on BasicInformation {
description
shortName
type
market
}
fragment currencyInfoSelectionSet on CurrencyInformation {
settlement
nominal
}
Пример словаря переменных
{
"customSymbolName1": "SBER",
"customSymbolName2": "VTBR",
"getBasicInfo": true,
"getCurrencyInfo": false
}

Где:

  • customQueryName — имя операции
  • $customSymbolName1, $customSymbolName2, $getBasicInfo и $getCurrencyInfo — переменные, чьи значения подставляются в текст запроса при его обработке системой. Значения переменных заданы в отдельном словаре переменных в формате JSON
  • CustomInstrument1 и CustomInstrument2 — псевдонимы интерфейса instrument, позволяющие в одном запросе получить данные по одинаковым полям для разных инструментов
  • basicInfoSelectionSet и currencyInfoSelectionSet — фрагменты, позволяющие переиспользовать входящие в объекты BasicInformation и CurrencyInformation строки в нескольких интерфейсах
  • @include(if: $getBasicInfo) и @include(if: $getCurrencyInfo) — директивы, указывающие на добавление объектов BasicInformation и CurrencyInformation в зависимости от значений переменных $getBasicInfo и $getCurrencyInfo
  • Всё, что заключено в круглые скобки — аргументы задействованных объектов, интерфейсов и операций

Подробнее о каждом из компонентов:

Имя операции

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

  • Объявить в операции переменные, которые задаются как аргументы имени операции
  • Передать в одном документе несколько операций с возможностью их идентификации

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

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

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

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

Для присвоения операции собственного имени достаточно указать его после типа операции:

Пример запроса
query Instrument {
instrument(symbol: SBER, exchange: MOEX, board: TQBR) {
basicInformation {
...
}
}
}

Где:

  • Instrument — имя операции
Требования, ограничения, рекомендации
  • Имя операции должно быть уникальным для документа
  • Количество имён операций на один документ не ограничени
  • Заданное имя не должно полностью повторять имена других компонентов запроса, схемы или служебных элементов GraphQL (например, type)

Аргументы

Аргументы позволяют указать дополнительные параметры операции или поля. В случае с полями такие дополнительные параметры позволяют уточнить или ограничить возвращаемые системой данные, тогда как для операций аргументы позволяют объявить используемые в запросе переменные. Ближайшим аналогом аргументов в GraphQL можно назвать Query-параметры запросов к HTTP API.

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

Пример схемы
instrument(symbol: String!, exchange: String!, board: String!): Instrument

Где:

  • Instrument — интерфейс, поля которого будут использованы при поиске данных и составлении ответа системой
  • instrument — псевдоним, которым вызывается интерфейс. Именно псевдоним выступает в качестве поля в запросе
  • symbol, exchange, board — аргументы псевдонима для уточнения запроса
  • String — формат данных, которому должно соответствовать передаваемое значение

Следуя схеме, запрос с аргументами будет выглядеть следующим образом:

Пример запроса
{
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
...
}
}
}

Формат записи аргументов в схеме не играет роли:

Пример схемы
instruments(
includeOld: Boolean! = false
includeNonBaseBoards: Boolean! = false
first: Int
after: String
last: Int
before: String
where: InstrumentModelFilterInput
order: [InstrumentModelSortInput!]
): InstrumentsConnection

Где:

  • InstrumentsConnection — тип, поля которого будут использованы при поиске данных и составлении ответа системой
  • instruments — псевдоним, которым вызывается тип
  • includeOld, includeNonBaseBoards, first, after, last, before, where и order — аргументы псевдонима для уточнения запроса

При этом в запросе все аргументы будут так же указаны в одну строку:

Пример запроса
{
instruments(includeOld: false, includeNonBaseBoards: false, last: 50, order: ) {
totalCount
nodes {
basicInformation {
...
}
}
}
}

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

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

Так, например, в схеме выше аргументы includeOld и includeNonBaseBoards носят обязательный для заполнения характер, когда такие аргументы, как before и after могут быть проигнорированы.

Предзаданные значения

Некоторым аргументам в схеме может быть задано значение по умолчанию, указываемое после знака равенства =. Такие аргументы всё ещё носят обязательный характер, но их не обязательно указывать в запросе — в этом случае будет использовано предзаданное значение.

Например, аргумент includeOld: Boolean! = false носит обязательный характер, но при этом имеет значение по умолчанию. Если предзаданное значение соответствует задаче, этот аргумент можно опустить.

Требования, ограничения, рекомендации
  • Имя аргумента должно быть указано в запросе в полном соответствии с написанием в схеме. Before и before — разные аргументы
  • Аргументы присваиваются только полям и операциям. Псевдонимы не обладают собственными аргументами
  • Аргументы с восклицательным знаком ! носят обязательный характер и для выполнения запроса для них должно быть указано значение
  • Аргументу может быть задано значение по умолчанию, позволяющее не прописывать аргумент в запросе, если предзаданное значение соответствует задаче

Псевдонимы

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

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

Допустим, существует задача получить в рамках одной операции данные по одинаковым полям для инструментов SBER и VTBR одновременно. Если просто продублировать объект instrument:

Пример запроса
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
instrument(symbol: "VTBR", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
}

При получении данных произойдёт ошибка слияния полученных данных. Причина — одинаковое имя instrument для обоих тикеров.

В таком случае можно присвоить каждому из полей instrument псевдоним, сделав их уникальными:

Пример запроса
query {
Instrument1: instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
Instrument2: instrument(symbol: "VTBR", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
}

Где:

  • instrument — вызываемое поле
  • Instrument1 и Instrument2 — заданные пользователем псевдонимы для разделения данных от поля instrument

Таким образом, после присвоения объекту с тикером SBER псевдоним instrument1, а объекту VTBR псевдоним instrument2, были созданы объекты с уникальными именами, чьи данные не конфликтовали при слиянии.

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

Фрагменты

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

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

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

Пример фрагмента
fragment basicInfoFragment	on	Instrument {
basicInformation {
description
shortName
type
market
}
}

Где:

  • fragment — оператор, объявляющий объект в документе как фрагмент
  • basicInfoFragment — уникальное имя фрагмента
  • Instrument — ссылающееся на интерфейс поле, в котором будет размещаться фрагмент
  • basicInformation, description, shortName, type, market — поля, из которых состоит фрагмент

Чтобы использовать созданный фрагмент в операции, добавьте в тело операции имя фрагмента с префиксом ...:

Пример указателя на фрагмент
query {
instrument1: instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
}

Таким образом, документ с повторяющимися полями вроде этого (681 байт UTF-8):

Пример запроса
query {
instrument1: instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
instrument2: instrument(symbol: "VTBR", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
instrument3: instrument(symbol: "TCSG", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
instrument4: instrument(symbol: "SVCB", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
}
}

С помощью фрагмента может быть преобразован так (581 байт UTF-8):

Пример запроса с фрагментом
query {
instrument1: instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
instrument2: instrument(symbol: "LKOH", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
instrument3: instrument(symbol: "TCSG", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
instrument4: instrument(symbol: "SVCB", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
}
fragment basicInfoFragment on Instrument {
basicInformation {
description
shortName
type
market
}
}

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

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

Пример запроса
query GetBasicInfo {
instrument(symbol: "SBER", board: "TQBR", exchange: "MOEX") {
...getBasicInfoFragment
}
}
fragment getBasicInfoFragment on Instrument {
basicInformation {
symbol
...getDescriptionFragment
...getMarketFragment

}
}
fragment getDescriptionFragment on BasicInformation {
shortName
description
}
fragment getMarketFragment on BasicInformation {
market
type
}

Где:

  • getBasicInfoFragment — фрагмент, использующийся в теле операции и содержащий в себе более низкоуровневые фрагменты
  • getDescriptionFragment и getMarketFragment — низкоуровневые фрагменты, входящие в getBasicInfoFragment и содержащие поля данных
  • symbol — поле данных, размещённое в фрагменте getBasicInfoFragment наравне с низкоуровневыми фрагментами
Требования, ограничения, рекомендации
  • Количество фрагментов в одном документе не ограничено
  • Каждый фрагмент должен обладать уникальным именем
  • Имя фрагмента не должно полностью повторять имена компонентов, уже фигурирующие в схеме, либо служебные имена компонентов GraphQL (например, type или on)
  • Фрагмент должен быть составлен и размещён в теле операции в соответствии с определённой структорой схемы иерархией
  • Фрагмент не должен использоваться в объектах, которым не соответствует его содержимое
  • Фрагмент должен содержать в себе тип, объект или интерфейс. Нельзя создавать фрагменты, состоящие только из полей данных
  • Фрагмент может содержать в себе аргументы входящих в него полей, но ему нельзя присвоить собственные аргументы
  • В фрагментах можно использовать переменные, но переменными нельзя задавать имя фрагмента
  • Объявленный в документе фрагмент должен быть использован как минимум в одной операции
  • Составные фрагменты не должны образовывать цикл, рекурсивно ссылаясь друг на друга

Переменные

Статического указания данных в запросе достаточно, когда речь идёт о тестовых взаимодействиях или одиночных запросах. В практическом применении становится сложнее изменять значения во всей операции каждый раз, когда нужно передать новые вводные.

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

Чтобы задать переменную:

  1. Объявите переменную с требуемым форматом данных для передаваемого значения в аргументах операции
  2. Разместите переменную в теле операции
  3. Объявите значение переменной в словаре переменных
Префикс

В словаре переменных указывается только имя переменной. В имени и теле операции это имя должно сопровождаться префиксом $

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

Пример запроса
query QueryWithVariable($symbolvar: String) {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...
}
}

Где:

  • $symbolvar: String — переменная symbolvar с форматом данных String

В качестве возможных форматов данных GraphQL принимает следующие варианты:

  • String
  • Int
  • Float
  • Boolean

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

Пример запроса
query QueryWithVariable($symbolvar: String!) {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...
}
}

Где:

  • $symbolvar: String! — переменная symbolvar с форматом данных String и указателем !, придающим переменной обязательный характер
Переменные и аргументы

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

Чтобы разместить переменную в теле операции, добавьте её имя с префиксом вместо статического значения:

Пример запроса
query QueryWithVariable($symbolvar: String!) {
instrument(symbol: $symbolvar, exchange: "MOEX", board: "TQBR") {
...
}
}

Где:

  • $symbolvar: String! — объявление переменной с форматом данных в аргументах операции
  • symbol: $symbolvar — размещение переменной в качестве значения для аргумента symbol

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

Пример переменной в словаре
{
"symbolvar": "SBER"
}
Требования, ограничения, рекомендации
  • Количество переменных в одном документе не ограничено
  • Каждая переменная должна обладать уникальным именем
  • Имя переменной не должно полностью повторять имена компонентов, уже фигурирующие в схеме, либо служебные имена компонентов GraphQL
  • Если переменная используется для передачи значения аргумента, она должна наследовать все свойства ожидаемого значения аргумента из схемы
  • Объявленная в словаре переменная должна быть хоть раз использована в теле операции
  • Переменные можно использовать внутри фрагментов, но нельзя использовать для передачи имени фрагмента
  • Переменные, размещённые в неиспользуемых фрагментах, считаются неиспользованными
  • В переменных можно передавать целые объекты, но их не стоит рассматривать как замену фрагментам

Директивы

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

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

  • @include — включить содержимое директивы в запрос
  • @skip — исключить содержимое директивы из запроса

Чтобы объявить директиву, добавьте её ключ вместе с условием выполнения в качестве аргумента в тело операции:

Пример запроса
query { 
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation @include(if: $getBasicInfo) {
...
}
currencyInformation @skip(if: $getCurrencyInfo) {
...
}
}
}

Где:

  • @include и @skip — ключи директив
  • if: $getBasicInfo и if: $getCurrencyInfo — условия выполнения директив

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

Пример запроса
query QueryWithDirectives($getBasicInfo: Boolean!, $getCurrencyInfo: Boolean!) { 
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation @include(if: $getBasicInfo) {
...
}
currencyInformation @skip(if: $getCurrencyInfo) {
...
}
}
}
Словарь переменных
{
"getBasicInfo": true,
"getCurrencyInfo": false
}

В представленном выше содержимое полей basicInformation и currencyInformation будет добавлено в ответ, так как для @include условие выполняется (значение управляющей переменной true), а для @skip — нет (значение переменной false).

Требования, ограничения, рекомендации
  • Количество директив в одном документе не ограничено
  • Для управления директивами используются переменные, которые подчиняются собственным требованиям в полной мере
  • Ключи директив зависят от схемы и не могут быть самостоятельно объявлены пользователем
  • Директивы можно размещать в фрагментах, равно как фрагменты можно указывать как содержимое директив
  • Нельзя разместить несколько директив для одного и того же поля, равно как нельзя задать несколько аргументов для одной директивы. Варианты написания basicInformation @include(if: $getBasicInfo) @include(if: $getCurrencyInfo) или basicInformation @include(if: $getBasicInfo, $getCurrencyInfo) будут восприняты как ошибка синтаксиса
  • Одна переменная может управлять несколькими директивами одновременно

Что дальше?