GraphQL

What is it?

“GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data” - graphql.org

  • Created by Facebook in 2012
  • Open sourced in 2015
  • Not dependent on a particular type of database
  • Able to use many different languages
  • Potential to reduce HTTP requests
  • Potential to lower the amount of unwanted data sent
  • Generally easier to read than REST API queries
  • Actually a language, unlike REST (an architecture style)
  • Usually sent over HTTP, but not required
  • Queries sent in JSON format
  • Query response sent in JSON format
  • Considered an alternative to a RESP API
  • Able to retrofit over legacy databases without changing them

The query request and reception:
GraphQL is commonly set up to work over HTTP, using the client server model. Common use case would be the client sending an HTTP GET request with a GraphQL query in JSON format as the body of the request, to a GraphQL API endpoint on a server. The server would receive the request, and use the GraphQL service already defined (set up by you, the awesome developer, beforehand) to read the query and retrieve the information it was requesting.
The query data retrieval:
What language does it use to retrieve the data? Where is the data stored? Once the GraphQL API service you wrote receives the client HTTP GET request, it opens the payload and decodes the GraphQL query sent as a JSON object, decodes it, and your service code determines what to do with it. You can write your own functions for what to do for each type of query and what items it is requesting. Your function for a particular type of query could use PHP to retrieve information from a database. Or Perl (please no), GO, Java, Swift, JavaScript, Kotlin, C#, Ruby, Rust and many more!!
The database can be anything. MongoDB, Oracle, PostgresSQL, MySQL, or no database at all (but in reality it almost always is). Once the data is returned, you can now instruct your GraphQL service what to do with it.
The query response:
This is almost always sending it back to the client via HTTP, though it could technically be anything you wanted. Maybe just simply logging it, sending it to a backup database. Or sending it to some random server that would reject it straight away. The possibilities are endless! But realistically, the query response is almost always sent back to the client that requested it over HTTP. The client gets an HTTP response with the payload being a JSON object of the data it requested (if the query worked, obviously). Now the client can do whatever it wants with the data, usually displaying it on browser.

Sounds like a REST API? What problems does it solve?

Two main ones:

  • Less HTTP requests
  • Less unneeded data transferred (over-fetching/under-fetching)

Normally if sending a query using a REST style architecture, there are different endpoints for distinct data. So a different endpoint for user data, blogs post data, and weather data. If you wanted to find a specific user, all photos they had posted, and all their friends, you would have to send three separate HTTP requests. One to get the user, another to get the users photos (after you had the information from the first query), and another to find all their friends. With GraphQL, you can send one request, and only get the information you want.

With a REST style API, an HTTP GET request to the /users/<id>would return all of the the items for that user, such as a name object, photos object, and friends object. Maybe there are 30 objects for that user, this request would return all of them even though you only wanted maybe one of them. With GraphQL, you can send very specific queries that only return the exact data you want. Below is the actual query I am using to automatically build each blog post with Gatsby. Each blog post might have much more data attached to it, such as photos, author, location, anything really. However, I currently have no use for this other data so I do not include it in my query.

 const result = await graphql(
    `
      {
        allMarkdownRemark(sort: { fields: frontmatter___date, order: DESC }) {
          edges {
            node {
              frontmatter {
                title
              }
              fields {
                slug
              }
            }
          }
        }
      }
    `
  )

Easier to read

Here is an example I commandeered from moesif.com:

Say you have a specific request, such as getting the friends of friends of a certain user, but only their names and only if their job is an engineer. With a REST API your request might look like this:
GET api.example.com/users/123?include=friend.friend.name&friend.friend.ocupation=engineer
Not very easy to read just glancing at it. Compared with a GraphQL query for the same thing:

{
 user(id: 123) {
   friends {
     friends(job: "engineer") {
       name
     }
   }
 }
}

\

REST example

We can use the REST API of jsonplaceholder.typicode.com for this. It is an awesome free service with a bunch of fake data for you to query. It has several different API endpoints:

An HTTP GET request to the /posts endpoints returns an array of 100 objects, each one a post.

//// HTTP request using postman
GET https://jsonplaceholder.typicode.com/posts

//// Response body sent by server:
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident 
    occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae 
    consequuntur expedita et cum\nreprehenderit 
    molestiae ut ut quas totam\nnostrum 
    rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore 
    vitae\nsequi sint nihil 
    reprehenderit dolor beatae ea dolores 
    neque\nfugiat blanditiis voluptate porro v
    el nihil molestiae ut r
    eiciendis\nqui aperiam non
    debitis possimus qui neque nisi nulla"
  },
  /////// 98 more ....
  ]

Wow! Well what if you don’t want all those 100 posts? I only want post 22! Way to much information. Easy, just change your request to GET https://jsonplaceholder.typicode.com/posts/22

GET https://jsonplaceholder.typicode.com/posts/22

/// Returns a single JSON object in
/// the body of the HTTP response
//// instead of all 100 post objects

{
  "userId": 3,
  "id": 22,
  "title": "dolor sint quo a velit explicabo quia nam",
  "body": "eos qui et ipsum ipsam 
   suscipit aut\nsed omnis non 
   odio\nexpedita earum mollitia
   molestiae aut atque rem s
   uscipit\nnam impedit esse"
}

Sweet, so now the HTTP response from the server just has a single post in the body. As you can see, it contains the userId, id, title, and body. Now, what if you wanted to the information for “userId”, the author who wrote the post? Their user ID is #3, and the API has a users endpoint, jsonplaceholder.typicode.com/users,a fast request to that returns an array of 10 user objects. But you want just that user, so you change your query to: jsonplaceholder.typicode.com/users/3. In return, you get back the user with the ID of 3, an object containing lots of info about them, seen below.

GET https://jsonplaceholder.typicode.com/users/3
///HTTP response body, a JSON object
{
  "id": 3,
  "name": "Clementine Bauch",
  "username": "Samantha",
  "email": "Nathan@yesenia.net",
  "address": {
    "street": "Douglas Extension",
    "suite": "Suite 847",
    "city": "McKenziehaven",
    "zipcode": "59590-4157",
    "geo": {
      "lat": "-68.6102",
      "lng": "-47.0653"
    }
  },
  "phone": "1-463-123-4447",
  "website": "ramiro.info",
  "company": {
    "name": "Romaguera-Jacobson",
    "catchPhrase": "Face to face bifurcated interface",
    "bs": "e-enable strategic applications"
  }

Now you have the name of the person who wrote the post, their address, username, phone, and password! Just kidding, who would store passwords in plain text that anyone could access??? Sweet!

So, how could a GraphQL based API make this better? What’s the problem with it right now?

GraphQL can do this in one HTTP request, it took this REST API two HTTP requests. With the REST API, you don’t know know which user ID wrote post number 22, but you know you will need to get the author information for that user ID by querying the user database with that user ID. However, you can’t do this in just one request unless you already knew their user ID. With GraphQL, you can! Here is how:

One query to unite them all!!

If the API you are querying is a GraphQL server, and not a REST API, you can make one HTTP request for post 22 and get the author information as well without knowing their name. With GraphQL, everything is considered connected, it is supposed to be thought of as graph. Here is a request from the client to the GraphQL server, sent over HTTP as a JSON object for the request body:

////HTTP GET request body to a
// GraphQL API server, before
// being turned into JSON for
// sending

query getInfo {
  post(id:22){
    id
    title
    user {
      id
      name
      email
      phone
    }
  }
}

Here, with a single HTTP request, we get back the post with the ID of 22, and the user who wrote it (which returns an object). Inside the user object, we can pick which object keys we want back. Here, we did not ask for the address so it will not be sent. We could remove email and phone, for example, if we did not need them. The GraphQL API would get this request, and send back an HTTP response to the client with a JSON object as the body:

"data": {
    "post": {
      "id": 22,
      "title": "Skunks > cats",
      "user": {
        "id": 3,
        "name": "Clementine Bauch",
        "email": "Nathan@yesenia.net"
        "phone": "2018675309"
      }
    }
  }

This is available because of how GraphQL is set up on the server. The beauty of GraphQL is that it does not require a specific database. MongoDB, NoSQL, some weird text storage system, GraphQL does not care! All you have to do on the GraphQL server to create a service is to define types, and fields on these types. After that you create functions for each field of your types. For example, a user type and a query type. The user type would contain user data, the query type what to query, and the functions would tell it how to get the data.

Below we define a query type of “me” which connects to the User type. The User type contains an id, and a name. We also have functions for each, for example the User_name function might connect to a MongoDB database and return the name being queried. Below we don’t have it connecting to anything really, you could use whatever you needed inside the function to get the desired data.

type Query {
  me: User
}

type User {
  id: ID
  name: String
}

function Query_me(request) {
  return request.auth.user;
}

function User_name(user) {
  return user.getName();
}