Blog

GraphQL 101: The What, the Why, and the How

April 8, 2019 · by Monika Rathor ·
Categories:

GraphQL is an open-source specification for data retrieval from server to client. It was originally developed internally by Facebook in 2012, and made publicly available in 2015. But what drove the need for yet another specification to fetch data from a server? It was the constantly growing adoption of mobile applications. Unlike desktop applications, mobile apps have to rely on limited resources, including network bandwidth, CPU power, and battery life.

Generally, network applications use RESTful APIs to fetch data. But RESTful APIs have problems, such as over-fetching and under-fetching of data, that degrade mobile application performance. Over-fetching means there is data in the response that the client does not need, and gets thrown away; under-fetching implies that the client is not getting all the required data with one call to an endpoint, forcing the client to make multiple API requests. Both of these conditions are inefficient.

GraphQL addresses both conditions. It is a strongly typed query language that allows the client to specify the data it needs from the server. GraphQL is not a framework, but a technology-implementation-agnostic specification. With the GraphQL spec, a client sends a GraphQL query request in GET query or POST body and the server will then parse, resolve, and execute the query, and return the requested data. The server can aggregate data from single or multiple sources in the background. This process increases efficiency by reducing over-fetching and under-fetching.

Why does GraphQL enjoy such wide adoption?

Because GraphQL has the flexibility to allow the client to specify what data it needs, GraphQL has become a popular alternative to REST. In fact, over a six-month period, the number of companies with unique GraphQL endpoints on the Akamai platform increased by 68%.

How does GraphQL operate?

A GraphQL server can support read, write, and subscribe actions by defining those operations on the server. In GraphQL, those three actions are known as query, mutation, and subscription. Here’s a definition of each:

  • Query: Queries are a read-only operation, used by the client to request data from the server.
  • Mutation: Mutations are used to write new data, update existing data, or delete data.
  • Subscription: A GraphQL client can subscribe to an event on the server, and when the event occurs, the server notifies the client on an already-established channel between client and server.

What are the components of GraphQL?

The diagram below shows the GraphQL client-server-backend architecture, and following that is a brief look at each component of the architecture.

architecture diagram

 

  • Client: A network client requests data using the GraphQL language. The GraphQL specification allows clients to send a query to request data.
  • Server: The server is the component that parses incoming GraphQL queries, then resolves and executes the query to send a response back to the client. The query parser, validator, and resolvers are core GraphQL server components.
  • Parser: The parser tokenizes each query according to specification, and converts the query into a representation that the machine can understand. The parser is responsible for checking the syntax of the incoming string.
  • Schema Validation: After syntactical analysis of the GraphQL query by the parser, the server performs semantic checks by verifying and validating the query against the schema definition (a schema defines the structure of objects and the relationship between them). This allows queries to jump to different points in the graph and navigate.
    • GraphQL also offers other schema advantages: with GraphQL, the API schema and the database schemas are decoupled, which offers the flexibility of connecting API schema with many backends while hiding complex database schema from API users.
  • Resolver: A RESTful API can have multiple endpoints, but with GraphQL, there’s a single, multifunction URL that can interpret, parse, process, execute query payload, and send the requested data back to the client. That means there is a need for an intelligence layer between various client data sources since the server receives client requests and has to fetch the necessary data according to the client’s instructions. This intelligence layer is created by using resolver functions. Clients can write code in resolver functions to get data from a backend database. This allows the GraphQL server to respond to different data sources from a single point of entry.

What does the “Graph” stand for in GraphQL?

In the GraphQL world (unlike RESTful APIs), we don't think in terms of endpoints, we think of graphs. With GraphQL, a business domain is modeled as a graph by defining a schema. Within the schema, different types of nodes and the way they connect or relate to one another are defined. A request lands on the graph’s root node in the graph schema and navigates the graph to fetch the fields requested in the GraphQL query.

Using a blog post as an example, let’s look specifically at how a GraphQL query lands and navigates through the graph. In this example, Author and BlogPost are the nodes of the graph. A BlogPost may consist of one or more authors, and the relationship between Author and BlogPost is represented with a line between these two nodes. The Author node consists of ID, name, and a list of BlogPosts, and the BlogPost node has ID, title, and author, as in this diagram:

author and post diagram

Let’s take a look at what a schema for this might look like:

Type Author {

           id : ID!

           name : String

           blogs : [BlogPost]

}

 

Type BlogPost {

            id : ID!

            title : String

            author : Author

}

 

Type Query {

            author(id : ID !) : Author

            blog(id : ID !) : BlogPost

}

The “Type Query” is the root object type, which is the entry point of the request. A request lands at the root object type and navigates to the next node from there.  For example, look at a query for a blog post with an ID of 100. The query below makes a request for all the blog IDs and titles that the current blog author has posted:

query {

       blog (id : "100")

       {

               author

               {

                       blogs

                       {

                             id

                             title

                       }

               }

       }

}

This query response is visually represented by the diagram below:

query response diagram

 

The query first lands on node blog with id 100, finds its Author, then obtains all the other blogs written by the Author.

This example shows how a client can avoid the N+1 problem by asking for only the specific data it needs. The N+1 problem arises when a client application is required to make N+1 calls to the server to fetch one resource. This is the over-fetching issue we discussed earlier.

Caching GraphQL at the Edge

According to the GraphQL caching page, in a typical endpoint-based RESTful API, edge servers can use HTTP caching to avoid refetching resources, since it’s easy to identify when two resources are identical. In a RESTful API, the URL acts as a unique identifier that can be used to build a cache key. However, in GraphQL, there is no URL-like primitive that provides a globally unique identifier for a given object.

Caching is a necessary component of API scalability, but is notably absent from the GraphQL specification. Fortunately, Akamai provides efficient GraphQL query caching by normalizing queries ordering fields and arguments. Akamai also supports GraphQL advanced primitives like variables, directives, and fragments processing at the edge.

To learn more about how Akamai edge servers help improve performance and security for GraphQL servers, see GraphQL Query Parsing and Caching at the Edge.

Monika Rathor is a senior software engineer at Akamai Technologies.