GraphQL 101

Andrew Daniel

IBM Watson Tooling Engineer

 

twitter.com/ajdaniel

slides.com/ajdaniel/graphql

About this presentation

  • This is an introduction to GraphQL
  • An overview of the benefits of GraphQL
  • A simple example of it in use
  • An introduction to Apollo GraphQL
  • Some code snippets of Apollo in Node & React
  • No running code shown
  • No in-depth details
  • Just a simple introduction to spark your interest
  • I am not an Expert! We're implementing this in Watson Compare & Comply service

What is GraphQL

In a nutshell:

GraphQL is a replacement for REST

GraphQL uses a QUERY LANGUAGE

GraphQL is a PATTERN not a technology

Created by Facebook in 2012

Why choose GraphQL?

REST in applications has a disadvantage with lots of information to display on one screen

One API Call for collection

Then one call per item for details

O = n + 1

GET /results
GET /results/1234
GET /results/5678
GET /results/9012

GraphQL vs REST

GraphQL can bundle lots of data into one query instead of many

POST /graphQl

{
    pins(last: 10) {
        imgUrl
        name
        likes
    }
}

O = 1

Other benefits

Strongly typed schema

  • Can be shared with client & server
  • Can validate requests at build time
  • Auto complete queries and mutations
  • Can be mocked easily

No under-fetching and over-fetching

  • Reduce those spinner times!

Passionate community

  • This project has been around a while
  • And will last a while too

How does it work?

Schema definition

First, you describe your data with a schema

type Query {
    pins: [Pin]
}

type Pin {
    id: ID
    name: String
    imgUrl: String
    likes: Number
}

Strongly Typed

 

Types can be a Scalar

(boolean, string, ID)

or Object with fields

This schema can generate types in Typescript, or Flow

Resolvers

Then you create resolvers for all schema fields in your chosen language using a library that implement the data. Resolvers work at the object property level

const types = {
    Query: {
        pins: () => {
            return db.get.pins()
        }
    }
    Pin: (pinData) => {
        return {
            id: pinData._id,
            name: pinData.name.toLowerCase()
            likes: pinData.likes.length
        }
    }
}

Deeper example

const resolvers = {
    Query: {
        documents: (parent, args, context) => context.wds.getDocuments(args)
    },
    Document: {
        title(doc) {
          return doc.extracted_metadata.title;
        },
        filename(doc) {
          return doc.extracted_metadata.filename;
        },
        date(doc) {
          return doc.extracted_metadata.publicationdate;
        },
        elements: async (doc, args, context) => {
          const elements = _get(doc, 'enriched_html_elements.elements');
          if (elements !== undefined) {
            return elements;
          }
          const document = await context.wds.getDocumentById(doc.id);
          return _get(document, 'enriched_html_elements.elements');
        }
    }
};

Query the data

Finally, your client can request the data, and query which data it likes

POST /graphql
{
  pins {
    name
    imgUrl
    likes
  }
}

Retrieve pins, for each pin return the name, image url, and the number of likes

Data returned

The data returned matches the query structure

{
  "data": {
    "pins": [{
      "name": "Batman poster",
      "imgUrl": "https://imgur.com/8sduf.png",
      "likes": 8
    }, {
      "name": "Bat suit",
      "imgUrl": "https://imgur.com/34dhh.png",
      "likes": 25
    }]
  }
}

Queries:

  • Retrieve data
  • Specify which fields are returned
  • Can pass in variables, great for pagination
query HeroNameAndFriends($episode: Episode) {
  hero(episode: $episode) {
    name
    friends {
      name
    }
  }
}
{
  "episode": "JEDI"
}
{
  "data": {
    "hero": {
      "name": "R2-D2",
      "friends": [
        {
          "name": "Luke Skywalker"
        },
        {
          "name": "Han Solo"
        }
      ]
    }
  }
}

Query

Variables

Response

Mutations:

  • Alter data
  • Can also specify return properties
  • Pass in variables
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}
{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}
{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "This is a great movie!"
    }
  }
}

Mutation

Variables

Response

Putting it into practice

Libraries

http://graphql.github.io/code/

Lots of libraries are out there for different technology implementations

Connectors

Out of the box solutions (or make your own) to apply a GraphQL API on top of databases or APIs.

Prisma is a leading library for SQL, NoSQL and Elastic DBs

Apollo GraphQL

Production-ready set of tools and libraries, built upon the GraphQL specification

Apollo GraphQL

Example with NodeJS + Express

npm install --save apollo-server-express@1 graphql-tools graphql express body-parser

https://www.apollographql.com/docs/apollo-server/example.html

const express = require('express');
const bodyParser = require('body-parser');
const { graphqlExpress } = require('apollo-server-express');
const { makeExecutableSchema } = require('graphql-tools');
const books = [
  {
    title: "Harry Potter and the Sorcerer's stone",
    author: 'J.K. Rowling',
  },
  {
    title: 'Jurassic Park',
    author: 'Michael Crichton',
  },
];

server.js

const typeDefs = `
  type Query { books: [Book] }
  type Book { title: String, author: String }
`;
const resolvers = {
  Query: { books: () => books },
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = express();

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

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

Fetch data with GraphiQL:

Powerful GraphQL viewer, like Postman

React Client

npm install apollo-boost react-apollo graphql-tag graphql --save
import ApolloClient from "apollo-boost";

const client = new ApolloClient({
  uri: "https://w5xlvm3vzz.lp.gql.zone/graphql"
});
import React from "react";
import { render } from "react-dom";

import { ApolloProvider } from "react-apollo";

const App = () => (
  <ApolloProvider client={client}>
    <BookList />
  </ApolloProvider>
);

render(<App />, document.getElementById("root"));
import { Query } from "react-apollo";
import gql from "graphql-tag";
const query = gql`
  {
    books {
      title
      author
    }
  }
`
const BookList = () => (
  <Query query={query}>
    {({ loading, error, data }) => {
      if (loading) return <p>Loading...</p>;
      if (error) return <p>Error :(</p>;

      return data.books.map(({ title, author }) => (
        <div key={title}>
          <p>{`${title} - ${author}`}</p>
        </div>
      ));
    }}
  </Query>
);

Where is it used?

Within IBM:

IBM Cloud Account microservice

SoftLayer VSI & bare metal provisioning microservice

Watson IoT (in development)

IBM Cloud IAM (in development)

Resources

GraphQL site

https://graphql.org/learn/

 

Apollo GraphQL site

https://www.apollographql.com/

 

How to GraphQL - great tutorial

https://www.howtographql.com/

 

IBM FED presentation on GraphQL

https://jlengstorf.github.io/presentations/better-data-layer

Questions?

Thank you

slides.com/ajdaniel/graphql