PLEASE DOWNLOAD REPO AND INSTALL MODULES

 

github.com/Danilo-Zekovic/oscon-graphql-starter.git

Or just search github oscon-graphql-starter

 

 

Fundamentals of graphQL

A presentation at OSCON2018

Tech Prep

  • Please attach to the link
    • slides.com/capouch/oscon18/live
    • Live URLs and cut-and-paste
  • Milestone branches
  • Type it in versus cut and paste

Network Time

Quick survey time

Who's out there??

  • Already using graphQL?
  • Maintain REST endpoint code?
  • Coding in JavaScript?
    • If so, exclusively ES6+?
  • SQL literate?
  • Old hand git clone; npm install; npm start?

Please do the evaluation

The Plan

  • Overview
  • Types and schemas
  • The graphiQL tool
  • Queries, mutations, subscriptions
  • Resolvers
  • Tools and interfaces
  • Example application w/Gatsby

Overview

What is graphQL?

Analog of REST

 

  • Facebook, ca. 2012, open sourced 2015
  • Addresses known problems with REST
  • More sophisticated capabilities
  • Widespread adoption
  • It is an API specification, not a database system

Why the excitement?

Addresses shortcomings

  • Data discovery poor with REST
  • Multiple data fetches common
  • Fetching extraneous data
  • Query validation can be done on client
  • Deprecation, additions, changes

More robust

  • Typed
  • Schema-based
  • Advanced syntactic features
  • Supports realtime functionality

How do you do it?

That's what we're here for!

The "big four" to mastery

  • graphQL-native schema language, SDL
  • graphql-js JavaScript wrapper
  • Queries
  • Resolvers
type Author {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String
    posts: [Post]
  }

graphQL SDL

const AuthorType = new GraphQLObjectType({
    name: 'Author',
    description: 'A single post by a single author',
    fields: {
      id: { type: new GraphQLNonNull(GraphQLID),
            resolve: (root, args, context, info) => {
              return root.id
              }
            },
      firstName: { type: new GraphQLNonNull(GraphQLString),
                   resolve: (root, args, context, info) => {
                     return root.firstName
                   }
                },
      middleInitial: { type: new GraphQLNonNull(GraphQLString),
                       resolve: (root, args, context, info) => {
                         return root.middleInitial
                          }
                      },

partial graphql-js JavaScript wrapper

{
  authors {
    firstName
    middleInitial
    lastName
  }
}

A simple query

const resolvers = {
  Query: {
    posts: () => posts,
    authors: () => authors,
    author: (_, args) => find(authors, { id: args.id }),
    post: (_, args) => find(posts, { id: args.id })
  },

Resolver functions

Let's get started

Think database

(But graphQL is not a database system!!

 

Basic Organization

  • Schema
    • Data type definitions
    • Queries
    • Mutations
    • Subscriptions
  • Resolvers

A schema is a set of type definitions

Both the client and server use the schema

An endpoint provides resolvers for a schema

  • Usually associated with a URL
  • Clients and servers share the schema
  • Database backends can connect to resolvers

Each endpoint serves a single schema*

Basic Schema Types

  1. scalar
  2. type (as in object type)
  3. enum
  4. interface
  5. union
  6. input

Basic Schema Types

  1. scalar

Five built-in scalar types

  • Int
  • Float
  • String
  • Boolean
  • ID

Custom scalars are possible*

Types can have modifiers, e.g.

  • String (nullable)
  • String! (non-nullable)
  • [String] (list of nullable Strings)
  • [String]! (non-null list of nullable Strings)
  • [String!]! (non-null list of non-null Strings)

Basic Schema Types

  1. scalar
  2. type (as in object type)

Objects are defined with type*

Our starting schema is simple

  • Two objects:
  • Authors, who create
  • Posts
  • As we go, we'll add more
type Author {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String
    posts: [Post]
  }
type Post {
    id: ID!
    title: String
    author: AuthorId
    articleType: String
  }

Query

Queries are read-only fetch operations

We will define four:

  • authors: Gets a list of all authors
  • posts: Gets a list of all posts
  • author(id: Int): Get a single author, by ID
  • post(id: Int): Get a single post, by ID
type Query {
    posts: [Post]
    authors: [Author]
    author(id: Int!): Author
    post(id: Int!): Post
  }

Introspection

  • We're not going to cover it here*
  • It's a built-in interface for meta-information discovery
  • Its existence is the reason graphiQL is a powerful tool

Introduction to graphiQL

GraphiQL

  • In-browser schema-development IDE
  • Powerful debugging tool
  • Automated documentation (via introspection)
  • Is the standard way of assessing a schema

git checkout intro-one

graphql-js vs. graphql-tools

How do they differ?

  • graphql-js is the reference implementation
  • It is pure JavaScript
  • graphql-tools done by folks at Apollo
  • Code directly in graphQL's SDL
    • Limits to power and complexity

git checkout intro-two-js

Basic Schema Types

  1. scalar
  2. type (as in object type)
  3. enum

Similar to enums in other languages

enum PostType {
    NEWS
    SPORTS
    OPINION
    REVIEW
    ANALYSIS
    TECHNICAL
    }

git checkout intro-two

Basic Schema Types

  1. scalar
  2. type (as in object type)
  3. enum
  4. interface

Semantically similar to other languages

Syntactically, not quite

interface Person {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String
    }
type Author implements Person {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String

    # Fields unique to the implemented type
    posts: [Post]
    # This is a derived field
    agent: Agent
    }
type Agent implements Person {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String

    # Field unique to this implemented type
    represents: [Author]
    }

Let's add some new queries

type Query {
    posts: [Post]
    authors: [Author]
    agents: [Agent]
    people: [Person]
    author(id: Int!): Author
    agent(id: Int!): Agent
    }

Interface types are special!!

(see the People resolver later on)

git checkout intro-three

Basic Schema Types

  1. scalar
  2. type (as in object type)
  3. enum
  4. interface
  5. union

Unions versus Interfaces

  • In graphQL they are very similar
  • Interfaces are better when objects share common fields
  • Union types are "true types" independently of the union
type Author {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String
    posts: [Post]
    agent: Agent
  }

  type Agent {
    id: ID!
    firstName: String
    middleInitial: String
    lastName: String
    represents: [Author]
  }

  union People = Author | Agent

git checkout intro-three-unions

Directives

Directives are meta-commands

Basic Schema Types

  1. scalar
  2. type (as in object type)
  3. enum
  4. interface
  5. union
  6. input

Input types are required for mutations

Points to note about mutations

  • Synchronous (queries are async)
  • Fetches the mutated object
  • Requires an input type
input PostInput {
    title: String
    authorId: Int
    articleType: PostType
    }

We'll need to define the mutation 

type Mutation {
      createPost(input: PostInput): Post
    }

git checkout final

Subscriptions

Still officially "experimental"

  • Implement the pub-sub model
  • Allow for realtime client data discovery
  • Not implemented yet in vanilla graphiQL
# The subscription type 
# Specifies possible pub-sub events
 type Subscription {
   postAdded: Post
   }

Subscriptions allow realtime

  • Clients can ask for different fields
  • pub-sub is arguably similar to the Observer model
  • Vanilla graphiQL does not support it yet

Resolvers

What's a graphQL resolver?

  • The implementation of a given field in the schema
  • Each query requests a series of fields
  • Each field's value is provided by its resolver
  • Resolvers are opaque to the client

Resolvers can be part of the schema object

(as we saw with graphql-js)

Resolvers get four parameters

  • root
  • args
  • context
  • info
const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'Query',
    fields: {
      user: {
        type: UserType,
        args: {
          id: { type: GraphQLID }
        },
        resolve: (root, args, context, info) => {
          const { id } = args
          return fetchUserById(id)
        }
      }
    }
  })
})

Let's look at our resolvers

const resolvers = {
  Query: {
    posts: () => posts,
    authors: () => authors,
    author: (_, args) => find(authors, { id: args.id }),
  },
  Author: {
    posts: (author) => filter(posts, { authorId: author.id }),
  },
  Post: {
    author: (post) => find(authors, { id: post.authorId }),
  },
};

intro-one

Tools and Interfaces

"jsFiddle for graphQL"

Launchpad Touts

  • Runs in the browser
  • Share "pads" with others who can fork them
  • Produces downloadable code
  • The original code for the tutorial was done this way
  • Provides an endpoint for testing 
    • e.g. with the Chrome graphiQL extension

Some graphQL projects

Sample Gatsby app

About Gatsby

  • "Static PWA generator"
  • React-based
  • graphQL for the data layer
  • Deploys from github push
  • Based on the JAMstack model

My Gatsby Project

OSCON18

By capouch

OSCON18

Fundamentals of graphQL

  • 2,059