GraphQL

GraphQL

A data query language and runtime

GraphQL

  • Declarative
  • Compositional
  • Strong-typed

DECLARATIVE

  • query responses decided by the client
  • query returns exactly what a client asks for

COMPOSITIONAL

  • query is a hierarchical set of fields
  • query is shaped just like the data it returns
  • natural way to describe data requirements

STRONG-TYPED

  • query can be ensured to be valid within
    a GraphQL type system at development time
  • this allows the server to make guarantees about the response
  • makes it easier to build high-quality client tools

"OK, but why?"

REST API

  • easy resource access and management
  • resource relationships
    • shallow resources - multiple requests
    • nested resources - expensive support

GraphQL

  • ask for all data you need in query
  • send query to single endpoint
  • receive all data in one response
  • to modify data call mutations

Play time!

{

}

empty query

{
    posts {
        title
    }
}

posts query (titles)

{
    posts {
        title,
        _id,
        summary
    }
}

posts query (title, id, summary)

{
    posts {
        title,
        _id,
        summary
    },
    authors {
        name
    }
}

posts query and authors query

{
  posts {
    _id,
    title,
    timestamp,
    summary
  }
  posts {
    _id,
    title,
    content,
    category
  }
}

different results for same schema

{
  postsShort: posts {
    _id,
    title,
    timestamp,
    summary
  }
  postsFull: posts {
    _id,
    title,
    content,
    category
  }
}
{
   author(_id: "indi"){
    _id,
    name
  }
  recentPosts(count: 5) {
    title,
    summary,
    timestamp
  }
}

query with params

named query results

{
   currentAuthor: author(_id: "indi"){
    _id,
    name
  }
  currentPosts: recentPosts(count: 5) {
    title,
    summary,
    timestamp
  }
  allPosts: posts {
    title,
    content,
    summary
  }
}

Single entries

{
   indi: author(_id: "indi"){
    _id,
    name,
    twitterHandle
  }
  arunoda: author(_id: "arunoda"){
    _id,
    name,
    twitterHandle
  }
  pahan: author(_id: "pahan"){
    _id,
    name,
    twitterHandle
  }
}

Nested queries

{
    posts {
        title,
        _id,
        author {
            name
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content
        },
        author {
            _id,
            name
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                _id,
                name
            }
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                _id,
                name
            }
        },
        author {
            _id,
            name
        }
    }
}

Repetitive queries - use fragments

{
   indi: author(_id: "indi"){
    _id,
    name,
    twitterHandle
  }
  arunoda: author(_id: "arunoda"){
    _id,
    name,
    twitterHandle
  }
  pahan: author(_id: "pahan"){
    _id,
    name,
    twitterHandle
  }
}
{
   indi: author(_id: "indi"){
    ...authorData
  }
  arunoda: author(_id: "arunoda"){
    ...authorData
  }
  pahan: author(_id: "pahan"){
    ...authorData
  }
}

fragment authorData on Author {
  _id,
  name,
  twitterHandle
}

Repetitive queries - use fragments

{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                _id,
                name
            }
        },
        author {
            _id,
            name
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                ...authorName
            }
        },
        author {
            ...authorName
        }
    }
}

fragment authorName on Author {
  _id,
  name
}

Repetitive queries - use fragments and interfaces

{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                _id,
                name
            }
        },
        author {
            _id,
            name
        }
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            ...authorOf
        },
        ...authorOf
    }
}

fragment authorOf on HasAuthor {
    author {
  	_id,
  	name, 
    }
}
{
    posts {
        title,
        _id,
        comments {
            _id,
            content,
            author {
                ...authorName
            }
        },
        author {
            ...authorName
        }
    }
}

fragment authorName on Author {
  _id,
  name
}

Change data - Mutations

mutation {
  createAuthor(
    _id: "john",
    name: "John Carter",
    twitterHandle: "@john"
  ) {
    _id
    name
  }
}
mutation {
  sam: createAuthor(
    _id: "sam",
    name: "Sam Hautom"
    twitterHandle: "@sam"
  ) {
    _id
    name
  },

  chris: createAuthor(
    _id: "chris",
    name: "Chris Mather"
    twitterHandle: "@chris"
  ) {
    _id
    name
  } 
}

Change data - Mutations

mutation {
  carter: createAuthor(
    _id: "carter",
    name: "Carter Boom"
    twitterHandle: "@carter"
  ) {
    _id
  },

  carter2: createAuthor(
    _id: "carter",
    name: "Carter Boom"
    twitterHandle: "@carter"
  ) {
    _id
  }
}
  • queries are concurrent
  • mutations are sequential! 

Query Variables

{
  recentPosts(count: 10) {
    title
  }
}

for hard-coded values

query getFewPosts {
  recentPosts(count: 10) {
    title
  }
}

full query syntax (with query name)

query getFewPosts($postCount: Int!) {
  recentPosts(count: $postCount) {
    title
  }
}

query with declared variable

Query Variables

multiple variables

query getFewPosts($postCount: Int!, $commentCount: Int) {
  recentPosts(count: $postCount) {
    title,
    comments(limit: $commentCount) {
      content
    }
  }
}
query getFewPosts($postCount: Int!, $commentCount: Int) {
  recentPosts(count: $postCount) {
    title,
    ...comments
  }
}

fragment comments on Post {
  comments(limit: $commentCount) {
    content
  }
}

variables in fragments

So, where that query goes?

You can send GraphQL queries through HTTP:

  • GET for querying
  • POST for mutation

GraphQL

  • "API pattern"
  • query language
  • specification
  • reference implementations

Implementations

  • PHP
  • Ruby
  • Python
  • C / C++
  • Scala
  • Elixir
  • Go
  • .NET
  • Haskell
  • JavaScript

TODO:

  • define graph of "resources"
  • send query to single endpoint
  • receive all data in one response

What about client?

  • send query string & get JSON data
  • use Relay
  • use Lokka
  • more clients soon

Risks

  • caching
  • N + 1 problem hard to detect
  • abusive

GraphQL

By Krzysztof Jung

GraphQL

  • 1,258