Unleashing the power of GraphQL

slides.com/gerardsans | @gerardsans

Google Developer Expert

Master of Ceremonies

Blogger

GraphQL

GraphQL co-authors

Lee Byron

Nick Schrock

Dan Schafer

GraphQL Timeline

2012

GraphQL created at Facebook

Support Mobile Native Teams

2015

Open sourced

First Specification

GraphQL.js

2016

GitHub announces GraphQL API

New GraphQL website

First GraphQL Summit

GraphQL First

 

2017

GraphQL Europe

Apollo Client 0.10.0 (RC-0)

graphql.org

Apollo Optics

May

graphql-europe.org

Speakers

Lee Byron

Sashko Stubailo

Implementations

Query Language


// GET '/graphql'
{
 user(name: "gsans") {
  twitter
 }
}

// result
{
 "user": {
  "twitter": "@gerardsans"
 }
}

GraphQL Server

source: blog

Who is using it?

Solution Architecture

demo

Components Tree

<app>
  <add-todo>
    <input><button>Add todo</button>
  </add-todo>
  <todo-list>
    <ul>
      <todo id="0" completed="false"><li>buy milk</li></todo>
    </ul>
  </todo-list>
  <filters>
    Show: <filter-link><a>All</a><filter-link> ... 
  </filters>
</app>

GraphQL Schema

Type System

  • Scalar Types: Int, Float, String, Boolean, ID 
  • Object Types: Todo
  • Entry points: Query, Mutation, Subscription*

Schema Syntax

  • Nullable: String, Todo
  • Non-nullable: String!, Todo!
  • Arrays: [String], [Todo]

Todos Schema (1/2)

schema { 
  query: Query,  
  mutation: Mutation 
}

type Query {
  allTodoes(skip: Int, take: Int): [Todo!]!
}

type Todo {  
  id: ID!
  text: String!
  complete: Boolean!
}

Todos Schema (2/2)

schema { 
  query: Query,  
  mutation: Mutation 
}

type Mutation {
  createTodo(text: String!, complete: Boolean!): Todo
  updateTodo(id: ID!, text: String, complete: Boolean): Todo
}

GraphiQL

demo

Apollo Client

GraphQL Server

source: blog

Dependencies

Setup

// client.js
import ApolloClient, { createNetworkInterface } from 'apollo-client'

const networkInterface = createNetworkInterface({
  uri: 'https://api.graph.cool',
  dataIdFromObject: record => record.id, // will be used by caching
})
export const client = new ApolloClient({ networkInterface })

Todo App

// TodoApp.js
class TodoApp extends React.Component {
  render () {
    return (
      <div>
        <AddTodo addTodo={this.props.addTodo} />
        <TodoList
          todos={this.props.todos || []}
          filter={this.props.currentFilter}
          toggleTodo={this.props.toggleTodo}
        />
        <Filters setFilter={this.props.setFilter} 
          filter={this.props.currentFilter} />
      </div>
    )
  }
}

Bootstrap

// app.js
import { ApolloProvider } from 'react-apollo'

let combinedReducer = combineReducers({
  filter, 
  apollo: client.reducer(),
})

const store = compose(
  applyMiddleware(client.middleware())
)(createStore)(combinedReducer)

render(
  <ApolloProvider store={store} client={client}>
    <TodoApp />
  </ApolloProvider>,
  document.getElementById('root')
)

Main APIs

  • query
  • mutate
  • updateQueries (reducer)

query

// TodoApp.js
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'

const withTodos = graphql(
  gql`query todos {
    allTodoes {
      id complete text
    }
  }`, {
    props: ({ ownProps, data }) => {
      if (data.loading) return { userLoading: true }
      if (data.error) return { hasErrors: true }
      return {
        todos: data.allTodoes,
      }
    }
  })

mutate

// TodoApp.js
const withAddTodo = graphql(
  gql`mutation addTodo($text: String!) {
    createTodo(text: $text, complete: false) { id text complete }
  }`,
  {
    props: ({ ownProps, mutate }) => ({
      addTodo (text) {
        return mutate({
          variables: { text },
          updateQueries: {
            todos: (state, { mutationResult }) => {
              return {
                allTodoes: [...state.allTodoes, 
                  mutationResult.data.createTodo
                ],
              }
         ...

mutate (immutability-helper)

// TodoApp.js
const withAddTodo = graphql(
  gql`mutation addTodo($text: String!) {
    createTodo(text: $text, complete: false) { id text complete }
  }`,
  {
    props: ({ ownProps, mutate }) => ({
      addTodo (text) {
        return mutate({
          variables: { text },
          updateQueries: {
            todos: (state, { mutationResult }) => {
              return update(state, {
                allTodoes: {
                  $push: [ mutationResult.data.createTodo ],
                },
              })
         ...

connect

// TodoApp.js
import { connect } from 'react-redux'
const withTodos = graphql(...)
const withAddTodo = graphql(...)
const withToggleTodo = graphql(...)

const TodoAppWithState = connect(
  (state) => ({ currentFilter: state.filter }),
  (dispatch) => ({
    setFilter: (filter) => {
      dispatch({
        type: 'SET_FILTER',
        filter,
      })
    },
  }),
)(TodoApp)

export default withTodos(withAddTodo(withToggleTodo(TodoAppWithState)))

Subscriptions

(Experimental)

GraphQL Server

source: blog

Dependencies

Setup

// client.ts
import { Client } from 'subscriptions-transport-ws';

export const wsClient = new Client('ws://subscriptions.graph.cool/ZZZ', {
  timeout: 10000
});

Posts Schema

schema { 
  query: Query,  
  mutation: Mutation,
  subscription: Subscription 
}
type Post { 
  id: ID! 
  description: String! 
  imageUrl: String!
}
type Mutation {
  createPost(description: String!, imageUrl: String!): Post
}

type Subscription {
  createPost(): Post
}

wsClient.subscribe

// feed.component.ts

wsClient.subscribe({
  query: `subscription newPosts {
    createPost { id description imageUrl }  
  }`,
  variables: null
}, (err, res) => {
  // res.createPost = { id: 34, description: 'woot', imageUrl: '...' } 
  // err = [{ message: 'Connection Timeout', stack: '...' }]
});

Why use GraphQL?

Some reasons

  • Declarative
  • De-coupled from storage
  • Validated and structured
  • Facilitates Collaboration
  • Super fast

Examples

Instagram

World Chat

Links

Thanks!