What is GraphQL?

04 January, 2018

If you’re interested in GraphQL testing, you may want to check out my side project, OnlineOrNot.

GraphQL is both a client-side query language, and a server-side runtime for fetching data from various sources.

A common misconception (at least one that I had) is that client-side GraphQL interacts with a GraphQL database, and magically brings back data. Similar to how you would use SQL to query a Postgres database.

This is not the case. GraphQL is not tied to any particular database or backend API. In fact, you can have several databases and external services working together to pull in a unified view of something (See example below).

Instead, what happens is that on the client-side, you query your GraphQL backend service using queries such as:

{
  user {
    id
    name
  }
}

and your GraphQL backend backend service would respond with JSON of the format:

{
  "user": {
    "id": 1,
    "name": "Max"
  }
}

Your GraphQL backend service (we’re using Node, running on Lambda in this example) would typically consist of a few files:

- graphql/
- - index.js // NodeJS/Lambda specific call to graphql()
- - resolvers.js // This file contains the logic of how you fetch your data
- - schema.graphql // This file tells GraphQL what format/types to expect the data in
- - schema.js // This file imports the GraphQL schema and the resolvers, combining them into an executableSchema

The index.js file only really parses the lambda event, and sends the query off to GraphQL (Note, this example does NOT handle authentication):

import schema from './schema'
exports.handler = function(event, context, callback) {
  const query = event.Requests[0].body.query
  const root = {}
  const context = {}
  const variables = {}
  graphql(schema, query, root, context, variables)
    .then(d => {
      callback(null, {
        statusCode: 200,
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(d),
      })
    })
    .catch(e => {
      callback({
        statusCode: 500,
        body: 'WTF?',
      })
    })
}

Since all requests for data from the client-side are now just going to this one function, we only really need one endpoint - /graphql/ or /data/

This also saves you from having to document your hundreds of internal endpoints for particularly large projects, since everything is described in the schema file (assuming you name things clearly!)

How do I actually get the data?

Say you have a schema, like this:

scalar JSON
type User {
  name: String!
  count: Int!
  somethingElse: Float!
}

type Query {
  user(id: Int!): [User],
}

schema {
  query: Query
}

Your resolvers file could look similar to the file below:

import pg
import aws-sdk/clients/DynamoDB as docClient
resolvers: {
    Query: {
      user(root, args, context) {
        const { id } = args
        docClient.query(
          {
            TableName: 'users',
            Key: {
              ID: id,
            },
          },
          function(err, data) {
            return data.Items
          }
        )
      },
    },
    User: {
      name(root, args, context) {
        pg.query('Select * from Users where id = ?', [123]).then(d => {//doStuff})
        //OR
        fetch(context.upstream_url + '/thing/' + args.id).then(d => {//doStuff})

        return root.name + '_thing'
      },
      somethingElse(root) {
        return root.count + 0.002
      },
    },

In this example, we pull in the bulk of our User fields from the AWS DynamoDB docClient.query call. We can then add extra fields like name from external sources (Postgres or an upstream_url in this example). You can even hard code values, as in the case of somethingElse.

Stay tuned for part two of this article, where I’ll show you how to set up your own graphql server.


Enjoyed this post? Receive the next one in your inbox!


Previous: My Side-project: https://JobsOk.io

Next: How to setup MailChimp with GraphQL