frontend
What is Graphql, Really?
The API standard providing a more efficient, powerful and flexible alternative to REST.
I’ve been hearing about GraphQL for months now, but for the longest time I really didn’t understand what it was.
Is it a new query language? A new type of database? Some other JavaScript black magic? Nope, none of the above.
GraphQL is a new API standard that provides a more efficient, powerful and flexible alternative to REST.
It’s an alternative to REST, so what? Why should I use GraphQL instead?
Great question.
Here’s what sets GraphQL apart from traditional REST endpoints: with GraphQL, a client can specify exactly what data it wants (from multiple sources), and get back just that from a single endpoint.
Traditional REST endpoints return fixed data structures, usually with extra info clients don’t need, and they require multiple endpoints (and HTTP calls) to get back all the necessary information. GraphQL does not.
Here's an example of two traditional HTTP calls to find students and their classes
// Call 1: HTTP get to: 'students/<id>'
// Returns:
{ "student":
{
"id": 1,
"firstName":"Jane",
"lastName":"Smith"
}
}
// Call 2: HTTP get to: 'students/<id>/classes'
// Returns:
{ "classes":
[{
"id": 1,
"className":"math"
},
{
"id": 1,
"className":"english"
}]
}
/* Here is the same information as one GraphQL query */*
// Query:
query { Student( id:1 ){
firstName
lastName
classes {
className
}
}
}
// Returns:
{ "data":
{ "Student":
{ "firstName":"Jane",
"lastName":"Smith",
"classes":
[{
"className": "math"
},
{
"className":"english"
}]
}
}
}
One thing that cannot be stressed enough is that GraphQL is not a query language for databases. GraphQL is a query language for APIs.
This makes it database agnostic, and makes it possible to be used in any context where an API is used.
And no, contrary to the fact that whenever GraphQL is mentioned, it seems to be in tandem with React (since Facebook invented both), GraphQL can be used everywhere a client communicates with an API: there’s server libraries for C, Go, Erlang, Java and more.
JavaScript is by far the most popular language to utilize with GraphQL, and it has some really excellent server packages that work in conjunction to make GraphQL easily implementable with projects.
It can be run straight from the command line with Node.js when Graphql.js is installed, but it really shines when it’s integrated into a project with the GraphQL API server sitting as a layer between the client frontend and the server backend and database(s).
The implementation I’ve used is Apollo, and it integrates easily with various Node.js HTTP frameworks like Express, KOA, Hapi, etc. But that’s for another post.
Ok, you’ve piqued my interest. So how does GraphQL work?
GraphQL works based on strongly typed schemas.
In short, everything that you want GraphQL to return to you, must be explicitly defined in the schema (which some developers like and others feel like it’s making them define data twice).
Schemas: How GraphQL Defines and Structures the Data it Receives
// **** Simple Schema Example for a Type Called Student ****
type Student {
firstName: String! // (the exclamation mark means it's required)
lastName: String!
}
The Schema Definition Language (SDL) is the syntax for writing schemas, and queries and mutations are composed to get back the required information from the server. Since the structure of the data returned from the single GraphQL endpoint is completely flexible, the query has to be extremely specific in what it requires.
If something is specified in the schema but not returned, the response will contain a null
value for that field, but it will not throw an exception as sometimes happens with REST endpoints. However, if a specific field is not specified but is needed, the client won’t receive it until the schema is updated to include that field.
Queries: How GraphQL Asks for Data
Here's a basic query asking the server for all students
// **** Basic Query Asking the Server for All Students ****
{ allStudents
{
firstName
lastName
}
}
// Returns:
{"allStudents":
[{
"firstName":"Jane",
"lastName":"Smith"
},
{
"firstName":"John",
"lastName":"Doe"
}]
}
Mutations: How GraphQL Manipulates Data
In addition to queries, GraphQL uses something called mutations to manipulate data stored in the backend. There’s generally three kinds of mutations to:
- create new data
- update existing data
- delete existing data
This mutation create a new student in the database
// **** A Create Mutation to Make a New Student ****
mutation { createStudent(firstName: "Robert", lastName: "Johnson")
{
firstName
lastName
}
}
// The server response would look like this:
"createStudent": {
"firstName":"Robert",
"lastName":"Johnson"
}
Between queries to read data and mutations to manipulate data, all the CRUD functionality supplied by traditional REST endpoints is handled by GraphQL.
Regardless of the way you’re implementing the GraphQL server (connected to a single database, over a number of third party legacy systems or as a hybrid of the two), when a query arrives at the GraphQL server, it resolves the query by reading the payload and fetching the data. Then it uses the schema to return the data in the correct format to the client.
This clearly defined schema structure is what helps GraphQL to be transport-layer agnostic (TCP, Websockets, etc. — it’s all good) and database agnostic (MySQL, MongoDB, Oracle, etc.).
Likewise, if GraphQL is being implemented over multiple systems (as is likely the case in larger corporations with lots of sprawling legacy systems and APIs), it can neatly pull only the requested information from all the various endpoints, and resolve it into one, clean response, laid out by the schema.
Resolvers: How GraphQL Fetches the Data for Its Query (or Mutation) Field
Each of the fields that makes up a piece of the GraphQL query function is called a resolver, and the sole purpose of the resolver is to fetch the data for its field.
// **** Sample Query and the Resolvers Corresponding to Each Field ****
query { Student(id:1){
firstName
lastName
}
}
// Resolvers:
Student(id: String!): Student
firstName: (student: Student!) String
lastName: (student: Student!) String
GraphQL Client Libraries
GraphQL is a big step forward for frontend developers because instead of having to imperatively fetch data through:
- building and sending an HTTP request (
fetch
in JavaScript), - receiving and parsing the response,
- storing the data (locally or persisted),
- and finally displaying the data in the UI,
Clients can declaratively fetch the data by:
- describing the data requirements,
- displaying the returned data in the UI
Complexity and data storage are pushed to the server-side of the application, which is better suited to take care of the heavy computation work, and the frontend is free to do what it was designed to do: show that data to users quickly in a way they can understand.
What I’ve described above is what GraphQL libraries like Apollo and Relay were designed to do. They abstract away the infrastructure implementation of connecting the backend services so developers can focus on the important parts of their application.
More About GraphQL
If you wanted to, you could stop right here with a much better understanding of what GraphQL is, but if you’d like to know a little more about the benefits on both the client and server side with GraphQL, read on.
GraphQL and the Client Side
As discussed in the previous sections using the GraphQL API makes frontend development easier with abstractions and helping implement common functionality on the client side.
Here I’ll go into more detail on how it handles “infrastructure” features developers normally want in their apps.
Directly Sending Queries and Mutations Sans HTTP Requests
GraphQL allows for fetching data in a declarative manner. Instead of making low-level HTTP calls, a client can simply query for the data it needs and GraphQL takes care of the request and response handling for you.
View Layer Integrations & UI Updates
Once the GraphQL client has received and handled the response, there’s a number of ways the UI can be updated to reflect the data. If you’re using React, the GraphQL clients make use of higher-order component concepts (when a function takes one component and returns a new component) to fetch the data make it available to your components in their props.
Client Side Caching
Typically, you’ll want to maintain some kind of a cache or data previously fetched from the server, it’s critical to having a good user experience and shorter load times as users interact with the UI. However, storing the data exactly as it’s resolved from the GraphQL client won’t be the most efficient way to hold it for most applications.
Normalizing the data (flattening nested query results) so that the store only contains individual records that can be referenced with a unique global ID is a better option for quickly retrieving only the necessary data when the same query is made in the future.
Validating & Optimizing Queries Based on the Schema
Also, since the schema contains all the information about what a client could do with a GraphQL API, the build environment can parse the GraphQL code that’s in the project and compare it against the schema’s information. This makes for catching errors and typos much earlier in the process easier (and less likely to happen in the hands of users).
Likewise, UI code and data requirements can actually be written side-by-side in some languages like JavaScript, which makes it easier for developers to see that the right data ends up with the right parts of the UI.
GraphQL and the Server Side
Although GraphQL is talked about a lot around the frontend API, the API itself is still implemented on the server side. And it allows the backend developer to focus on describing the data, rather than implementing and optimizing numerous REST endpoints.
GraphQL Query Execution
For starters, GraphQL uses a simple algorithm for how it changes its queries into results. It traverses the query field by field, executing resolvers for each field.
If a parent resolver function is required by a child, the parent query will resolve first and pass its result to the child for use in its own query. Finally, once the execution algorithm is done, it forms all the data into the correct shape and returns it.
// **** Sample Query and Execution Algorithm ****
type Query {
director(id: ID!): Director
}
type Director {
movies: [Movie]
}
type Movie {
title: String
description: String
}
// **** Sample Query to the Server ****
query { director(id: "2wsx3edc")
{ movies
{
title
description
}
}
}
// **** Execution Algorithm Visualized ****
Query.director(root, { id:"2wsx3edc" }, context) -> director
Director.movies(director, null, context) -> movies
for each movie in movies
Movie.title(movie, null, context) -> title
Movie.description(movie, null, context) -> description
Batched Query Resolving
GraphQL also offers the ability to make data fetching smarter, if multiple similar calls will be made to the server in one query.
In JavaScript, a utility called DataLoader can be used to wrap the fetching function, which will wait for all the resolvers to run, then make sure to only fetch each item once.
// **** Sample Query and API Calls ****
query{ movies
{ title
director
{
firstName
lastName
}
}
}
/* Regardless of the fact that the API is being called for the same piece of data multiple times, the query executes for the director info */
fetch('/directors/1')
fetch('/directors/2')
fetch('/directors/2')
fetch('/directors/1')
fetch('/directors/1')
// **** Sample API Calls with DataLoader****
directorLoader = new DirectorLoader()
// Queue up all the fetches
directorLoader.load(1);
directorLoader.load(2);
directorLoader.load(2);
directorLoader.load(1);
/* Then the loader only makes the necessary amount of calls for each unique piece of information */
fetch('/directors/1')
fetch('/directors/2')
GraphQL Tooling and Its Ecosystem
As I mentioned briefly, GraphQL’s Type System allows us to define the surface area of our APIs and validate queries against a schema. What’s cool, is that GraphQL also allows clients to ask a server for information about its schema, in a move called introspection.
GraphQL Introspection
The designers of a schema obviously know what fields are available, but clients can query GraphQL using the __schema
meta field available on the root type of a Query to find out too. This is a powerful technique that can be used to provide many interesting, useful features.
// **** Introspection Query and Return Information ****
query{
__schema {
types
{
name
}
}
}
Schema Definition:
type Query {
director(id: ID!): Director
}
type Director {
movies: [Movie]
}
type Movie {
title: String
description: String
}
// Results:
{
"data": {
"__schema": {
"types": [
{
"name": "Query"
},
{
"name": "Director"
},
{
"name": "Movie"
},
{
"name": "ID"
},
{
"name": "String"
},
{
"name": "__Schema"
},
{
"name": "__Type"
},
{
"name": "__TypeKind"
},
{
"name": "__Field"
},
{
"name": "__InputValue"
},
{
"name": "__EnumValue"
},
{
"name": "__Directive"
},
{
"name": "__DirectiveLocation"
}
]
}
}
}
GraphiQL & GraphQL Playground
Finally, there are two very useful GraphQL IDEs I’m familiar with. The first is GraphiQL, which is a JavaScript, in-browser IDE that’s easy to install and use in projects (it also comes pre-bundled with Apollo for even easier use).
The second is GraphQL Playground, which can be downloaded as a desktop app or used on its website. It incorporates some of the same components as GraphiQL, but boasts additional features like automatic schema reloading, query history, the ability to work with multiple GraphQL APIs at once, interactive schema documentation and more.
In short, both are excellent tools, that allow you to debug and try queries on a GraphQL server without having to write queries over curl.
Conclusion
I hope this helps shed some light on the hot topic that is GraphQL. This is just the tip of the iceberg in terms of what it can do and how it does it, but there are already tons of great resources out there to learn more and start building your own projects using it.
Personally, I haven’t used it for very long yet, but I’m already enjoying it.
Thanks for reading!
References & Further Resources
Want to be notified first when I publish new content? Subscribe to my newsletter.