BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Turbocharge React with GraphQL

Turbocharge React with GraphQL

Key Takeaways

  • GraphQL and React go together like chocolate and peanut butter, yum.
  • GraphQL can help you write expressive queries to pull as much or as little from your APIs as needed.
  • The GraphQL type system is tremendously powerful and can be used to validate and assemble flexible queries to your API.
  • Sometimes you still need REST. It’s OK, they can peacefully live side by side if needed.
  • GraphQL can be implement in any backend software, how will you integrate GraphQL in your backend services?

At an office in Boston, among the ping pong tables, beer on tap, and Ms PacMan, my client, Greg, sat across the table from me. Greg was a longer term and respected developer at this business. He asked me point blank “Is GraphQL production ready?”.

It was a fair question, since he never worked with GraphQL. Let’s be honest, GraphQL was only open sourced in 2015 and really just created as a standard in 2016. Is anyone besides Facebook really using GraphQL? Greg and his team were intimately familiar with REST and had built several apps in the last few years with REST. They leveraged Swagger for validation and documentation and it worked well for them. Hence the skepticism if GraphQL could really best REST as the communication conduit for apps.

Before we answer Greg’s question, let's jump into GraphQL itself.

Give it to me Straight, What is GraphQL?

Under the hood GraphQL is many things. First, it’s a buzzword. The cool kids are using it, so one might think it’s just a flash in the pan, techno babel, shiny, new hotness, or whatever the cool adjectives kids are using today. It’s much more, I assure you.

First, What GraphQL is NOT

Before we proceed, let’s dispel some frequent misconceptions surrounding GraphQL.

  • Misconception: A client can request any type of data in any way they want. For example, let’s say a client wants a list of all users and their favorite ice cream flavor. This can be made available to the client ONLY IF the schema on the server has defined this relationship.

           Truth: The client is restricted to the data relationships defined on the GraphQL server.

  • Misconception: Is GraphQL compatible with MSSQL? No. It’s also not compatible with MongoDB, Oracle, MySQL, PostgreSQL, Redis, CouchDB, or Elasticsearch.

           Truth: GraphQL doesn’t talk to databases. It receives the request from the client and it’s up to your backend software to use the requested data to query your data storage and return the data in a format compatible with your GraphQL schema.

  • Misconception: You have to choose between GraphQL or REST. Nonsense.

           Truth: Offering both on your server can be done with contemptuous ease.

GraphQL is a Strongly-Typed Language

Seriously, GraphQL is a language? You betcha! Take a look at this simple definition below. You’re looking at definition for an image thumbnail.

type Thumbnail {
  # URL of the image, ! means it’s required
  uri: String!

  # Width in pixels
  width: Int

  # Height in pixels
  height: Int

  # String used for the title tag on images
  title: String
}

As you can see, we’re defining a type or an object called Thumbnail. This object has a few properties such as uri, a string; width and height are integers, and finally title is also a string.

There is a nice GraphQL Language reference cheat sheet.

GraphQL is All About Relationships

The power of GraphQL is found not only in the Types you define, but also how those types are related. If we look at an example Person type, we can add a relationship to another type, Address. This relationship is established by our definitions and now our clients can request a person and optionally receive a list of their addresses.

type Address {
  street: String
  city: String
  state: String
  zip: String
  country: String
}

type Person {
  # First name
  fname: String!

  # Last name
  lname: String

  # Age in years
  age: Int

  # List of addresses
  addresses: [Address] 
}

GraphQL is a Query Language

This part of GraphQL fits what most developers understand it to be, a query language. A replacement for REST. What makes it so much better than REST? Glad you asked.

Think of REST as two dimensional. GraphQL as three dimensional.

  • REST relies heavily on the URL as the resource you are interacting with, while GraphQL allows you to interact with many levels of resources. For example, a GraphQL client could request a Person by their ID, nested in the response could be a list of the person’s Friends (an array of Person). Within each of their friends, we can request to receive their addresses (an array of Address). Here is an example query demonstrating a query with this nested power.
query {
  person(id: 123) {
    id
    friends {
      id
      addresses: {
        street
        city
        state
        zip
      }
    }
  }
}
  • REST is tightly coupled to HTTP status codes, such as 200 and 404. GraphQL does not use HTTP status codes, but instead uses an errors array in the response.
  • GET in REST can become unwieldy when having to specify IDs for multiple levels of resources, such as a post > comment > author > email. For GraphQL, this is handled nicely by leveraging the type definitions and relationships.
  • GraphQL automatically performs validation on your incoming data. For example, if we have an input defined such as you see below. If the client submits age as a string, GraphQL is going to throw an error, or if fname is missing, it will throw an error.
input Person {
  # First name
  fname: String!

  # Last name
  lname: String

  # Age in years
  age: Int
}

This is also true when the API is returning data. It will validate and format to match the defined schema. This is handy for example if you happen to query your database for a person record, and it accidentally sends the password field to the client. Gasp!!!!

It’s going to be OK, since GraphQL didn’t have a definition for the password, it will silently prune the password from the response. Hoorah!

GraphQL is Extendable

Let’s say you sit down to write your GraphQL schema and decide you would like to change the way dates are handled. Maybe you’d prefer them to be returned to the client as epoch timestamps instead of ISO strings. This is easily done by defining your own Scalar type for GraphQL to utilize and then you just need to define how this scalar parses and serializes data. Here is a custom Date scalar returning the date as an integer. You’ll notice there is a parseValue function to handle the data from the client and a serialize function to change the data before sending to the client.

const { GraphQLScalarType } = require('graphql')
const { Kind } = require('graphql/language')

exports.Date = new GraphQLScalarType({
  name: 'Date',
  description: 'Date custom scalar type',
  parseValue (value) {
    return new Date(value) // value from the client
  },
  serialize (value) {
    if (typeof value === 'object') {
      return value.getTime() // value sent to the client
    }
    return value
  },
  parseLiteral (ast) {
    if (ast.kind === Kind.INT) {
      return parseInt(ast.value, 10) // ast value is always in string format
    }
    return null
  }
})

Sure, GraphQL was only open sourced by Facebook in 2015, and only became a standard in 2016. It’s young, but it has a few things in its favor.

  • Long incubation history: Facebook, may have only open sourced it in 2015, but it had been developed back in 2012 and used by them well before they released it to the general public. It’s a little comforting knowing one of the biggest tech companies in the world has placed this at the heart of it’s apps.
  • Tooling: As we’ll see in a bit, the tooling around GraphQL sprouted very quickly, there are very mature tools and libraries for GraphQL available. Have a look at the GraphQL Awesome repository.
  • Standardized: Many companies release open source software, but GraphQL has gone a step further and has become a standard (in draft form currently). You can read the standard more in depth.
    Standards are more likely to be adopted by other companies and the industry as a whole.

I’m Convinced GraphQL is Production Ready. Now What?

Now we know a little more about what makes up GraphQL. We also have a better understanding of its readiness for serious production usage. Let’s build an React / Node.js app demonstrating GraphQL in action.

No Secret Sauce

Let me be very clear: you do NOT need special libraries on the client side to make a GraphQL request. GraphQL is nothing more than POSTing a specific JSON object to an endpoint and receiving JSON in return.  For example, this sample GraphQL is POST’d to our endpoint.

{
  "query": "query tag($id:ID) { tag(id:$id) { id title }}",
  "variables": {
    "id": "6d726d65-fb99-4fa7-9463-b79bad7f16a7"
  }
}

You can see two properties:

  • query: A string representing our GraphQL query.
  • variables: a JSON object of variables our GraphQL will need. Note they are preceded with a $ in the query string (e.g. $id).

That’s it!

Your response will be shaped as you requested in your query.

Houston, This is Apollo, There's no Problem.

I’d like to introduce you to my favorite suite of tools for GraphQL, Apollo.

The wonderful developers at Apollo have created a suite of amazing tools we’ll leverage to build our React frontend and Node.js backend. In fact, they provide GraphQL tools for more than just React and Node.js, but also Angular, Vanilla JS, Swift (iOS), and Android.

For our purposes today, we’ll leverage several Apollo tools in our stack:

  • react-apollo
    • Tools to integrate GraphQL with a React app
  • graphql-server-express
    • An Node.js / ExpressJS middleware handling the request and responses to our GraphQL server.
  • graphql-tools
    • A library of tools we use to convert out GraphQL schema language into functions our ExpressJS server understands.

Countdown to App Lift Off

Without wasting anymore time, let’s get to work and start to build a simple app to demonstrate React, Node.js, and of course, GraphQL. For today, we’ll keep it simple and create a Contacts application and will allow us to add people to our Contacts and list them as well.

First Things, First

Before we get ahead of ourselves, let’s chart our course. We do this by creating our GraphQL schema. This will define the shape of the request and responses our GraphQL server will accept and return. Have a look at the following GraphQL schema for a Person.

type Person {
  # The internal ID of the person, required
  id: ID!

  # First name, required
  firstName: String!

  # Last name, required
  lastName: String!

  # Age in years
  age: Int

  # Phone number of the person
  phone: String

  # Is the phone number a mobile number
  isMobile: Boolean

  # This person’s best friend
  bestFriend: Person
}

It’s a simple schema for a person we might use to keep track of the contact information for a friend. Pretty simple. Now we just need two other items to define in our GraphQL schema: the input used to create / update a person and the operations our clients will call. First, let’s have a look at the input for our Person.

input PersonInput {
  # The internal ID of the person
  id: ID

  # First name, required
  firstName: String!

  # Last name, required
  lastName: String!

  # Age in years
  age: Int

  # Phone number of the person
  phone: String

  # Is the phone number a mobile number
  isMobile: Boolean

  # The person’s best friend ID
  bestFriend: ID
}

Hey, what gives?

It looks like we just took the Person type and used it for the input, you’re pretty much right. GraphQL treats input different from output as you may want to require certain input, but not output. For instance, look at the id. For the type or output, we require it, but for our input, we do not require it. Think about when we create a new person, we don’t yet know the ID which may be assigned by the database. When an id is present, we know this is an update instead of a create operation. In addition, bestFriend is just an ID for the input, but the type or response it is a full Person type.

The last definition we’ll define in our schema is the actual methods or operations the client will call to create, update, and list our contacts.

type Query {
  # Fetch an individual Person by id
  person (id: ID): Person

  # Fetch all the people
  people: [Person!]!
}

type Mutation {
  # Create or Update a Person
  person (input: PersonInput): Person
}

schema {
  query: Query
  mutation: Mutation
}

From this definition, you can see we have two query operations and one mutation operation. The two queries, person and people fetch a single person and a list of people respectively. The mutation operation allows us to create or update a person.

Now save those three schema definitions into one file called “schema.gql”. We’ll import it later when we setup our server.

A Solid Platform

Now we have our schema well defined, it’s time to assemble our Node.js / Express server. As I mentioned earlier, Apollo provides a middleware utility to work nicely with Express, but that’s the easy part. Before we set the app up, we need to talk about an important concept for Apollo GraphQL.

Resolvers

What are Resolvers? Glad you asked. Remember when I mentioned GraphQL doesn’t know how to talk to your database? It’s true. Each query, mutation, and type needs to know how to resolve the GraphQL request into an acceptable response. To do this, Apollo will require you to create an object that knows how to return the data requested.

Let’s take a look at what our resolvers for our schema would look like.

For simplicity, we are just going to hold our data in memory in a ‘people’ array. For a real app, you would use a data store of some kind.

// Our make shift, in memory, array as a database
const people = [ ];
const resolvers = {
  Query: {
    // Fetch one person
    person (_, { id }) {
      return people[id];
    },
    // Fetch all people
    people () {
      return people;
    }
  },
  Mutation: {
    person (_, { input }) {
      // If this person already exists, update
      if (input.id in people) {
        people[input.id] = input;
        return input;
      }
      // Default to create (or add) this person
      // Set the id equal to the index of the record
      input.id = people.length
      people.push(input)
      return input
    },
  },
  Person: {
    // Resolve a bestFriend Id into a person record
    bestFriend (person) {
      return people[person.bestFriend];
    }
  }
};

module.exports = resolvers;

If the resolvers look familiar, that’s good. Resolvers are nothing more than a JavaScript object with keys matching our schema. Since we are just using a simple JavaScript array as our datastore, we’ll use the index as the id of the person.

Apollo matches these resolvers we just defined with our schema. Now it knows how to handle each type of request. Even though we’ve just scratched the surface, you can see GraphQL Queries, Mutations, Resolvers, and Types at work.

Let me draw your attention to the Person resolver. By default, Apollo will just try to return the properties of an object as-is, but sometimes we need to give it some help or change what’s returned on the fly. Take a look at the bestFriend resolver. Since this one will return a Person type, we need to use the id of the bestFriend, look them up in the people array and return the entire person.

Keep in mind, Apollo will only fire off this bestFriend function if the client has requested to receive the bestFriend property.

Assembly Time

Now we have our schema defined in `schema.gql`, and our resolvers defined in `resolvers.js`, we need to put everything together to serve up our GraphQL. Here we define a simple Express app you can place in index.js of your project.

const fs = require('fs');
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const { graphqlExpress, graphiqlExpress } = require('graphql-server-express');
const { makeExecutableSchema } = require('graphql-tools');
const typeDefs = fs.readFileSync(path.join(__dirname, './schema.gql'), 'utf8')
const resolvers = require('./resolvers');

const myGraphQLSchema = makeExecutableSchema({
  typeDefs,
  resolvers
});

var app = express();

// bodyParser is needed just for POST.
app.use('/graphql',
  bodyParser.json(),
  graphqlExpress({ schema: myGraphQLSchema })
);

app.use('/graphiql',
  graphiqlExpress({ endpointUrl: '/graphql'})
);

app.listen(3000);

This may look a little complicated on surface, but in reality, it’s just pulling together your schema definition (typeDefs), matching it with your resolvers by using the makeExecutableSchema, then finally adding the GraphQL to the URL path /graphql. Now you can start the server with the command:

node index.js

The Express server will start to listen to GraphQL POSTs at the http://localhost:3000/graphql URL.

Since we also included GraphiQL, you can also go to http://localhost:3000/graphiql to see the GraphiQL Browser and Documentation.

Now you have the API server running, you can click on this link to populate GraphiQL with a Mutation operation to add a person. http://localhost:3000/graphiql.....

Pretty cool huh?

Go ahead and play around with some of the operations we defined. It feels good to have the backend up and running, doesn’t it? Let’s take a look at some simple React components and how they would communicate with our GraphQL backend.

Front Command Center

Now it’s time to demonstrate some React components leveraging this great Contacts API we’ve defined. Setting up a full frontend using Webpack, React Router, Redux and other elements is a little beyond the scope of this article, so we’re going to just show how Apollo fits into the picture.

First, let’s take a look at some top-level app code you’ll need to take advantage of Apollo’s React library in your components. This npm modules is called react-apollo.

import { ApolloClient, ApolloProvider } from 'react-apollo';

// Create the client as outlined above
const client = new ApolloClient();

ReactDOM.render(
  <ApolloProvider client={client}>
    <MyAppComponent />
  </ApolloProvider>,
  document.getElementById('root')
)

In this simple example of an app, we are wrapping our App with the ApolloProvider Higher Order Component. This will set up much of the needed communication between the client and the GraphQL server.

Now let’s take a look at what it would look like to display a Person with ID of 10. This simple example below would automatically fire off the GraphQL query when the component mounted. The query is defined with the const query = gql` …. `; template literal. This query and the component for the PersonView are combined using the graphql library you see here.

This is a Higher Order Component to your Person component. This means Apollo will take of contacting the GraphQL server and when it’s received an answer, Apollo will inject those props into your component as

props.data.person

import React  from 'react'
import { gql, graphql } from 'react-apollo'

function Person ({ data: { person = {} } }) {
  return (
    <PersonView data={person} />
  );
}

const query = gql`
  query person($id: ID) {
    person(id: $id) {
      id
      firstName
      lastName
      age
      phone
      isMobile
      bestFriend {
        id
        firstName
      }
    }
  }
`;

export default graphql(query, {
  options: () => ({
    variables: {
      id: 10 // You would probably use URL params, not hard coded
    }
  })
})(Person);

Next, let’s have a look at a Mutation as it’s a little different. In fact, the Query and the Mutation can live on the same React component, so let’s expand our previous example to update our person with a mutation.

import React  from 'react'
import { gql, graphql, compose } from 'react-apollo'

function Person ({ submit, data: { person = {} } }) {
  return (
    <PersonView data={person} submit={submit} />
  );
}

const query = gql`
… omitted … 
`;

const update = gql`
  mutation person($input: PersonInput) {
    person(input: $input) {
      id
      firstName
      lastName
      age
      phone
      isMobile
      bestFriend {
        id
        firstName
      }
    }
  }
`;


export default compose(
  graphql(query, {
    options: () => ({
      variables: {
        id: 10 // You would probably use URL params, not hard coded
      }
    })
  }),
  graphql(update, {
    props: ({ mutate }) => ({
      submit: (input) = mutate({ variables: { input } })
    })
  })
)(Person);

Take a close look at this interesting piece of code. We’ve pulled in the compose utility so we can use it to combine multiple GraphQL operations around a single component.

We’ve also defined an update query to use the person update mutation we’ve defined in our schema. If you look towards the bottom of the code, you see we’ve created a wrapper function called submit. This submit gets passed as a prop into the Person component. From there we pass it as a prop into our PersonView component.

Our PersonView component could fire off an update to the person by simply calling the submit function much like this example here

props.submit({
  firstName: “Neil”,
  lastName: “Armstrong”,
  …
  isMobile: true
})

Apollo would take the input to the submit function and pass it to the person mutation as the variable input.

You want to know something mind blowing?

When you fire off a mutation to update something like the Person type, Apollo automatically updates your local cache for the item. So everywhere your app uses the Person record, it will get automatically updated; you don’t have to do a thing on your end.

Last, let’s have a look at the code to display all the people in a table. In the following code sample, we have a simple HTML table displaying our list of people. Pay special attention to the loading property. This is a prop Apollo will set when it’s fetching this data, so you can show a spinner or some other informative UI message to the visitor.

Again, like before, we define our React component. Then our query using the gql tool to transform our template literal into a working GraphQL request. Finally, we tie them together with the graphql tool. Now when this component is mounted, the query will automatically fire and load the people we have stored in our backend.

import React  from 'react'
import { gql, graphql } from 'react-apollo'

function People ({ data: { loading, people = [] } }) {
  // Apollo will set loading = true when still fetching data from GraphQL
  if (loading) return <Spinner />

  return (
    <table className='table table-hover table-striped'>
      <tbody>
        {people.map((person, i) =>
          <tr key={i}>
            <td>{person.firstName}</td>
            <td>{person.lastName}</td>
            <td>{person.age}</td>
            <td>{person.phone}</td>
            <td>{person.isMobile}</td>
            <td>{person.bestFriend && person.bestFriend.firstName}</td>
          </tr>
        )}
      </tbody>
    </table>
  );
}

const query = gql`
  query people {
    people {
      id
      firstName
      lastName
      age
      phone
      isMobile
      bestFriend {
        id
        firstName
      }
    }
  }
`;

export default graphql(query)(People);

Conclusion

As you’ve seen, GraphQL is a powerful suite of tools you can integrate into your React applications to supercharge the interaction with the API. Using Apollo makes it easy to add GraphQL to your React frontend and Node.js backend. Now is a great time to test out these new found skills you have in GraphQL. You could write a small app to play with the technology more, or unobtrusively included GraphGL into one of your existing API servers. Whichever way you choose to include GraphQL into your application stack, you’ll be happy you did.

About the Author

Shane Stillwell, author, speaker, and senior developer resides up north in chilly Duluth, Minnesota. Over the past fifteen years, when not shoveling snow, he's consulted for Under Armour, BrightCove, Meijer, and other top notch organizations. Shane specializes is in the realm of custom web application development using React, Node.js, and Docker. During this time he’s been able to hone his skills and happily shares them with anyone willing to listen. A family man that enjoys anything in the out of doors, you can find Shane most places around the web using @shanestillwell.

Rate this Article

Adoption
Style

BT