Skip to main content

Introduction to GraphQL

5 min read

Older Article

This article was published 8 years ago. Some information may be outdated or no longer applicable.

We’re going to look at GraphQL, walk through a basic example, and explore why it’s a great supplement for REST APIs.

The history of GraphQL

GraphQL is a data query and manipulation language created by Facebook. They had a perfect use case for it. Imagine wanting to see a post by a specific user, plus all the likes and comments on that post, where each like and comment should (at minimum) show the commenter’s avatar and name.

That query gets complicated fast, and complicated usually means slow. The typical fix is splitting it into separate REST API endpoints. That works for one case, but picture multiple data sources with more complex requirements. Things can spiral out of control quickly from a developer’s perspective.

GraphQL solves this by introducing a single endpoint that accepts complex queries and returns data in the exact format you asked for. Data retrieval becomes much simpler.

Think of GraphQL as a proxy sitting between your application and a REST API.

Example

Our example uses a simple dataset: a list of people, each with a name, age, and a friends property (an array of IDs):

const data = [
  { id: 0, name: 'Tamas', age: 30, friends: [1] },
  { id: 1, name: 'Susan', age: 45, friends: [0, 2] },
  { id: 2, name: 'Joe', age: 24, friends: [] },
];

Next, we need a GraphQL Schema. The schema defines what objects our queries return and their datatypes. GraphQL services define a set of types that describe the data we can query against. Incoming queries get validated and executed against this schema.

The most basic schema building block is an object type: it represents something you can fetch from the service.

To build a schema, we’ll use the graphql npm module: npm i graphql.

const { buildSchema } = require('graphql');
const schema = buildSchema(`
  type Query {
    people: [Person]
    person(id: Int): Person
  }

  type Person {
    id: Int,
    name: String,
    age: Int
    friends: [Person]
  }
`);

We’ve created a Person type and a Query type. Every GraphQL service needs a query type. (It can also have a mutation type, but that’s optional.) The query type is special because it defines the entry point for every GraphQL query.

The Person type has properties: id, name, age, and friends, each with its own datatype. The friends property is an array of Person types, giving us recursion.

Arguments for GraphQL types can be required or optional. An exclamation mark means non-nullable, e.g.: name: String!

The Query type specifies two things: people returns an array of Person types, and person(id: Int) returns a single person. The id is a changeable parameter.

Next, we build what the query actually returns. We’ll use Restify with a middleware (originally built for Express, but I’m showing it works with other REST frameworks too). Install the dependencies: npm i restify express-graphql.

const restify = require('restify');
const graphqlHTTP = require('express-graphql');

const server = restify.createServer();
server.post(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue: root,
    graphiql: false,
  })
);

server.get(
  '/graphql',
  graphqlHTTP({
    schema,
    rootValue: root,
    graphiql: true,
  })
);

server.listen(3000, () => console.info('Server is up'));

Nothing unusual in the Restify setup.

Notice the two routes, GET and POST. Both use the middleware and require the schema we defined earlier. The rootValue property specifies the root node for our graph, which we’ll define in a moment.

The graphiql property is set to true for the GET method. This enables a visual interface for writing GraphQL queries. Navigate to http://localhost:3000/graphql in your browser to see it:

This tool lets you construct queries and explore your data types. Press ctrl - space to see all available types.

Tools like Insomnia also support making GraphQL queries natively within the UI.

The last missing piece is the root, the actual graph. In other words: what data should we return when someone queries for a person? For every property in the Query type, we need code to retrieve the data. GraphQL calls these resolvers. One key point: the API and the database are decoupled. You can query against field names that don’t match the database. (The database might have fullName while your GraphQL schema uses name.)

Let’s look at the simplest one, people. Querying for people returns the entire dataset:

const root = {
  people: () => {
    const data = require('./data');
    return data;
  },

We’ve got the graph, we’ve got the types. Time to write some queries. Fire up GraphiQL (port 3000 in the browser) and let’s grab all people’s names:

{
  people {
    name
  }
}

That’s it. Want age too? Add another property:

{
  people {
    name,
    age
  }
}

Retrieving a single person isn’t any harder. Listing friends takes a bit more work though. We need to find each person by their friend ID and attach their details:

person: ({ id }) => {
  delete require.cache[require.resolve('./data')];
  const data = require('./data');
  const friendIds = data[id].friends;
  if (friendIds.length === 1) {
    const friendObject = data.find((obj) => obj.id === friendIds[0]);
    delete friendObject.friends;
    data[id].friends = [friendObject];
  } else if (friendIds.length > 1) {
    const friends = friendIds.map((friendId) => {
      return data.find((obj) => obj.id === friendId);
    });
    friends.forEach((friend) => delete friend.friends);
    data[id].friends = friends;
  }
  return data[id];
};

Because we’re working with static data, the code has some quirks, like clearing the require cache. That’s only needed for this example.

Let’s query for a person’s name and their friends’ names:

We’ve got a working GraphQL server. Consuming it from a frontend is easy too. Apollo, for example, has clients for the most popular libraries.

I hope this helped show the basics of GraphQL. Remember: GraphQL doesn’t replace REST APIs. It supplements them. If your current REST API design is working well, brilliant. But if you’re bumping up against its limits, give GraphQL a go.