GraphQL Query

Composing and running GraphQL queries

As we have elaborated in other articles about GraphQL , the specification describes the query language, the schema definition language, and the runtime to resolve GraphQL operations which can be of type query, mutation, or subscription. This article focuses on GraphQL queries and describes variations in composing them so that GraphQL servers can fulfill them. Let's get started!

The GraphQL query operation

The GraphQL query is one of the core operations of GraphQL. With a single endpoint, App developers can retrieve data from a GraphQL server using queries. Hence, a query is the equivalent of an HTTP GET request in a REST API. By convention, GraphQL queries are operations to fetch data from a server without any side effects.

The GraphQL schema defines the query's structure, such as its arguments, fields, and return value. When defining a schema, the API developer declares queries as fields of the root type Query. A GraphQL server validates the query against the GraphQL schema before executing the attached query resolvers and projects the retrieved data onto the defined query fields. Query resolvers will run in parallel to ensure the best possible performance.

GraphQL Query Example

Let's start with a simple query for retrieving a welcome message from the server. The corresponding GraphQL schema with a Welcome object type and its query field welcome could look as follows:

To execute the welcome query, a developer can enter the following syntax into, e.g., the GraphiQL explorer:

In the above example, we used the shorthand syntax without the keyword query and without defining an operation name. If no operation type is specified, it's assumed to be a query by default. To make queries less ambiguous, however, the client should set the operation name when running in production, e.g.:

Serving GraphQL queries over HTTP

The de facto standard for serving GraphQL over HTTP looks as follows.

The operationName property is required if multiple query operations are present in the query. Batching of query operations is one of many advantages that GraphQL has over classical REST APIs. Important to note is that each query operation must have a unique name for the GraphQL server to execute them successfully. To achieve that, GraphQL provides a concept called aliasing.

Query Aliases and Fragments

Aliases are a great concept allowing clients to organize query results. Let's have a look at an example:

In the above example, we used aliases and fragments, which allow us to define once which fields the server should respond with. The operation can then reference the fragment from all queries that return objects of type Welcome. The result of the above query will look something like the following:

Concepts like aliases and fragments are a great way to reduce duplication and structure query responses. However, as applications usually do more than just welcome users, we will look at object relationships and nested queries in the next section.

Nested Queries

In real-world applications, different object types often are related to each other. In that case, when designing a data model, it helps to define the relationships between objects using simple terms, such as has-one or has-many. For example, a user of a Social Network has a profile, has many friends, has created many posts, and might have written many comments.

Now, these relationships can be defined as follows:

  • User has-one Profile
  • User has-many Friends
  • User has-many Posts
  • User has-many Comments

Schema for nested queries

In a GraphQL schema, the API developer defines the relationships between objects with a nested connection type that allows embedding the fields of the nested type to the query of a parent type. For example, let's assume we want to welcome many musicians instead of just one. In that case, the Welcome object has-many Musicians, and the schema would look as follows:

Please note that the exact syntax of how the schema defines a connection is up to the GraphQL server platform or if you're building a GraphQL API from scratch up to personal preference. With graphapi® we decided to append the word Connection to make it very explicit.

Running a nested query

A welcome query with the user connection would look as follows:

The result would look as follows:

Running a list query

If list queries are available, they allow querying all items of a given type in a similar fashion.

Running the query leads to the following result:

You might have already figured out that the client defines the response structure of a query by specifying aliases, arguments, and fields. This is another fundamental difference to classical REST APIs, where the server hardwires the response structure of a given request.

In the_beatles query above, we also used variables and query arguments. In the next section, we look deeper into these two concepts.

GraphQL Query Variables

We added arguments and values directly to the query string in previous examples. While this makes sense for demonstration purposes, GraphQL variables are better for production-ready apps, which change context often and need to react to dynamic user input.

To use variables, you need to do four things:

  • Replace static values in arguments with $variableName
  • Declare $variableName as a parameter of the query
  • Make sure that the variable type matches the expected schema type
  • Pass its value in a separate variables map

Using variables eliminates the need to manipulate the query string at runtime in app clients. All declared variables must be either scalars, enums, or input object types.

GraphQL Query Arguments

In REST-based APIs, clients pass arguments using query parameters or URL segments. In GraphQL, however, every field and nested object can have typed arguments. In addition to the default set of scalar types, e.g., String, Int, or Float, a GraphQL server also can declare custom types. What type of arguments are available depends on the particular GraphQL Server implementation.

With graphapi we are providing the following arguments for all list- and connection-queries:

  • limit:Int - limits the response length
  • nextToken:String - nextToken for pageination
  • sortDirection:SortOrderType - one of DESC or ASC for sort order
  • filter:TypeComparisonInput - filter based on object type fields

Conclusion

The GraphQL query is the core operation of GraphQL-based APIs. It offers a wide variety of options that influence the query response from a GraphQL server. Although the power and flexibility of GraphQL queries make GraphQL APIs easy to integrate for application developers, it can be challenging for teams tasked with implementing all the logic on the server side. With graphapi® we entirely abstract that complexity away, so teams can launch faster and focus on providing value for their customers.

Useful References