I'll be honest, I'm a big fan of GraphQL. I love interfacing with it as a UI developer, and I love not having to make a new API call every time we want to deliver a different subset of data when working on the backend. It's a powerful contract that allows nice abstractions between layers of software.

I'm also not alone in feeling this way. GraphQL is seeing rapid adoption and popularity. This is particularly true in the JavaScript ecosystem, but you may be surprised to hear that Java based GraphQL APIs are also a thing!

Recently, I've been helping to onboard UI developers who are unfamiliar with GraphQL. In the process I've found myself repeating a handful of tips that are particularly impactful when starting to both read and create GraphQL queries (and mutations, fragments, etc).

Fair warning, this is not a GraphQL 101 post. If you've never worked with GraphQL before you'll want to go read up on it first. Moreover, these tips are centered around those consuming GraphQL APIs, rather than developers who are building schemas.

This looks like a wall of text

First things first, let's talk about making your life a bit easier. If you're like me, then you may be using the gql function from Apollo Client to write your queries inside JS/TS files. This is helpful and efficient! It also means all your queries are strings...

Not exactly ideal for scannability and finding your errors. I highly recommend a VS Code extension (or whatever editor you use) that provides syntax highlighting for GraphQL. I use the Apollo Client extension, but there are other good options as well.

Keywords

Now that you can read your code a bit better, let's talk about what you're looking at. We'll start with two examples.

The first is a bare-bones query.

const myQuery = gql`
{
some_query {
id
name
}
}
`

The second includes a minor change.

const myQuery = gql`
query {
some_query {
id
name
}
}
`

As it turns out, these code snippets are functionally the same. The query keyword is missing in the first example, but it isn't needed. GraphQL defaults to treating this like a query. However, not everything is a query!

Alternatively, the schema could have, confusingly, created a mutation called some_query, or a subscription. As a result, I tend to include all keywords for consistency.

Variable names vs Schema names

In teaching people about GraphQL I've found that one of the best ways to get more comfortable with the language is to learn what variables are up to the query author, and what is pre-determined by the schema. The schema gives names to all the queries it defines. However, the consumer of the GraphQL API can also provide a name for a given instance of that query.

Let's look at an example.

const myQuery = gql`
query myQuery {
some_query {
id
name
}
}
`

In this example, I chose myQuery as my JavaScript variable name. I also used that same name for my query instance. However, some_query is the name of the query defined in the schema. Learning to differentiate these pieces is helpful. So let's look at an example with more moving pieces.

const myQuery = gql`
query myQuery($something: String) {
some_query(filter: $something) {
id
name
}
}
`

The example above includes arguments that I'm passing to the query. $something is a variable name I get to choose. filter is the key that the schema has defined for the argument to this query. You'll often see people match these for clarity. So instead of $something, it would be $filter.

Fragments and re-use

I mentioned keywords earlier. As it turns out there is a keyword that isn't used in the schema and is purely for client use. It's called a fragment. A fragment is essentially a piece of a query that you can include in other queries. It's great for re-use if you find yourself querying the same fields on a given object in a number of places. Or if you want to get the same fields out of your mutation return object.

Let's suppose the query I want to run is this:

const myQuery = gql`
query myQuery {
some_query {
id
name
metadata {
description
url
}
}
}
`

I could define a fragment for that metadata object.

const metadataFragment = gql`
fragment metadataFragment on Metadata {
description
url
}
`

A few things to call out. metadataFragment is a variable name I get to choose. For consistency, I've also made it the name of the JavaScript variable I'm using. Metadata is the type of the metadata field from the query above. The schema determined that for me.

Now I can re-write my query to use the fragment.

const myQuery = gql`
${metadataFragment}
query myQuery {
some_query {
id
name
metadata {
...metadataFragment
}
}
}
`

It's important to point out that if I hadn't used metadataFragment for the name of both things this would look different. The first instance is the name of my JavaScript constant. The second is the name I used to define the GraphQL fragment.

Alias

One last piece to talk about is what happens if you need to send the same query, at the same time, with different inputs. This is where aliases come into play!

This example won't work. GraphQL won't know how to create a return object with two keys that have the name some_query.

const myQuery = gql`
query myQuery($something: String, $otherthing: String) {
some_query(input: $something) {
id
name
}
some_query(input: $otherthing) {
id
name
}
}
`

The fix is to give them alternative names.

const myQuery = gql`
query myQuery($something: String, $otherthing: String) {
thing: some_query(input: $something) {
id
name
}
other: some_query(input: $otherthing) {
id
name
}
}
`

Now, there are distinct keys in the return object. These alias names are completely up to me as they're about my consumption of the API, rather than the schema I'm interfacing with.

Just a few tricks

There is a lot more to GraphQL! You may find some of the things I've mentioned here to be helpful or familiar, you may find they don't correspond very well to how you work with GraphQL. It's a big wide world and we've just scratched the surface.