Web Development  

GraphQL for Beginners

When I first started learning about Architectual styles, I always had a question to the one I discovered:

Why do we need another way to build APIs?

If REST already allows a client to request data, and gRPC allows one service to call another service with high performance, and low latency, what problem is GraphQL actually solving?

The answer becomes clearer when we stop thinking about GraphQL as simply "another REST alternative" too.

GraphQL is mainly about giving the client more control over the data it requests.

Imagine an book store application. A book page might need the name, price, cover image, category, reviews, and information about the author.

With a traditional REST API, you might have:

GET /api/books/111

and the server decides what the response looks like:

{
  "id": 111,
  "name": "GraphQl from Scratch",
  "price": 1200,
  "image": "book.jpg",
  "category": "developer technology",
  "description": "...",
  "stock": 42,
  "createdAt": "...",
  "updatedAt": "..."
}

But perhaps your mobile application only needs:

name
price
image

The API still sends everything else.

Or perhaps the page needs information from several resources:

Book
 ├── Category
 ├── Reviews
 └── Author

The client might have to make several requests to retrieve everything it needs.

This is one of the problems GraphQL tries to address.

Instead of the server deciding exactly which fields to return, the client can describe the data it needs.

For example:

query {
  book(id: 111) {
    name
    price
    image
  }
}

The server can then return:

{
  "data": {
    "book": {
      "name": "GraphQl from Scratch",
      "price": 1200,
      "image": "book.jpg"
    }
  }
}

The client asked for three fields, and the response contains those three fields.

That is the central idea behind GraphQL.

So, what exactly is GraphQL?

GraphQL is a query language for APIs and a runtime for executing those queries.

It was originally developed at Facebook and later open-sourced.

The important word here is "query."

With REST, the API generally defines endpoints such as:

GET /books/111
GET /books/111/reviews
GET /books/111/author

With GraphQL, you generally have a single endpoint:

POST /graphql

The client sends a query describing the data it wants.

For example:

query {
  book(id: 111) {
    name
    price
    reviews {
      rating
      comment
    }
  }
}

GraphQL then executes that query against the server's data sources and returns the requested shape.

So instead of thinking:

"Which URL should I call?"

you start thinking:

"What data do I need?"

That is a major difference in the way the API is consumed.

REST vs GraphQL

Let's imagine we're building a food delivery application.

A restaurant has:

Restaurant
 ├── name
 ├── address
 ├── openingHours
 ├── menu
 │    ├── name
 │    ├── price
 │    └── ingredients
 └── reviews
      ├── rating
      └── comment

A REST API could expose:

GET /restaurants/42

Then:

GET /restaurants/42/menu

and:

GET /restaurants/42/reviews

The client may need three requests.

GraphQL could instead allow the client to ask for everything it needs:

query {
  restaurant(id: 42) {
    name
    address
    menu {
      name
      price
    }
    reviews {
      rating
      comment
    }
  }
}

One request can describe the entire data structure needed by the screen.

This doesn't mean GraphQL magically makes everything one database query. The server still has to retrieve the data. It simply gives the client a more flexible way to describe its requirements.

But where does GraphQL get its data?

GraphQL is not a database.

It doesn't replace SQL Server, PostgreSQL, MongoDB, or your existing services.

Think of GraphQL as a layer between the client and your underlying data sources.

For example:

2222

Your GraphQL server might retrieve information from:

SQL Server
PostgreSQL
REST APIs
gRPC services
Other microservices
External APIs

The GraphQL Schema

One of the most important concepts in GraphQL is the schema.

The schema describes what the API can provide.

For example:

type Book {
    id: ID!
    name: String!
    price: Float!
    description: String
}

type Query {
    book(id: ID!): Book
}

This tells us that the API provides a Product type.

A product has the following properties: id, name, price, description

And the API provides a query called: product

which accepts an ID.

The schema therefore acts as a contract between the client and the server. Like the .proto file in gRPC that we discussed in previous article.

Query, Mutation and Subscription

GraphQL has three major operation types.

Query:

Queries are used to retrieve data.

query {
    book(id: 111) {
        name
        price
    }
}

This is conceptually similar to retrieving data with:

GET /api/products/123

Mutation

Mutations are used when we want to change data.

For example:

mutation {
    createBook(
                    name: "GraphQl from Scratch"
                    price: 1200
    ) {
        id
        name
        price
    }
}

This could create a product.

Subscription

Subscriptions are designed for receiving updates when something happens on the server.

For example, imagine an application that displays the status of an order:

Order placed ---> Preparing ---> Out for delivery ---> Delivered

A subscription could allow the client to receive updates when the order status changes.

subscription {
    orderStatusChanged(orderId: 123) {
        status
    }
}

What makes GraphQL different from REST?

One of the biggest differences is who controls the response shape.

With REST:

     --- GET /products/123 --->
Client                      Server
      <--predefined response--

The server largely determines the representation.

With GraphQL:

      --- "I need name and price" --->
Client                          Server
      <---resolves requested fields---

The client describes the shape of the response.

This can help solve two common problems.

Over-fetching

Suppose your REST endpoint returns:

{
  "id": 123,
  "name": "Laptop",
  "description": "...",
  "price": 1200,
  "manufacturer": "...",
  "stock": 42,
  "createdAt": "...",
  "updatedAt": "..."
}

But the UI only needs:

name
price

The client receives information it doesn't need.

GraphQL allows:

query {
    product(id: 123) {
        name
        price
    }
}

The response can contain only those fields.

Under-fetching

The opposite problem can happen when one REST endpoint doesn't provide enough information.

For example:

GET /products/123

returns the product.

Then:

GET /products/123/reviews

returns the reviews.

Then:

GET /products/123/seller

returns the seller.

The client needs multiple requests to build one screen.

GraphQL can represent these relationships in one query:

query {
    product(id: 123) {
        name
        price

        seller {
            name
        }

        reviews {
            rating
        }
    }
}

When would I choose GraphQL?

GraphQL becomes particularly interesting when the client needs flexibility.

GraphQL allows each client to request the fields it needs.

This is one reason GraphQL is particularly popular for frontend-heavy applications.

⚠️ But GraphQL has its own problems

GraphQL isn't automatically better or faster.

In fact, one of the first problems you can encounter is surprisingly related to a database problem many .NET developers already know: the N+1 query problem.

Imagine:

query {
    products {
        name
        reviews {
            rating
        }
    }
}

You have 1,000 products.

A poorly implemented resolver might execute:

  • 1 query --> get products

  • 1000 queries --> get reviews for each product

That's the N+1 problem.

GraphQL gives the client a lot of power, so the server needs to control that power.

Imagine allowing a client to request:

product {
    relatedProducts {
        relatedProducts {
            relatedProducts {
                relatedProducts {
                    ...
                }
            }
        }
    }
}

Without limits, a malicious or simply badly designed query could become extremely expensive.

REST vs GraphQL vs gRPC

A small comparation before we roll wrap this up for the different architectural designs we know

RESTGraphQLgRPC
Typical transportHTTPHTTPHTTP/2
Data formatOften JSONUsually JSONProtobuf
StreamingPossible, but not its main modelSubscriptionsStrong support
Browser usageExcellentExcellentUsually requires gRPC-Web
Service-to-serviceVery commonPossibleExcellent
Strong contractOpenAPI can provide oneSchema.proto