Syntax
The GraphQL API is currently under development, so some features of the language and system may not be available.
To interact with the GraphQL API in the most efficient way, it is necessary to understand the structure and syntax of queries. To fully understand all the capabilities and limitations of the language, it is recommended to read the GraphQL specification. This article describes the main query components and examples of their application based on the ALOR Broker GraphQL API schema.
Query structure
Required components
Query structure of GraphQL API operates with two main entities: Operations and Fields.
- Operation — a method of interacting with data stored in the system.
- Field — a data unit requested from the system or a path pointer to it.
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
currencyInformation {
nominal
settlement
}
}
}
Where:
query
is the operation typeinstrument
,basicInformation
,currencyInformation
and others are fields indicating the path to the requested data
The parameters symbol
, exchange
and board
are required arguments of the instrument
field, without which a request to it cannot be executed. See Optional components for more information about arguments.
Details about each of the components:
Operations
GraphQL API of the ALOR Broker trading system supports only one type of operations — query
.
Messages of this operation type provide a one-time interaction with the server and are used to retrieve data from the system. HTTP is used as a transport protocol for this type of operations. In terms of functionality, operations of this type are similar to GET requests to HTTP API, but actually sending as POST requests.
To specify the type of operation, add it to the root level of the request.
- You may not specify the operation type if the request body contains only one operation that does not use variables and directives. Otherwise, the operation type must be specified
- To add arguments (e.g., variable declarations) to the root of the request, the operation must be named
Fields
Fields are the basis of a GraphQL query. Fields describe both the data itself, to which the query is addressed, and the path to them. For convenience, you can categorize them as follows:
- Data field — a field that directly indicates the data that can be requested from the system
- Path field — path pointer to the data field according to the schema structure
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
description
shortName
type
market
}
currencyInformation {
nominal
settlement
}
}
}
Both types of fields are presented in the query above:
instrument
,basicInformation
andcurrencyInformation
- path fields that have their own attachments and specify the path to the data according to the schema structuredescription
,shortName
and others - data fields defining the data itself to be returned by the server.
In the schema, fields can be represented as type
, object
or interface
(or their contents) depending on the role of the field in the data structure.
So, for example, the query above includes interface Instrument
, type BasicInformation
, type CurrencyInformation
and their contents.
- At least one data field must be specified in the body of the query
- All path fields that constitute a path to a data field must be specified in the body of the query according to the following scheme
Optional components
In addition to the required components that describe the structure of the requested data, GraphQL also supports a number of additional parameters that simplify the work with the system:
- Operation name — the ability to assign a unique name to an operation. Naming operations not only helps to distinguish different operations from each other in the system, but is also required to use some other language features, such as variables assignment.
- Argument — an additional query parameter that specifies data search or limits its output.
- Alias — the name of a field, object or interface specified by the sender of the request, under which the system will return the requested data instead of the one defined by the system.
- Variable — a parameterization feature that allows to specify and pass argument values not directly in the operation body, but using a special variable dictionary.
- Fragment — a programmer's tool allowing to reuse fields, objects and interfaces in several queries without having to specify them for each query separately.
- Directive — a dynamic query management tool that defines the actions and conditions to be performed within an operation according to the data passed.
In some cases arguments may be required to execute a request. If an object, interface or operation in the schema has arguments with an exclamation mark !
after the value format - this argument is required and the request will not be executed without it.
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
}
Where:
$customQueryName
is the name of the operation$customSymbolName1
,$customSymbolName2
,$getBasicInfo
and$getCurrencyInfo
- variables whose values are substituted into the text of the request when it is processed by the system. The values of the variables are specified in a special variable dictionary in JSON formatCustomInstrument1
andCustomInstrument2
- aliases ofinstrument
interface, allowing to get data on the same fields for different instruments in one request.basicInfoSelectionSet
andcurrencyInfoSelectionSet
- fragments allowing to reuse strings included inBasicInformation
andCurrencyInformation
objects in several interfaces@include(if: $getBasicInfo)
and@include(if: $getCurrencyInfo)
- directives indicating the addition ofBasicInformation
andCurrencyInformation
objects depending on the values of the$getBasicInfo
and$getCurrencyInfo
variables- Everything enclosed in parentheses are arguments of the involved objects, interfaces and operations
More details about each of the components:
Operation name
You can assign a unique name to each operation in a message sent to the system. Assigning a name to an operation is useful when you want to:
- Declare variables in an operation that should be set as arguments to the operation name
- Pass multiple operations in one document which can be identified by their name
Variable declaration is described below, so here we will focus on identifying multiple operations in a single document.
A document in GraphQL is a message body that contains all the payload that the server should process, except for variable values, which are transmitted in a separate dictionary.
A single document can contain several unrelated operations that can be transmitted one at a time or all at once. Assigning a unique name to an operation allows it to be identified among others and only those operations that meet the current needs can be passed.
Operation names are not dependent on the schema and should be defined by the user. The names of operations predefined in the scheme should be considered by the user as a necessity caused by the scheme structure or as a variant of designation of a specific operation.
To give a name to an operation, simply specify it after the operation type:
query Instrument {
instrument(symbol: SBER, exchange: MOEX, board: TQBR) {
basicInformation {
...
}
}
}
Where Instrument
is the operation name
- Each operation name must be unique for a document
- The number of operation names per document is not limited.
- The given name must not repeat the names of other GraphQL query components, schema or service elements (e.g.
type
).
Arguments
Arguments allow you to specify additional parameters for an operation or field. In the case of fields, such additional parameters allow you to specify or limit the data returned by the system, while for operations, arguments allow you to declare the variables used in the query. The closest analog of arguments in GraphQL is Query-parameters in requests to HTTP API.
For fields, the list of available arguments is defined by the schema and is enclosed in parentheses:
instrument(symbol: String!, exchange: String!, board: String!): Instrument
Where:
Instrument
— the interface whose fields will be used when the system searches for data and composes a responseinstrument
— alias, which is used to call the interface. This alias acts as a field in the querysymbol
,exchange
,board
— arguments of the alias to refine the queryString
— data format to which the passed value should correspond.
According to the scheme, a query with arguments will look as follows:
{
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation {
...
}
}
}
The format in which the arguments are written in the schema does not matter:
instruments(
includeOld: Boolean! = false
includeNonBaseBoards: Boolean! = false
first: Int
after: String
last: Int
before: String
where: InstrumentModelFilterInput
order: [InstrumentModelSortInput!]
): InstrumentsConnection
Where:
InstrumentsConnection
— the type whose fields will be used when the system searches for data and composes a responseinstruments
— alias by which the type is calledincludeOld
,includeNonBaseBoards
,first
,after
,last
,before
,where
andorder
— arguments of the alias for query refinement
All arguments in the query can be set to a single line as well:
{
instruments(includeOld: false, includeNonBaseBoards: false, last: 50, order: ) {
totalCount
nodes {
basicInformation {
...
}
}
}
}
Arguments can be either required or optional. Without required parameters, the query will not be executed as the system will not receive enough data to compose a response. Optional parameters allow you to refine a functional query or apply additional sorting or filtering settings to it.
The required nature of the argument is indicated in the scheme by an exclamation mark !
when specifying the data format of the expected value. If it is not present — the argument is optional.
So, for example, in the schema above, the arguments includeOld
and includeNonBaseBoards
are required, when arguments such as before
and after
can be ignored.
Some arguments in the schema may be given a default value, specified after the equal sign =
. Such arguments are still required, but they do not have to be specified in the query — in this case the predefined value will be used.
For example, the argument includeOld: Boolean! = false
is required, but it has a default value. If the pre-defined value is appropriate for the task, this argument can be omitted.
- The argument name must be specified in the query in full conformance with the spelling in the schema.
Before
andbefore
are different arguments - Arguments can be assigned to fields and operations only. Aliases do not have their own arguments
- Arguments with an exclamation mark
!
are required and a value must be specified for them in order to execute the query - An argument can be given a default value, allowing the argument to be omitted from the query if the predefined value is appropriate for the task.
Aliases
Aliases allow you to give a custom name for a field under which the system will return the requested data. This can be useful when the same field occurs several times in the same request, or when the client application that sent the request expects to receive data under specific names in the response.
Aliases are not directly schema-dependent and are user-defined. The aliases predefined in the schema should be treated by the user as regular fields.
Suppose there is a task to get data on the same fields for SBER
and VTBR
instruments simultaneously within one operation. If we simply duplicate the object 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
}
}
}
When receiving data, an error of merging the received data will happen. The reason is the same instrument
name for both tickers.
In this case, you can give each of the instrument
fields an alias, making them unique:
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
}
}
}
Where:
instrument
— invoked fieldInstrument1
andInstrument2
— user-defined aliases to split data of theinstrument
field
So, by giving the object with ticker SBER
the alias instrument1
and the object VTBR
the alias instrument2
, objects with unique names were created whose data would not conflict when merged.
- The number of aliases in one query is not limited
- Each alias must be unique
- The alias must not repeat component names that already appear in the schema or service names of GraphQL components (e.g.
type
).
Fragments
Some field sets may be used multiple times in the same document, which causes an increase in network load from bigger data packets being transmitted, and also makes it difficult to read and modify the document. Fragments allow you to optimize the body of the document by replacing the duplication of the entire field set with a single pointer to the set outside the operation area.
To declare a new fragment, you must take it out of any operations in the document, declare it using the fragment
operator, name it, give it an origin, and specify its contents.
For example, to create a fragment from the basicInformation
field included in Instrument
and some of its constituent fields, the following object must be added to the body of the request:
fragment basicInfoFragment on Instrument {
basicInformation {
description
shortName
type
market
}
}
Where:
fragment
- operator that declares an object in the document as a fragmentbasicInfoFragment
- unique fragment nameInstrument
- interface-referenced field that will contain the fragmentbasicInformation
,description
,shortName
,type
,market
- fields that make up the fragment
To use the created fragment in an operation, add the fragment name with the prefix ...
to the body of the operation:
query {
instrument1: instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...basicInfoFragment
}
}
So a document with repeating fields like this (681 bytes 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
}
}
}
Using a fragment can be converted like this (581 bytes 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
}
}
When declaring a fragment, it is important to structurally designate the order of fields to be substituted in the query. Thus, if the hierarchy of the extracted fields is a nesting of the field instrument
, when declaring a fragment, the target for it should be declared as this field.
In addition, GraphQL supports the creation of compound fragments, in which one fragment refers to another. For example:
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
}
Where:
getBasicInfoFragment
— the fragment used in the body of the operation and containing the lower-level fragments within itgetDescriptionFragment
andgetMarketFragment
— low-level fragments included ingetBasicInfoFragment
and containing data fieldssymbol
— data field placed in thegetBasicInfoFragment
fragment along with low-level fragments.
- The number of fragments in one document is not limited
- Each fragment must have a unique name
- The fragment name must not repeat the names of components that already appear in the schema, or service names of GraphQL components (for example,
type
oron
). - The fragment must be composed and placed in the body of the operation according to the hierarchy defined by the schema structure.
- The fragment must not be used in objects to which its contents do not correspond
- A fragment must contain a type, object or interface. Fragments consisting only of data fields must not be created.
- A fragment can contain arguments of its fields, but it cannot be assigned with its own arguments.
- You can use variables in fragments, but you cannot use variables to set the fragment name.
- A fragment declared in a document must be used in at least one operation
- Compound fragments must not form a loop by recursively referring to each other.
Variables
Specifying data statically in a query is sufficient when it concerns test interactions or single shot queries. In practical use it becomes more difficult to change the values in the entire operation every time new inputs need to be passed.
Instead of statically specified data, you can use variables whose values will be substituted into the query automatically.
To specify a variable:
- Declare a variable with the required data format for the value to be passed in the operation arguments
- Place the variable in the operation body
- Declare the value of the variable in the variable dictionary
Variable dictionary contains only the name of the variable. In the name and body of the operation, this name must be prefixed with $
.
To declare a variable in an operation, give it a unique name and in the arguments to it, declare the name of the variable to be added and the data format to which the value should correspond:
query QueryWithVariable($symbolvar: String) {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...
}
}
Where $symbolvar: String
is the symbolvar
variable with String
data format
GraphQL accepts the following options as possible data formats:
- String
- Int
- Float
- Boolean
Like the predefined arguments in the schema, variables declared in the operation name can be made required - the operation will not be executed if the variable is not assigned a value matching the specified data format:
query QueryWithVariable($symbolvar: String!) {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
...
}
}
Where $symbolvar: String!
is the symbolvar
variable with String
data format and !
pointer indicating that this variable is required.
If a variable replaces the value of an argument from a schema with the value of an argument, the properties of the argument must be fully inherited by that variable. For example, if the argument expects String!
, the variable cannot be set to String
.
To place a variable in the body of an operation, add its name with a prefix instead of a static value:
query QueryWithVariable($symbolvar: String!) {
instrument(symbol: $symbolvar, exchange: "MOEX", board: "TQBR") {
...
}
}
Where:
$symbolvar: String!
— declaration of a variable with a data format in the operation's argumentssymbol: $symbolvar
— placement of the variable as a value for thesymbol
argument
The variable dictionary is a separate entity whose format depends on the selected transport protocol (most often JSON). To declare a variable in the dictionary, add its name with a new line and specify its value:
{
"symbolvar": "SBER"
}
- The number of variables in one document is not limited
- Each variable must have a unique name
- The variable name must not repeat the names of components that already appear in the schema or service names of GraphQL components.
- If a variable is used to pass an argument value, it must inherit all properties of the expected argument value from the schema
- A variable declared in a dictionary must be used at least once in the body of an operation.
- Variables can be used inside fragments, but cannot be used to pass the fragment name
- Variables placed in unused fragments are considered unused.
- Variables can be used to pass integer objects, but should not be considered as a substitute for fragments.
Directives
Some tasks may require temporarily changing a set of fields in an already generated query. Such tasks can be solved by creating several separate operations or by introducing directives into the operation body.
Directives allow you to create alternative scenarios for executing the same operation without having to manually modify the entire operation body. GraphQL currently supports two types of directives:
@include
- include the contents of the directive in the query@skip
- exclude the contents of the directive from the query
To declare a directive, add its key together with the execution condition as an argument to the operation body:
query {
instrument(symbol: "SBER", exchange: "MOEX", board: "TQBR") {
basicInformation @include(if: $getBasicInfo) {
...
}
currencyInformation @skip(if: $getCurrencyInfo) {
...
}
}
}
Where:
@include
and@skip
— directive keysif: $getBasicInfo
andif: $getCurrencyInfo
— conditions of directives execution
Variables are used as a condition for executing a directive and must be declared in accordance with the rules applied to them. A correctly composed operation body will look like this:
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
}
In the example above, the contents of the basicInformation
and currencyInformation
fields will be included in the response, since the condition is met for @include
(control variable value true
) and not for @skip
(variable value false
).
- The number of directives in one document is not limited
- Variables are used to manage directives, which obey their own requirements to the fullest extent
- Directive keys are schema-dependent and cannot be declared by the user independently
- Directives can be placed in fragments, as well as fragments can be set as contents of directives.
- You cannot place multiple directives for the same field, nor can you specify multiple arguments for a single directive. Variants of writing
basicInformation @include(if: $getBasicInfo) @include(if: $getCurrencyInfo)
orbasicInformation @include(if: $getBasicInfo, $getCurrencyInfo)
will be reported as a syntax error - A single variable can control multiple directives at the same time
What's next?
Additionally, we recommend reading the following related articles:
- Official GraphQL specification
- Official GraphQL documentation
- Official FAQ
- Arrays
- Pagination
- Filtering
- Sorting
- Sandbox for Production environment
- Sandbox for Test environment