GraphQL - Introduction

RESTful service is one of the most popular design choices to connect client applications with servers. In REST, everything evolves around the idea of having resources that are accessible by URLs.

When designing RESTful services, you use a design pattern called CRUD (Create, Read, Update and Delete) around your resources and combined it with the HTTP protocol methods such as GET request, POST request, PUT and DELETE request.

The format for transferring data is not opinionated with RESTful design, but most often people will use JSON for it. In the end, RESTful design enables applications to communicate with each other by using plain HTTP with URLs and HTTP methods.

// a RESTful request with HTTP GET
https://api.domain.com/authors/7

// the response in JSON
{
  "id": "7",
  "name": "Henrik Grönvall",
  "avatarUrl": "https://domain.com/authors/7",
  "firstName": "Henrik",
  "lastName": "Grönvall"
}

What is GraphQL?

In short, GraphQL is an open source query language created by Facebook. Before GraphQL went open source in 2015, Facebook used it internally for their mobile applications as an alternative to the common REST architecture.

It allows requests for specific data, giving clients more control over what information is sent. This is more difficult with a RESTful architecture because the backend defines what data is available for each resource on each URL, while the frontend always has to request all the information in a resource, even if only a part of it is needed. This problem is called over-fetching. In worst case scenario, a client application has to read multiple resources through multiple network requests and adds the need for waterfall network requests.

A query language like GraphQL on the server-side and client-side lets the client decide which data it needs by making a single request to the server which leads to dramatically reduced network request.

A GraphQL operation is either a query (read), mutation (write), or subscription (continuous read). Each of those operations is only a string that needs to be constructed according to the GraphQL query language specification. Fortunately, GraphQL is evolving all the time, so there may be other operations in the future.

Once this GraphQL operation reaches the backend application, it can be interpreted against the entire GraphQL schema there, and resolved with data for the frontend application. GraphQL is not opinionated about the network layer, which is often HTTP, nor about the payload format, which is usually JSON. It isn't opinionated about the application architecture at all. It is only a query language.

// a GraphQL query
author(id: "7") {
  id
  name
  avatarUrl
  articles(limit: 2) {
    name
    urlSlug
  }
}

// a GraphQL query result
{
  "data": {
    "author": {
      "id": "7",
      "name": "Henrik Grönvall",
      "avatarUrl": "https://domain.com/authors/7",
      "articles": [
        {
          "name": "Handle complex object in JavaScript",
          "urlSlug": "mediator pattern"
        },
        {
          "name": "Node.js worker threads",
          "urlSlug": "nodejs-worker-threads"
        }
      ]
    }
  }
}

As you can see, one query can requests multiple resources (author and articles), where a RESTful architecture needs at least two waterfall requests to retrieve the author entity and its articles.

In addition, the query only selected the necessary fields instead of the whole entity, left out the firstName and lastName from the author entity for example.

Pros with GraphQL

Declarative Data Fetching

GraphQL embraces declarative data fetching with its queries. The client selects data along with its entities with fields across relationships in one query request.

No Over-fetching

A mobile client usually over-fetches data when there is an identical API as the web client with a RESTful API. With GraphQL, the mobile client can choose a different set of fields, so it can fetch only the information needed for what's on screen.

React, Vue, Angular, Node, Express, Koa, etc

Facebook showcased GraphQL on a client-side application with React, it is decoupled from any frontend or backend solution. The reference implementation of GraphQL is written in JavaScript, so the usage of GraphQL in Angular, Vue, Express, Hapi, Koa and other JavaScript libraries on the client-side and server-side is possible, and that's just the JavaScript ecosystem. GraphQL does mimic REST's programming language-agnostic interface between two entities, such as client or server.

Single Source of Truth

The GraphQL schema is the single source of truth in GraphQL applications. It provides a central location, where all available data is described. The GraphQL schema is usually defined on server-side, but clients can read (query) and write (mutation) data based on the schema.

Schema Stitching

Schema stitching makes it possible to create one schema out of multiple schemas. Think about a microservices architecture for your backend where each microservice handles the business logic and data for a specific domain. In this case, each microservice can define its own GraphQL schema, after which you'd use schema stitching to weave them into one that is accessed by the client. Each microservice can have its own GraphQL endpoint, where one GraphQL API gateway consolidates all schemas into one global schema.

Introspection

A GraphQL introspection makes it possible to retrieve the GraphQL schema from a GraphQL API. Since the schema has all the information about data available through the GraphQL API, it is perfect for auto-generating API documentation. It can also be used to mock the GraphQL schema client-side, for testing or retrieving schemas from multiple microservices during schema stitching.

Strongly Typed

GraphQL is a strongly typed query language because it is written in the expressive GraphQL Schema Definition Language (SDL). Being strongly-typed makes GraphQL less error prone, can be validated during compile-time and can be used for supportive IDE/editor integrations such as auto-completion and validation.

Versioning

In GraphQL there are no API versions as there used to be in REST. In REST it is normal to offer multiple versions of an API (e.g. api.domain.com/v1/, api.domain.com/v2/), because the resources or the structure of the resources may change over time. In GraphQL it is possible to deprecate the API on a field level. Thus a client receives a deprecation warning when querying a deprecated field. After a while, the deprecated field may be removed from the schema when not many clients are using it anymore. This makes it possible to evolve a GraphQL API over time without the need for versioning.

Cons with GraphQL

Complexity

GraphQL doesn't take away performance bottlenecks when you have to access multiple fields (authors, articles, comments) in one query. Whether the request was made in a RESTful architecture or GraphQL, the varied resources and fields still have to be retrieved from a data source. As a result, problems arise when a client requests too many nested fields at once so there must be a mechanism like maximum query depths, query complexity weighting, avoiding recursion, or persistent queries for stopping inefficient requests from the other side.

Rate limiting

Another problem is rate limiting. Whereas in REST it is simpler to say "we allow only so many resource requests in one day", it becomes difficult to make such a statement for individual GraphQL operations, because it can be everything between a cheap or expensive operation. That's where companies with public GraphQL APIs come up with their specific rate limiting calculations which often boil down to the previously mentioned maximum query depths and query complexity weighting.

Caching

Implementing a simplified cache with GraphQL is more complex than implementing it in REST. In REST, resources are accessed with URLs, so you can cache on a resource level because you have the resource URL as identifier. In GraphQL, this becomes complex because each query can be different, even though it operates on the same entity. You may only request just the name of an author in one query, but want to know the email address in the next. That's where you need a more fine-grained cache at field level, which can be difficult to implement. However, most of the libraries built on top of GraphQL offer caching mechanisms out of the box.


Published: 2020-02-03
Author: Henrik Grönvall
Henrik Grönvall
Copyright © 2022 Henrik Grönvall Consulting AB