GraphQL queries
This is part 2 of the series for GraphQL and Adobe Commerce. In this tutorial and video, learn about GraphQL queries and how to perform them against Adobe Commerce.
Related videos and tutorials on GraphQL in this series
GraphQL Syntax example
Let’s dive right into GraphQL query syntax with a full-fledged example. (Remember, you can try this yourself against https://venia.magento.com/graphql.)
Observe the following GraphQL query, which is broken down piece by piece:
{
country (
id: "US"
) {
id
full_name_english
}
categories(
filters: {
name: {
match: "Tops"
}
}
) {
items {
name
products(
pageSize: 10,
currentPage: 2
) {
items {
sku
}
}
}
}
}
A plausible response from a GraphQL server for the above query could be:
{
"data": {
"country": {
"id": "US",
"full_name_english": "United States"
},
"categories": {
"items": [
{
"name": "Tops",
"products": {
"items": [
{
"sku": "VSW06"
},
{
"sku": "VT06"
},
{
"sku": "VSW07"
},
{
"sku": "VT07"
},
{
"sku": "VSW08"
},
{
"sku": "VT08"
},
{
"sku": "VSW09"
},
{
"sku": "VT09"
},
{
"sku": "VSW10"
},
{
"sku": "VT10"
}
]
}
}
]
}
}
}
The above example relies on the out-of-the-box GraphQL schema for Adobe Commerce, defined at the server. In this request, you
query multiple types of data at once. The query expresses exactly the fields that you want, and the returned data is formatted
similarly to the query itself.
{string}
”, where {string}
is simply the raw string of your entire query. If the request is being sent as a GET, the query might be encoded in the query string parameter “query” instead. Unlike with REST, the HTTP request type doesn’t matter, only the contents of the query.Querying for what you want
country
and categories
in the example represent two different “queries,” for two different kinds of data. Unlike a traditional API paradigm like REST, which would define separate and explicit endpoints for each data type. GraphQL gives you the flexibility to query a single endpoint with an expression that can fetch many types of data at once.
Likewise, the query specifies exactly the fields that are desired for both country
(id
and full_name_english
) and categories
(items
, which itself has a subselection of fields), and the data you receive back mirrors that field specification. There are presumably many more fields available for these data types, but you get back only what you requested.
items
is actually an array of values, but you are nevertheless directly selecting subfields for it. When a field’s type is a list, GraphQL implicitly understands subselections to apply to each item in the list.Arguments
While the fields you want returned are specified within the braces of each type, named arguments and values for them are specified within parentheses after the type name. Arguments are often optional and often affect the way query results are filtered, formatted, or otherwise transformed.
You are passing an id
argument to country
, specifying the particular country to query, and a filters
argument for categories
.
Fields all the way down
While you might tend to think of country
and categories
as separate queries or entities, the entire tree expressed in your query actually consists of nothing but fields. The expression of products
is syntactically no different from that of categories
. Both are fields, and there is no difference between their construction.
Any GraphQL data graph has a single “root” type (typically referred to Query
) to start the tree, and the types often considered to be entities are assigned to fields on this root. The example query is actually making one generic query for the root type and selecting the fields country
and categories
. It is then selecting subfields of those fields, and so on, potentially several levels deep. Wherever the return type of a field is a complex type (for example, one with its own fields, rather than a scalar type), continue to select the fields you want.
This concept of nested fields is also why you can pass arguments for products
(pageSize
and currentPage
) in the same way you did for the top-level categories
field.
Variables
Let’s try a different query:
query getProducts(
$search: String
) {
products(
search: $search
) {
items {
...productDetails
related_products {
...productDetails
}
}
}
}
fragment productDetails on ProductInterface {
sku
name
}
The first thing to note is the added the keyword query
before the opening brace of the query, along with an operation name (getProducts
). This operation name is arbitrary; it doesn’t correspond to anything in the server schema. This syntax was added to support the introduction of variables.
In the previous query, you hard-coded values for the arguments of your fields directly, as strings or integers. The GraphQL specification, however, has first-class support for separating user input from the main query using variables.
In the new query, you are using parentheses before the opening brace of the entire query to define a $search
variable (variables always use the dollar sign prefix syntax). It is this variable that is being provided to the search
argument for products
.
When a query contains variables, the GraphQL request is expected to include a separate JSON-encoded dictionary of values alongside the query itself. For the query above, you might send the following JSON of variable values in addition to the query body:
{
"search": "VT01"
}
related_products
.In any GraphQL-aware client that you are using for testing (such as Altair and GraphiQL), the UI supports entering the variables JSON separately from the query.
Just as you saw that the actual HTTP request for a GraphQL query contains “query: {string}
” in its body, any request containing a variables dictionary simply includes an additional “variables: {json}
” in that same body, where {json}
is the JSON string with the variable values.
The new query also uses a fragment (productDetails
) to reuse the same field selection in multiple places. Read more about fragments in the GraphQL documentation.