DEV Community

Cover image for GraphQL Tutorial: how to use fields, fragments, and more
Ryan Thelin for Educative

Posted on • Originally published at educative.io

GraphQL Tutorial: how to use fields, fragments, and more

Data query and manipulation are experiencing a historic rise in popularity as modern companies become more reliant on data for day-to-day tasks. Businesses are looking for candidates and technologies that can efficiently generate results despite high volumes of complex data.

GraphQL (Graph Query Language) is the answer many companies are looking for. GraphQL offers tools for complex queries and a less-is-more approach to fetch calls, and it is projected to soon overshadow the REST API format as the query language of tomorrow's job market.

Today, we'll explore the key pieces of GraphQL and show you how to implement each in your own schemas.

Here’s what we’ll cover today:

Upskill in less than 2 hours

Learn fullstack GraphQL fast with hands-on practice and projects.

Up and running with Node and GraphQL

What is GraphQL

GraphQL is a query language for APIs that includes a server-side runtime to execute queries. GraphQL is used alongside open-source back-end server software like Node, Express, or Apollo.

Developed by Facebook in 2012, GraphQL is designed to reduce the number of empty fields and roundabout fetch calls common with the RESTful API formats.

As GraphQL has developed, the philosophy has continued to prioritize reducing the number of steps for any behavior.

REST API stores objects at specific URLs, but GraphQL has a user-created type system that acts as a template by defining a set of fields that each object of that type will have. You can make many objects of the same type, each with its own values for the defined fields.

This is similar to the relationship between classes and objects in OOP languages like Java.

Schema:

{
  data: {
    User: {
      name: "Jane Doe"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Query:

{
  query User {
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

At the simplest level, GraphQL is about asking different objects for the value of a specific field. The advantage here is that GraphQL always knows exactly what information you need and returns only the data you want.

GraphQL allows you to go beyond this simple operation with complex queries that navigate nested objects or modify fetched data using object mutations.

GraphQL Building Blocks

The fundamental building blocks of GraphQL are the schema and queries.

Schema:

The GraphQL schema outlines the categories or type that the data can be split into. It also defines what information will be contained in each type. Think of this as a blueprint to display the architecture of your database.

   type Book {
        id: ID
        title: String
        published: Date
        author: Author
    }
    type Author {
        id: ID
        name: String
        book: [Book]
    }
Enter fullscreen mode Exit fullscreen mode

Queries:

Once your data has been mapped out, you need a way to fetch it. GraphQL queries request data by following an input route to the data endpoint. The returned information is called a payload.

These can be simple requests, like fetching a book name and author by its ID.

    type Query {
        book(id: ID): Book
        author(id: ID): Author
    }
Enter fullscreen mode Exit fullscreen mode

Queries can also be complex, like
asking for the name and bio, and the name of all the books they've written.

    {
        book(id: 100) {
            title
            isbn
            date
            author {
                name
                bio
                books {
                name
                }
            }
        }
    }
Enter fullscreen mode Exit fullscreen mode

Below, explore both schema and queries deeper by learning some of the more specific elements of GraphQL.

Fields

A field is essentially an object-specific attribute that holds a value. The object's parent type defines which fields an object must have. Each field is set at definition to hold certain data types, such as String or Enum.

Let's take a look at an example:

type User {
   id: String!
   email: String!
   name: String
}
Enter fullscreen mode Exit fullscreen mode

Here, we have a type User that we'll use as a template to represent individual users. Each object of type User will have three fields: id, email, and name.

Fields can also reference other objects to create hierarchies between different types of objects. For example, we could add a friends field to our User that contains a list filled with the names of other users.

type User {
   id: String!
   email: String!
   name: String
"friends": [
        {
          "name": "John Doe"
        },
        {
          "name": "Jane Doe"
        },
        {
          "name": "Guy Manson"
        }
}
Enter fullscreen mode Exit fullscreen mode

The ! here means that this field cannot hold the value of null. In other words, each user must have an id and email, but name is optional.

GraphQL can fetch the entire friends list object at once or traverse the object to find a specific piece of data. This function allows you to fetch large amounts of related data/objects in just a single query.

Arguments

One of the most useful parts of GraphQL is that you can pass arguments to any field or object within a query. Fields accept arguments similar to functions in other languages, in that arguments have a name and a passed value. The object/field will then use this value wherever the argument's name is referenced.

In REST API, you can only send the query parameters and the URL segment for the query. GraphQL's approach allows you to skip several queries by passing arguments to anything and receiving the exact information needed in just a single query.

The most common use of arguments is to filter which object you're querying for within a type. For example, we could include the getUser field in our User type that accepts an id argument. Since each user has a specific id, this will allow us to easily pull the information about a specific user.

{
  getName(id: "1010") {
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

Aliases

GraphQL will throw an error if we query the same field with different arguments. Imagine we have our user objects and want to filter them by an implemented "subscriptionStatus" argument.

query getUsers {
  user(subscriptionStatus: SUBSCRIBED) {
  id
  email
  name
  }
  user(subscriptionStatus: UNSUBSCRIBED) {
  id
  email
  name
  }
}
Enter fullscreen mode Exit fullscreen mode

This would throw an error because the later query on the users type will overwrite the previous one.

message: "Fields "user" conflict because they have differing arguments. Use different aliases on the fields to fetch both if this was intentional."
Enter fullscreen mode Exit fullscreen mode

As the error says, we need to set aliases for these queries to fetch both at once. You can think of aliases as nicknames for specific subgroups within a type.

We'll set alias subscribers for user objects with a subscriptionStatus of SUBSCRIBED:

query getUsers {
  subscribers: user(subscriptionStatus: SUBSCRIBED) {
  id
  email
  name
  }
Enter fullscreen mode Exit fullscreen mode

We can use the subscribers alias later as a shortcut to query this subgroup of user whenever we want. Aliases are an effective way to divide wider types into more specific groups that you'll frequently query together.

Keep learning about GraphQL

Widen your skillset with experience in GraphQL and Nodejs. Educative's mini-courses give you the hands-on experience to grow as a developer in less than 2 hours.

Up and running with Node and GraphQL

Intermediate GraphQL Concepts

Fragments

In complex applications, you'll likely have several operations that reference the same fields. To shorthand this, GraphQL includes fragments that allow you to wrap a set of fields for reuse across your different queries. Fragments are defined for an object type, like User, and they can be used in any operation that features those objects.

Below, we'll remake our previous arguments example but this time replace the identical fields with our AccountInfo fragment.

Without Fragments:

query getUsers {
     subscribers: user(subscriptionStatus: SUBSCRIBED) {
  id
  email
  name
  }

  nonSubscribers: user(subscriptionStatus: UNSUBSCRIBED) {
  id
  email
  name
  }
Enter fullscreen mode Exit fullscreen mode

With Fragments:

query getUsers {
     subscribers: user(subscriptionStatus: SUBSCRIBED) {
  id
  ...AccountInfo

  nonSubscribers: user(subscriptionStatus: UNSUBSCRIBED) {
  id
  ...AccountInfo
  }



fragment AccountInfo on User{
  email
  name
}
Enter fullscreen mode Exit fullscreen mode

Both of these code segments accomplish the same behavior. The advantage of fragments is that they simplify our queries for readability and allow us to modularize queries for reuse.

Variables

Sometimes we'll want to include the option for dynamic arguments in our queries, such as when making a search bar. GraphQL allows dynamic arguments using variables. Variables act as a placeholder that points to a field within a paired JSON file.

To implement a variable, we have to make three changes:

  • Replace the static argument with a variable anchor, $subscriptionStatus: Subscription
  • Declare $subscriptionStatus as one of the variables accepted by the query
  • Pass Subscription: value in a separate variable dictionary file (usually JSON)

Query:

query getUsers ($subscriptionStatus: Subscription) {
    user(subscriptionStatus: $subscriptionStatus) {
  id
  ...AccountInfo
    }
}
Enter fullscreen mode Exit fullscreen mode

Variable Dictionary:

"subscriptionStatus": "SUBSCRIBED"
Enter fullscreen mode Exit fullscreen mode

Now we can change which group of users we're analyzing across the entire program by simply changing the value of subscriptionStatus in the variables dictionary.

Variables, therefore, allow your queries to be adaptable and widen the reusability of behaviors.

Mutations

While queries let you fetch data, mutations let you create, update, or delete server-side data. You can think of mutations as the GraphQL equivalent of POST from REST API.

To implement a mutation, you need to set the field name and the arguments it will accept. Imagine we're trying to add a mutation that allows us to create more User objects. We need to create a mutation query that will accept all the essential information for account creation:

mutation createUser(email: String!, password: String!) {
   createUser(email: $email, password: $password) {
      id
      email
      password
}
Enter fullscreen mode Exit fullscreen mode

First, we declare that createUser will be a mutation and accept arguments named email and password. Then in line 2, we declare that these arguments will be used to populate the separate email and password fields below.

Mutations exist on a spectrum between fine-grained, meaning it only edits a few specific fields, and coarse-grained, which edits entire types.

Directives

Sometimes we only want to fetch a field's value under certain conditions. To do this, we can use directives, which tells the server to skip or include a field. Directives always include a conditional statement like if, and a boolean variable.

With this feature, you can skip tricky string manipulations or allow you to implement "show more" buttons on UI readouts.

The two types of basic directives act as logical switches. Each directive activates if the boolean is true to avoid double negatives; include indicates to shows the field when the boolean is true and skip indicates not to show the field when the boolean is true.

  • @include(if: Boolean) Only include this field in the result if the argument is true.
  • @skip(if: Boolean) Skip this field if the argument is true. Imagine we want to fetch user data but only want to include the email address if a specific setting is ticked.

Query:

{
query getUsers {
User {   
   name
   email @include(if: $showEmail)
  }
}
Enter fullscreen mode Exit fullscreen mode

Variable Dictionary:

"showEmail": true
Enter fullscreen mode Exit fullscreen mode

What to learn next

Now that you've seen all the basic pieces of GraphQL in action, you're ready to explore more advanced concepts like resolvers or combining GraphQL with back-end software.

API and data manipulation are rising in demand as more businesses adopt data-driven methods. Now is the perfect time to upskill with GraphQL.

Setting up your own GraphQL server with Nodejs is the best way to practice your skills. To help you do that, Educative has created the Up and running with Node and GraphQL. This mini-course acts as a compact crash course to pivot JavaScript developers to GraphQL API implementations.

By the end, you'll have hands-on experience launching and manipulating your own GraphQL server.

Happy learning!

Continue reading about APIs and databases

Top comments (0)