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"
}
}
}
Query:
{
query User {
name
}
}
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]
}
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
}
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
}
}
}
}
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
}
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"
}
}
The
!
here means that this field cannot hold the value ofnull
. In other words, each user must have anid
andname
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
}
}
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
}
}
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."
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
}
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
}
With Fragments:
query getUsers {
subscribers: user(subscriptionStatus: SUBSCRIBED) {
id
...AccountInfo
nonSubscribers: user(subscriptionStatus: UNSUBSCRIBED) {
id
...AccountInfo
}
fragment AccountInfo on User{
email
name
}
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
}
}
Variable Dictionary:
"subscriptionStatus": "SUBSCRIBED"
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
}
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 istrue
. -
@skip(if: Boolean)
Skip this field if the argument istrue
. 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)
}
}
Variable Dictionary:
"showEmail": true
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!
Top comments (0)