/ API

Introduction & Quick Guide to GraphQL for BackEnd & FrontEnd


GraphQL is an API query language. It provides a runtime to describe and query the data, no matter what the storage engine is.

GraphQL's benefits include

  • Typed data
  • Get what you asked for
  • Multiple data requests in one call
  • One endpoint, changes in API are now bit easier
  • Subscriptions
  • etc.

For example, consider a system similar to a social network where we have users and they have other users as friends. Following entity will completely encapsulate the data model; how it is going to be dealt in Back End and Front End, we will see after that.

type User {
  id: String!
  fullName: String
  name: Name
  email: String!
  friends: [User]
}
type Name {
  first: String
  last: String
}

For this data description, the front end will sent the request to data as follows:

{
  user(id: "5a826fdf9856ad6503d728d5") {
    id
    name {
      first
      last
    }
    email
    friends {
      id
      email
    }
  }
}

And will receive the response as follows:

{
  "data": {
    "user": {
      "id": "5a826fdf9856ad6503d728d5",
      "name": {
        "first": "Stanton",
        "last": "Hansen"
      },
      "email": "stanton.hansen@example.com",
      "friends": [
        {
          "id": "5a826fdf0d054a6fab7bb067",
          "email": "barr.wolf@example.com"
        },
        ...
        {
          "id": "5a826fdf0d054a6fab7bb067",
          "email": "barr.wolf@example.com"
        }
      ]
    }
  }
}

And the good part is that the data can be from any interface, be it the RDBMS, NoSQL DB, a File or even in-memory operation.

GraphiQL

GraphiQL is a GUI tool to explore the GraphQL API. It lets you design your queries, play with the combinations and other things for the provided endpoint.

This tool also serves as a Documentation for the GraphQL API.

For above queries, following is the GraphiQL view:

gql-query

In above screenshot, you can:

  • design your query in the left panel
  • see documentation in the right panel
  • see the data response in the middle panel

You can use the variables to optimize your queries, as shown in following way; the query can be exactly same in GraphiQL and and the FrontEnd, variables are passed separately:

gql-query-with-variables

Back End

Lets take a deep look at the following example of graphql with express, a node.js server framework.

Dependencies:

npm i -S apollo-server-express graphql-tools graphql express body-parser uuid node-fetch

Server:

const express = require('express');
const bodyParser = require('body-parser');
const { graphqlExpress, graphiqlExpress } = require('apollo-server-express');
const { makeExecutableSchema } = require('graphql-tools');

// The GraphQL schema in string form
const typeDefs = require('./schema');

// The resolvers
const resolvers = require('./resolvers');

// Put together a schema
const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

// Initialize the app
const app = express();

// The GraphQL endpoint
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));

// GraphiQL, a visual editor for queries
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));

// Start the server
app.listen(3000, () => {
  console.log('Go to http://localhost:3000/graphiql to run queries!');
});

Schema:

module.exports = `
  type Mutation {
    addUser(first: String, last: String, email: String, picture: String, friends: [String]): User
  }
  type Query {
    users: [User]
    user(id: String!): User
  }
  type User {
    id: String!
    fullName: String
    name: Name
    email: String!
    friends: [User]
  }
  type Name {
    first: String
    last: String
  }
`;

Resolvers:

const uuid = require('uuid/v4');

// Some fake data
const users = require('./users');

const friendsMapper = id => (Object.assign(
  {},
  users[id],
  { friends: users[id].friends.map(i => users[i]) }
));

// The resolvers
module.exports = {
  Query: {
    users: () => Object.values(users).map(u => friendsMapper(u.id)),
    user: (_, {id}) => friendsMapper(id),
  },
  Mutation: {
    addUser: (_, data) => {
      const uid = uuid();
      users[uid] = Object.assign({}, data, {id: uid})
      return friendsMapper(uid);
    }
  }
};

And Data:

{
  "5a826fdf27256fa9ba444cd4": {
    "id": "5a826fdf27256fa9ba444cd4",
    "email": "nancy.palmer@example.com",
    "fullName": "Nancy Palmer",
    "name": {
      "last": "Palmer",
      "first": "Nancy"
    },
    "picture": "http://placehold.it/32x32",
    "friends": [
      "5a826fdf50f698c2d9c492ca",
      "5a826fdf40b7d805ca2d6e75",
      ...
    ]
  },
  ...
}

As the server.js is executed, the server will be up at port 3000 and then graphiql & graphql endpoints are available to use.

Brief Steps

Lets review above code in brief steps:

  • create schema definition as a string (see schema.js); schema has following:
    • types: the entities which are going to be used in the GraphQL
    • Query type: this is a root type for all the queries that the API consumer can request
    • Mutation type: this will contain all the data modification queries
  • create resolvers to define how data I/O should be done; this will have following in an object
    • Query: all the query resolvers will reside under this key and should match the Query type in schema
    • Mutation: same as queries, all the mutation will reside under this and should match with what is there in the Mutation type in schema
  • make the schema executable by makeExecutableSchema
  • attach the route /graphql to the express by the apollo plugin as app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));
  • attach the /graphiql route as app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
  • start the server 🚀

As above code list the example of data being seeded through JSON file and then completely in memory.

You can also make the resolver to fetch the data form an endpoint and then return it, consider following schema and resolver:

const typeDefs = `
  type Post {
    id: Int!
    userId: Int
    title: String
    body: String
  }
  type Query {
    posts: [Post]
  }
`;
const resolvers = {
  Query: {
    posts: async () => fetch('https://jsonplaceholder.typicode.com/posts')
        .then(response => response.json())
  }
}
module.exports = {
  typeDefs,
  resolvers
}

And the queries can be used as shown in following screenshot:

gql-query-rest

And in similar manner you can make it work with other data tools like mongodb, SQL etc.

Front End

From front end point of view, the use of graphql is pretty simple as there are not many things changing; and is approximately similar effort.

Though any change in requirement of data will introduce change in the request-query as well; which in case is good because the errors are intended and hence fixed right away.

Lets take a look at basic vanilla JS code to get data and show it on front end. For certain, this code will be passed through bundler like webpack or rollup.

Dependencies:

npm i -D webpack@next webpack-cli

npm i -S graphql graphql-tag apollo-cache-inmemory apollo-client apollo-link-http

Code:

import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';

const client = new ApolloClient({
  link: new HttpLink({ uri: 'http://localhost:3000/graphql' }),
  cache: new InMemoryCache()
});

client.query({
    query: gql`
      query users {
        users {
          id
          fullName
          email
        }
      }
    `,
  })
  .then(({data}) => appendData(data))
  .catch(error => console.error(error));

const appendData = (data) => {
  console.log(data);
  const app = document.querySelector('#app');
  app.innerHTML = '';
  data.users.map(user => {
    const d = document.createElement('div');
    d.innerHTML = `<div class="user">
      <h3>${user.fullName}</h3>
      <p>${user.email}</p>
    </div>`;
    app.appendChild(d.firstElementChild);
  })
}

And it will look like following in the HTML page

graphql-frontend-centered


Conclusion

GraphQL is considered a replacement or alternative to REST API. Above examples show a quick configuration and usage of GraphQL on both BackEnd and FrontEnd; the actual usage requires more configuration and proofing. The real world use case also involves complex schema and resolvers, which will pose you different challenges.

Let us know your experiences and troubles while using GraphQL via comments and social network.

Thank you!

Check the code on GitHub


Introduction & Quick Guide to GraphQL for BackEnd & FrontEnd
Share this