Introduction to GraphQL

A good starting point for learning GraphQL is to talk about its fundamental terminology and concepts.

GraphQL terminology

While studying and using GraphQL, a few terms will appear frequently. Understanding them is important to comprehending the graph query language (GraphQL) and communicating effectively with other professionals.

Schema

Every GraphQL service defines an application-specific type system, known as a schema. A schema defines the complete set of operations and data a client can access. Think of it as a blueprint that describes everything available for a client.

The primary function of the schema is to validate API calls. Calls from the client are executed against the schema and must follow its directives to be successful and return data.

The schema lives on the GraphQL API server, and a client can get information about it by performing an introspection operation. 

Query, Mutation, and Subscription

Query and Mutation are two operation types in GraphQL. They represent the types of action we wish to perform.

Queries retrieve data from the server. Compared to REST, GraphQL queries operate like GET requests.

Mutations change data on the server and fetch the changed data in a single operation. GraphQL mutations are analogous to performing HTTP verbs such as POST, PATCH, and DELETE.

In addition to queries and mutations, GraphQL supports a third operation type: subscriptions

Subscriptions enable you to fetch data. However, unlike queries, they will open and maintain an active connection with the GraphQL server (most commonly via WebSocket). Through this open connection, the server can notify the client in real-time about changes in the back-end data.

Due to its specific usage, subscriptions are the less common GraphQL operation. Currently, the Brikl GraphQL API does not provide any subscription operation.

Fields

Think of a field as a property from an object. GraphQL is basically about asking for specific fields on objects.

Let's look at a short query:

query {
 address {
   city
 }
}

The example above asks for the field city from the object address.

Here is the JSON response we get back from the GraphQL API server:

{
 "data": {
   "address": {
     "city": "Bangkok"
   }
 }
}

You can see immediately that the response has exactly the same shape as the query. This predictability of responses makes GraphQL friendly for developers: you always get back what you expect.

One important thing to note is that fields must always return scalar values. Schema validation will throw an error if you try to return a field that is not a scalar. If a field does not return a scalar value, you must add nested fields until all fields in a query return scalars.

Don't worry if it sounds confusing now. We'll dive deep into the details of API calls on Forming calls with GraphQL.

Scalars

In GraphQL, scalar types represent concrete data, such as integers, float numbers, strings, and booleans.

As we saw above, GraphQL objects have fields, and a field can contain another field or a final, concrete value, such as a string or number. This final value is known as a scalar value.

The official GraphQL website says: "A GraphQL object type has a name and fields, but at some point, those fields have to resolve to some concrete data. That's where the scalar types come in: they represent the leaves of the query."

In the following query, we ask for the city, latitude, and longitude fields: 

{
 address {
   city
   latitude
   longitude
 }
}

The response fields will resolve to scalar types: city is a string, while latitude and longitude are float numbers.

{
 "data": {
   "address": {
     "city": "Bangkok",
"latitude": 13.7563,
"longitude" 100.5018
   }
 }
}

To see all scalar types in the Brikl GraphQL API, see Scalars in the Admin API reference.

Argument

Sometimes we might want to retrieve data about a specific resource, such as a single customer order. Arguments can help us with this. By passing one or more arguments to fields, we can inform GraphQL about the data we want.

In the following query, orderById receives an id as an argument:

query {
 orderById(id: "a9acf424-931e-4fae-b1ee-a86bdc0e6076") {
   subTotal
   total
 }
}

The query will then return the required fields from the order with the specified ID:

{
 "data": {
   "orderById": {
     "subTotal": 24,
     "total": 38.8
   }
 }
}

In GraphQL, every field, including nested fields, can get its arguments. This possibility makes API calls extremely customizable. In fact, due to its powerful arguments feature, GraphQL eliminates the necessity of multiple API fetches to retrieve all data you might need.

Arguments can be of many different types. The same orderById query has a history field that receives a filter argument: 

query {
 orderById(id: "a9acf424-931e-4fae-b1ee-a86bdc0e6076") {
   subTotal
   total
   history(filter: {
     payloadTypes: [EMAIL_NOTIFICATION, BRIKL_SIMPLE_MESSAGE]
   }) {
     message
   }
 }
}

A filter argument is an object with a payloadTypes property, an array containing one or more values from an Enumeration type, which represents one of a finite set of options (in this case, payload types like EMAIL_NOTIFICATION and BRIKL_SIMPLE_MESSAGE).

We'll dive deep into arguments on Forming calls with GraphQL.

Implementation

In GraphQL, as in many type systems, the term "implements" indicates that an object inherits certain fields from an abstract type called Interface

Here's a simple example from the Brikl Admin API schema: 

interface LogData {
 type: AuditLogPayloadType!
}

type EmailNotificationLogData implements LogData {
 type: AuditLogPayloadType!
 data: EmailNotificationPayload!
}

In the code above, the type EmailNotificationLogData implements the interface LogData. That means EmailNotificationLogData must have the same fields/arguments/return types the interface LogData has. In this case, we can see that EmailNotificationLogData has the field type from LogData while adding a new particular field called data

Connection

Connections let you query related objects as part of the same call. With connections, you can use a single GraphQL call where you would have to use multiple calls to a REST API. Connection is also a pattern for implementing cursor-based pagination in GraphQL.

Edge

In short, edges represent a list of nodes (or objects, if you prefer). When you perform a query that returns a list (a list of orders, for example), you will usually get an array called edges containing a number of node objects. Here's an example:

query {
 orders {
   edges {
     node {
       total
     }
   }
 }
}

In the query above, we ask for a field called edges in orders. Within edges, we then ask for node. The node field represents the actual order object, from which we're retrieving the total field. Here's the response:

{
 "data": {
   "orders": {
     "edges": [
       {
         "node": {
           "total": 68.8
         }
       },
       {
         "node": {
           "total": 116.8
         }
       },
       {
         "node": {
           "total": 38.8
         }
       }
     ]
   }
 }
}

Node

In GraphQL, a node is simply an object. It is usually a field we request from edges, and it represents the actual resource we wish to retrieve information from. We can retrieve a node directly (using its ID) or access a list of nodes, as in the example above.

What's next?

Now that you learned helpful terminology and concepts for using the Brikl GraphQL Admin API, it's time to get more practical and set up your GraphQL Playground to start writing your first GraphQL query.