One of the key problems GraphQL aims to solve is the very idea of only making one API request to get all data needed, instead of constructing custom endpoints per use case.
GraphQL is a data query language and runtime designed and used at Facebook to request and deliver data to mobile and web apps since 2012.
About three years ago, Facebook started working on GraphQL. It was invented during the switch from HTML5-driven mobile applications to purely native applications. At the beginning of 2015 they started to open source GraphQL as reference implementation based on the specification, since then the community implemented it in a lot of different programming languages.
When using REST, we typically interact with a single entity type by accessing one or many objects per HTTP Request. In order to link to items that are connecting to these entities, resource URLs are used. It is a convenient way to navigate REST APIs by doing multiple round trips between clients and servers. Think about a Star Wars API where we can access a character with one request. The response includes a list of visited planets that are associated as an array of resource URLs. In order to get all the information about the visited planets we might find ourself doing multiple requests to fetch all the data. We would either hit many other end-points, which is unwanted on a mobile network, or we would make use of a custom end-point or query params to fetch all that data at once. With this the server and the client get coupled and the API in some cases unmaintainable. That’s where GraphQL comes into play, which allows us to do this with one single request. We don't need to change our backend for data requirement changes on the client side. This solves one of the biggest problems in managing a REST API. In GraphQL we still have these individual resources, but they are allowed to link to one another directly rather than through additional resource URLs. Another interesting aspect is the payload size. There is no predefined payload response shape. We always query exactly what we want, nothing more, nothing less.
We should clarify common questions about “GraphQL”. GraphQL sounds like “graph”, does my data source need to be a graph or can I still use a relational store to query data? Does it mandate a particular programming language? We do not need a specific data store, it works just fine using a graph database, an in-memory array, a relational database, a key-value store or whatever we like. GraphQL is not language specific, it's just a specification between the client and the server. Generally speaking, it defines a way of moving data between Clients and Servers. It sits between the client apps and the actual data source as a kind of agreement between both, using a query language to fetch and mutate data. A query itself consists of a string that is sent to a server to be interpreted and fulfilled, which then returns JSON back to the client. Here is an example query:
And here is the response to that query.
In the above example, we are requesting a specific user and asking for its attributes along with the user's friends to be returned. GraphQL returns data in a similar way we requested it. The response format is described in the request query, defined by the client instead of the server. With this we can specify exactly what information we want, and a single response delivers everything that we asked for, without having to do any further requests. There is an impressive playground called ‘graphiQL’, a web-based IDE for GraphQL which also provides autocomplete and syntax validation – but more on that later.
- Hierarchical: A GraphQL query itself is structured hierarchically.
- Product centric: GraphQL is unapologetically driven by the requirements of views and the frontend engineers that write them.
- Strongly typed: Every GraphQL server defines an application specific type system.
- Client‐specified queries: Through its type system, a GraphQL server publishes the capabilities that its clients are allowed to consume. It is the client that is responsible for specifying exactly how it will consume those published capabilities.
- Introspective: A GraphQL server is queryable by the GraphQL language.
- Version free: GraphQL removes the need for an incrementing version number.
Let’s highlight three of these principles:
We have to define the type system that describes all the capabilities of the server.
On the top-level we usually have a type called “Query”, that describes all the fields we can ask for. User is one of these fields that is going to give us back a User. Within the User type we have attributes (the name and the profilePicture), including arguments we can ask for. At each level of a GraphQL query, a particular type describes not only what fields are available, but also what arguments we can supply to these fields and what types are going to result from those fields. Let’s look at this in terms of a query:
We already mentioned the graphiQL playground as a web-based IDE for GraphQL. To get an idea of how introspection works we can just ask for the typing information that the endpoint provides which gets exposed via GraphQL by using GraphQL. Sounds crazy? Just type the following query into the graphiQL interface:
It is based on a special top-level field called ‘__schema’ – the introspection API. This gives us a tool to build other tools. Just think about an API Documentation, code completion/generation and validation based on this.
[...] we're using the same version of GraphQL, the same type system today that we were using three years ago. And if you boot up a version of our iOS app that we shipped three years ago, it'll still work. So, we don't use version numbers. And basically that means that we can never delete any field that we've shipped. In order for everything to stay working that's shipped before, your type system has to remain additive only.
What this means is that we create an interface that can be expanded, but not diminished. What are the consequences? If we want to add new things to the API, we have to make sure that it doesn't conflict with something that already existed. For example, changing a type breaks the API. We are fine as long as we are the only one working with it, but a recommended way is to add a new type including its fields and maintain the deprecated one, or make the assumption that almost any field can return back null – this would also work for unauthenticated fields. Another solution could be to track if a field is still used, and remove it if it’s not the case. For more details on the core principles of GraphQL check out the GraphQL specification.
We just had read-only access to our data. If we want to change data, we need some kind of operations, which are intended to have side effects. These operations are called mutations in GraphQL. They are expressed as a collection of top level actions, which simply expose fields that take arguments. Instead of using the implicit top-level field “query”
we can make use of an explicit top-level field called “mutation”.
As we can see, we also make use of an alias called ‘user’. This helps us to assign a key for the result. The endpoint returns something like this:
Voila, we changed data the same way as we would ask for it.
GraphQL is just a query language and has no opinion on that. It is up to each application on how it could be implemented. There are already some discussions on that topic, so let’s think about that on a higher level. Instead of authenticating during controller actions, we can use the request header to run some kind of authentication. However, we could also use the query context within our GraphQL data resolver – the function that fetches and returns data. We could also manage authentication via a non-GraphQL endpoint to check whether the User is logged in or not.
GraphQL has its user-defined type system, the server describes what data is available and the client expresses its needs through the GraphQL query language. All of this is achieved while keeping the concepts programming language agnostic. One of the key problems GraphQL aims to solve is the idea of only making one API request to get all data needed, instead of constructing custom endpoints per use case. Clients want to have a way to ask for the data they need and servers want to have a way to expose the data they have. GraphQL tries to solve this in the most simple and efficient way that is possible. What if those implications create a lot of extra work for no benefit? What if we still have to do a lot of work on the backend, parsing a query, applying validation and permissions, caching and then translating that query into something our data store can understand? Replacing the complexity of multiple endpoints with the complexity of a query language could sound like that. Is GraphQL the interface of the future? GraphQL was designed to fix problems in the mobile sector on a large scale. It is a young technology in terms of the first open spec including a reference implementation, and it shows how much potential this type of approach can have. GraphQL may not be the ultimate solution here, but it solves a lot of pain points we’ve been having for the last few years. It needs its proof outside of the controlled Facebook ecosystem, but if it works it could become a major piece of architecture of how we build modern applications in the future.