Approaching GraphQL

What is GraphQL?

  1. A query language for APIs
  2. A runtime for fulfilling those queries with your existing data.
query {
  viewer {
    email
  }
}
{
  "data": {
    "viewer": {
      "email": "foo@example.com"
    }
  }
}

What is GraphQL not?

  1. An ORM
  2. Data storage layer

Why use GraphQL?

Build user interface centric API. Clients using GraphQL to construct design elements can efficiently fetch everything required for a component in one request.

Why use GraphQL?

queryui

Differences with REST?

Single endpoint
/graphql
vs resource URLs
/posts
/posts/1
/posts/1/comments
/posts/1/comments/1
...

Differences with REST?

Single representation
content-type: application/json
vs multiple representations
content-type: text/html
content-type: application/xhtml+xml
content-type: application/json
...

Differences with REST?

Fewer status codes
200 OK
400 Bad Request
vs many status codes
200 OK
201 Created
202 Accepted
204 No Content
418 I'm a teapot
...

Differences with REST?

Single request method (typically)
POST
vs varied reqeust methods
GET
POST
PUT
PATCH
DELETE
...

GraphQL is a query language

GraphQL defines an API schema to organize available resources. Resources are declared using static types and connected to one another via fields.

GraphQL is a query language

Reads
# Query
query {
  authors {
    name
    posts {
      title
    }
  }
}
# Schema
type Author {
  name: String
  posts: [Post]
}
type Post {
  title: String
}
{
  "data": {
    "authors": [{
      "name": "Dat Boi",
      "posts": [
        { "title": "Here he come" }
      ]
    }
  }]
}

GraphQL is a query language

Writes
# Query
mutation createUser {
  createUser(email: "baz.com") {
    id
    email
  }
}
# Schema
type Mutation {
  createUser(email: String): User
}
{
  "data": {
    "createUser": {
      "id": "3",
      "email": "baz@example.com",
    }
  }
}

GraphQL is a query language

Expressive type system
  • Scalars
  • Objects
  • Enums
  • Inputs
  • Interfaces
  • Unions
  • Modifiers

GraphQL is a query language

Expressive type system

Builtin scalars

ID         # A unique identifier
Int        # A signed 32‐bit integer.
Float      # A signed double-precision floating-point value.
String     # A UTF‐8 character sequence.
Boolean    # `true` or `false`.

GraphQL is a query language

Expressive type system

Custom scalars. Create any type you want

scalar Time
type Post {
  time: Time
}
# Create parse and serialize handlers in your runtime.
TimeType = GraphQL::ScalarType.define do
  name "Time"
  description "Time since epoch in seconds"
  coerce_input ->(value) { Time.at(Float(value)) }
  coerce_result ->(value) { value.to_f }
end
# irb(main):001:0> Time.at(Float(1234567890))
# => 2009-02-13 17:31:30 -0600

GraphQL is a query language

Expressive type system

Enum

# Enums are a collection of scalars referencing specific values
enum ExpenseType {
  FOOD
  TRAVEL
  PARKING
}
type Query {
  expenses(type: ExpenseType): [Expense]
}
query {
  expenses(type: PARKING) {
    merchant
    amount
  }
}
{
  "data": {
    "expenses": [
      {"merchant": "Regional airport", "amount": 40.23}
    ]
  }
}

GraphQL is a query language

Expressive type system

%p Objects

enum CompanyType { CNN }
type Author {
  name: String
  posts: [Post]
}
type Post {
  title: String
}
type Query {
  bloggers(type: CompanyType): [Author]
}

GraphQL is a query language

Expressive type system

Inputs

Define an explicit shape of all input objects for mutations. Save the work of manually parsing and validating the shape and size of user input. Let GraphQL do the grunt work!

GraphQL is a query language

Expressive type system

Inputs

mutation createExpense($input: CreateExpenseInput!) {
  createExpense(input: $input) {
    id
  }
}
input CreateExpenseInput {
  type: ExpenseType!
  amount: Float!
  description: String
}

GraphQL is a query language

Expressive type system

Modifiers

When reading data the GraphQL runtime responds with error messages communicating the state of the fulfilled query. Any non conformity to the schema is reported as an error.

GraphQL is a query language

Expressive type system

Modifiers

!     # Non null
[]    # List

How it works

name: String         # nullable name
name: String!        # non nullable name
names: [String]      # nullable list of nullable names
names: [String]!     # non nullable list of nullable names
names: [String!]!    # non nullable list of non nullable names

GraphQL is a query language

Expressive type system

Modifiers on types

query {
  user {
    name
  }
}
type User {
  id: ID!
  name: String!
}
type Query {
  user: User
}
{
  "errors": [
    { "message": "Cannot return null for non-nullable field User.name.", }
  ],
  "data": {
    "user": null
  }
}

GraphQL is a query language

Expressive type system

Modifiers on arguments

query {
  user {
    name
  }
}
type User {
  id: ID!
  name: String!
}
type Query {
  user(id: ID!): User
}
{
  "errors": [
    {
      "message": "Field \"viewer\" argument \"id\" of type \"ID!\" is required but not provided.",
    }
  ]
}

GraphQL is a query language

Expressive type system

Modifiers on inputs 😍

mutation {
  createUser(input: { name: "Jazzy Scooter" }) {
    name
  }
}
input CreateUserInput {
  name: String!
  email: String!
}
type Mutation {
  createUser(input: CreateUserInput!): User
}
{
  "errors": [
    {
      "message": "Field CreateUserInput.email of required type String! was not provided.",
    }
  ]
}

GraphQL is a query language

Expressive type system

Unions

A set of possible types with no guarantee of consistency between fields.

GraphQL is a query language

Expressive type system

Unions

type SocialMediaPost {
  content: String
}
type BlogPost {
  title: String
  content: String
}
union PostUnion = BlogPost | SocialMediaPost
type Query {
  posts: [PostUnion]
}

GraphQL is a query language

Expressive type system

Unions

query {
  posts {
    ... on SocialMediaPost {
      content
    }
    ... on BlogPost {
      title
      content
    }
  }
}
{
  "data": {
    "posts": {
      { "content": "I ate a donut!" },
      { "title": "🍩 are tasty.", "content": "That is all." },
    }
  }
}

GraphQL is a query language

Expressive type system

Interface

An abstract type declaring common fields and arguments. Any object implementing an interface is guarantees certain fields exist.

GraphQL is a query language

Expressive type system

Interface

interface MutationResult {
  notice: String
  success: Boolean!
}
type CreateExpensePayload implements MutationResult {
  result: Expense
  success: Boolean!
}
mutation {
  createExpense(input: CreateExpenseInput!): CreateExpensePayload
}

GraphQL is a query language

Expressive type system

Interface

mutation createExpense($input: CreateExpenseInput!) {
  createExpense(input: $input) {
    success
    result {
      amount
    }
  }
}
{
  "data": {
    "createExpense": {
      "success": true,
      "result": {
        "amount": 42.0
      }
    }
  }
}

GraphQL is a runtime

GraphQL is a runtime layer between the client and your business logic. It is responsible for analyzing, validating, and resolving incoming requests. GraphQL implementations currently exist for: C# / .NET, Clojure, Elixir, Erlang, Go, Groovy, Java, JavaScript, PHP, Python, Scala, Ruby

GraphQL is a runtime

Analysis

Queries can become complex and tax your system of resources 😫. The runtime performs complexity and depth analysis on all incoming requests, and if you wish, it can only allow queries within a specific threshold.

MySchema = GraphQL::Schema.define do
 max_depth 10          # Prevent deeply nested resources
 max_complexity 100    # Reject complex queries
end

GraphQL is a runtime

Validation
query {
  user {
    juicyBitcoins
  }
}
{
  "errors": [
    {
      "message": "Cannot query field \"juicyBitcoins\" on type \"User\".",
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ]
    }
  ]
}

GraphQL is a runtime

Resolution

The runtime resolves fields. By default, fields are resolved from the underlying struct and the values are returned for the field.

GraphQL is a runtime

Resolution

Resolvers are anonymous functions called with three arguments

query {
  currentUser {
    email
  }
}
const resolvers = {
  Query: {
    currentUser: (object, args, context) => context.currentUser
  }
}
// object      # In memory object (think ORM)
// args        # GQL query arguments
// context     # Global state

GraphQL is a runtime

Resolution

Define resolvers inline, if you want.

const resolvers = {
  User: {
    emailAddressLength: object => object.email.length,
    randomFloat: () => Math.random()
  }
}
type User {
  email: String
  emailAddressLength: Int
  randomFloat: Float
}
{
  "data": {
    "currentUser": {
      "email": "foo@example.com",
      "emailAddressLength": 15,
      "randomFloat": 0.008373373072433798,
    }
  }
}

Benefits

There are many. Here are a few.

Benefits

Explorable APIs

Add descriptions. Deprecate fields. Communicate.

search

Benefits

Explorable APIs

Add descriptions. Deprecate fields. Communicate.

intellisense

Benefits

Explorable APIs

Add descriptions. Deprecate fields. Communicate.

deprecated

Benefits

Explorable APIs

Add descriptions. Deprecate fields. Communicate.

enum descriptions

Benefits

Explorable APIs

Add descriptions. Deprecate fields. Communicate. Some good examples:

Benefits

Static Types πŸ‹οΈ

Push input paramater validations out into the GraphQL runtime and build user interfaces to consume and bubble up responses.

Benefits

Declarative User Interface

Design components that speak GraphQL and only request data required to render the component

Benefits

Declarative User Interface
const query = gql`
  query {
    greeting
  }
`
const Greeter = props => (
  <div>
    <h1> {props.data.greeting} </h1>
  </div>
)
export default graphql(query)(Greeter);

See Apollo GraphQL for more information

Benefits

Composable APIs

Present N distinct GraphQL APIs into a single schema with a concept known as schema stitching.

See Schema stitching for more information. Example

Benefits

Composable APIs

stitching

Benefits

Performance Insight

The GraphQL runtime provides executions hooks into each of your resolvers. It is possible to know the time it takes to resolve each field.

Benefits

Performance Insight

tracing

Questions?

The end πŸ‘‹