Skip to main content

Introduction to GraphQL

Learn the core concepts of GraphQL, why to use it and how it differs from REST.


What is GraphQL?

GraphQL is an open-source query language for APIs and a server runtime for fulfilling those queries.

Unlike REST, where you might need to hit multiple endpoints to gather related data (over-fetching) or receive large JSON blobs with fields you don't need (under-fetching), GraphQL allows you to fetch all the data you need in a single request.

GraphQL vs REST

GraphQL isn't tied to any specific database or storage engine - it is backed by your existing code and data.

The core idea

In practical terms, GraphQL comprises two separate parts:

  • GraphQL query language - specific language that enables API client to specify their intent, such as what data to query, which action to perform...
  • backend service able to execute the above requests

The high level flow is usually something like:

  1. Client sends a GraphQL query
  2. Server validates it against a Schema
  3. Resolvers fetch data from sources like databases, REST APIs...
  4. Server returns JSON shaped response containing only the requested fields

Explain it using REST

As most developers are already familiar with REST APIs, I find it easiest to explain GraphQL just by drawing a parallel with a similar REST API.

Let's imagine we want to build a website showing some info about movies. We would like to display a page that lists movie name, year it was released, some reviews and some awards the movie received or has been nominated for.

In REST, we could model this something like:

GET https://movieDB.com/movies
[
{
"id": 1,
"title": "Star Wars: Episode IV - A New Hope",
"genre": "Adventure Epic",
"releaseYear": 1977,
"durationMinutes": 121,
"originalTitle": "Star Wars",
"summary": "Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a Wookiee and two droids to save the galaxy"
},
{
"id": 2,
"title": "The Matrix",
"genre": "Cyberpunk",
"releaseYear": 1999,
"durationMinutes": 136,
"originalTitle": "The Matrix",
"summary": "Neo discovers the shocking truth--the life he knows is the elaborate deception of an evil cyber-intelligence"
}
]

GET https://movieDB.com/movies/reviews?movieIds=1,2
[
{
"id": 111,
"movieId": 1,
"rating": 9.5,
"title": "Visually stunning sci-fi",
"comment": "The story starts slow but really pays off.",
"userId": 42,
"createdAt": "2024-06-12T14:23:00Z"
},
{
"id": 701,
"movieId": 2,
"rating": 9.5,
"title": "Life changing special effects",
"comment": "The world-building and visuals were incredible.",
"userId": 777,
"createdAt": "2024-06-12T14:23:00Z"
}
]

GET https://movieDB.com/movies/awards?movieIds=1,2
[
{
"id": 111,
"movieId": 1,
"name": "Oscar",
"category": "Best Picture",
"status": "NOMINATED"
},
{
"id": 121,
"movieId": 2,
"name": "Oscar",
"category": "Best Effects, Visual Effects",
"status": "WON"
}
]

This is of course a very simplified example, but the general idea is there - we need to make 3 separate requests, but also, we get a lot of fields back that we're not that interested in at the moment.


The same request in GraphQL would look something like:

POST https://movieDB.com/graphql
{
movies {
title
releaseYear
reviews {
rating
}
awards {
name
category
}
}
}

That single request will return all data we need for this specific page:

{
"data": {
"movies": [
{
"title": "Star Wars: Episode IV - A New Hope",
"releaseYear": 1977,
"reviews": [
{
"rating": 9.5
},
{
"rating": 9.8
}
],
"awards": [
{
"name": "Oscar",
"category": "Best Picture"
}
]
},
{
"title": "The Matrix",
"releaseYear": 1999,
"reviews": [
{
"rating": 9.2
},
{
"rating": 9.9
}
],
"awards": {
"name": "Oscar",
"category": "Best Effects, Visual Effects"
}
}
]
}
}

Request flow comparison

Everything above can be briefly visualized as:

You can read a bit more about REST vs GraphQL comparison in this blog post

GraphQL Schema (the Contract)

A GraphQL service is created by defining types and their fields in a Schema, and we'll go through it in detail in a dedicated chapter, but for now, just so you have a general idea, this is how a Schema might look for the example service we defined above. Don't worry if you can't understand it, that is what we focus on in the next chapter.

"""
ISO-8601 timestamp, e.g. "2024-06-12T14:23:00Z".
Commonly implemented by libraries (GraphQL Scalars, Spring, Apollo, etc.).
"""
scalar DateTime

"""
A movie available in the catalog.
"""
type Movie {
"""
Globally unique identifier (preferred over Int for GraphQL clients/caching).
"""
id: ID!

title: String!

"""
Some movies have different titles by region/language.
Null if identical/unknown.
"""
originalTitle: String

"""
Short plot description .
"""
summary: String

"""
Many systems allow multiple genres. Use a list for flexibility.
Example: ["Cyberpunk", "Action"].
"""
genres: [String!]!

releaseYear: Int!

durationMinutes: Int!

"""
Reviews submitted for this movie.
"""
reviews: [Review!]!

"""
Awards associated with this movie (nominations and wins).
"""
awards: [Award!]!
}

"""
A user review for a movie.
"""
type Review {
id: ID!

"""
Numeric rating. Scale is product-specific (e.g., 0-10).
"""
rating: Float!

"""
Short headline for the review.
"""
title: String

"""
Full review text/body.
"""
comment: String

createdAt: DateTime!

"""
Best practice: represent relationships as objects.
"""
movie: Movie!

"""
Keep movieId for educational parity with REST/DB models, but prefer `movie`.
"""
movieId: ID! @deprecated(reason: "Prefer the `movie` field (GraphQL relationship).")

"""
If you have a User type, expose the relationship.
"""
user: User

"""
Keep userId for educational parity, but prefer `user`.
"""
userId: ID @deprecated(reason: "Prefer the `user` field (GraphQL relationship).")
}

"""
Minimal user shape (we'll expand this later).
"""
type User {
id: ID!
displayName: String
}

"""
An award associated with a movie (nomination or win).
"""
type Award {
id: ID!

"""
Awarding body or award name, e.g. "Oscar", "BAFTA".
"""
name: String!

"""
Category is often free-form and varies by organization/year,
so String is safer than an enum for real-world use.
"""
category: String!

status: AwardStatus!

"""
Relationship (preferred in GraphQL).
"""
movie: Movie!

"""
Kept for parity with REST/DB, but prefer `movie`.
"""
movieId: ID! @deprecated(reason: "Prefer the `movie` field (GraphQL relationship).")
}

enum AwardStatus {
NOMINATED
WON
}

type Query {
"""
List movies. In real APIs, you typically paginate.
"""
movies(limit: Int = 20, offset: Int = 0): [Movie!]!

"""
Fetch a single movie by ID.
"""
movie(id: ID!): Movie
}