GraphQL 101: Query Language Part 2
A quick recap of GraphQL 101: Query Language Part 1: the parts of the query language were presented and we looked at what those parts do along with examples of queries and mutations. In this post, we'll finish covering operations topics with fragments, directives, and Subscriptions. More examples will be added here and we'll wrap up this discussion of the query language that clients use to request and alter data.
- Fragments - there are three types of fragments: named fragments allow us to reuse fields, type conditions allow us to conditionally select fields, and inline fragments that don't have a name are defined inside the selection set.
- Directives - there are four directives defined in the spec: @skip, @include, @specifiedBy, and @deprecated. All directives begin with the
@
symbol and change how a section of the document is validated or executed by the server. Custom directives are allowed and frequently used. - Subscriptions - These are long-lived requests that allow the server to send the client events as they happen.
FUN FACT: Facebook added subscriptions to GraphQL to enable updating "Like" clicks.
Fragments
Fragments provide a mechanism for reusing groups of fields or applying conditions to their use in a query. They are introduced in an object and initiated with three dots, ...
. There are three types as illustrated in the examples below: named fragments, type conditions, and inline fragments. Fragments are defined on a type and those types can be an object, an interface or a union.
Named Fragment
Suppose you want to collect the number of watchers, forks and stars for several repositories. This fragment we named "repository" groups the fields we want so that we can call this fragment again as "...repository" in a new Github Respository within the query.
#This will return the repository name, url and date/time
it was created with the number of stars, watchers and forks
for a specific repo.
fragment repository on Repository {
name
url
createdAt
stargazers {
totalCount
}
watchers {
totalCount
}
forks {
totalCount
}
}
Type Conditions
Type Conditions are attached to either interfaces or unions. In this case, we have an interface called user. The server will use the fragment that fits the type returned.
#This fragment will return the profilePic and isOnline values
for active users or the suspensionReason and reactivateOn values
if the user is suspended.
query {
user($id: ID!) {
id
name
...activeFields
...suspendedFields
}
}
fragment activeFields on ActiveUser {
profilePic
isOnline
}
fragment suspendedFields on SuspendedUser {
suspensionReason
reactivateOn
}
Inline Fragments
This query uses variables for owner and name and adds an inline fragment for Tree and Blob which are defined in the Github API.
#This query returns a list of files for a specific
owner and repo.
query RepoFiles($owner: String!, $name: String!) {
repository(owner: $owner, name: $name) {
object(expression: "HEAD:") {
... on Tree {
entries {
name
type
mode
object {
... on Blob {
byteSize
text
isBinary
}
}
}
}
}
}
}
Directives
Directives can be added after various parts of a document to change how that part is validated or executed by the server. They begin with an @
symbol and can have arguments. There are four included directives, @skip, @include, @specifiedBy and @deprecated, and servers can define custom directives.
@skip
@skip(if: Boolean!) is applied to a field or fragment spread. The server will omit the field/spread from the response when the if argument is true.
#If there's no image, the response won't try to return an
image from the profilePic.
query User($id: Int!, $textOnly: Boolean!) {
user(id: $id) {
id
name
profilePic @skip(if: $textOnly)
}
}
@include
@include(if: Boolean!) is the opposite of @skip, only including the field/spread in the response when the if argument is true.
#This @include will add the email address and group
memberships to a response if the user is an admin.
query User($id: Int!, $adminMode: Boolean!) {
user(id: $id) {
id
name
email @include(if: $adminMode)
groups @include(if: $adminMode)
}
}
@specifiedBy
#Here we use @specifiedBy to provide as resource for
generating a unique user id or UUID.
scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
@deprecated
Unlike @skip and @include, which are used in executable documents, @deprecated is used in schema documents. It is placed after a field definition or enum value to communicate that the field/value is deprecated and why — it has an optional reason String argument that defaults to “No longer supported.”
#This is mechanism that GraphQL uses to avoid the need
for versioning - well mostly that's the idea.
type User {
id: Int!
name: String
fullName: String @deprecated("Use `name` instead")
}
Subscriptions
Subscriptions are long-lived requests in which the server sends the client data from events as they happen. The manner in which the data is sent is not specified, but the most common implementation is WebSockets and webhooks (when the client is another publicly addressable server).
#A subscription to the creation of new issues on Github
could look something like this in its simpliest form. How
the schema defines the newIssues type will play a heavy
role on how this works.
subscription newIssues($query_string: String!) {
search(query: $query_string, type: ISSUE, last: 1) {
issueCount
}
#This is a more contrived example of what a client
Subscription would look like for a bookstore.
subscription {
reviewCreated {
id
text
createdAt
author {
name
photo
}
}
}
Conclusion
This completes the Graph 101: Query Language discussion. There will be other aspects of the basic build blocks of GraphQL as defined by the Specification including what's absent in the next level.
The next topics to cover are the Type System. A GraphQL Schema is a collection of types described using SDL (Schema Definition Language) though the syntax of a schema can, and often is, provided in a different programming language.
And finally, we look at validation and execution. There have been some changes to how GraphQL is executed in the last year. A few companies are replacing the GraphQL engine with a compiler. As a growing trend, it makes sense to at a minimum mention it. Mostly, we'll focus on the execution engine for our 101-level discussion.