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 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:
- Client sends a GraphQL query
- Server validates it against a Schema
- Resolvers fetch data from sources like databases, REST APIs...
- 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
}