Flying high with

Apollo Client 2 + React

slides.com/gerardsans | @gerardsans

Google Developer Expert

Google Developer Expert

International Speaker

Spoken at 64 events in 23 countries

Blogger

Blogger

Community Leader

900

1.2K

Trainer

Master of Ceremonies

Master of Ceremonies

GraphQL Timeline

2012

GraphQL created at Facebook

2013

React is released

2014

React Native is released

2015

GraphQL is open sourced

Relay Classic is open sourced

2016

New GraphQL website graphql.org

First GraphQL Summit

GitHub announces GraphQL API

2017

Relay Modern 1.0

Apollo Client 2.0

Relay vs Apollo Downloads

launchpad.graphql.com

graphql.com

Developer Tools

Todo App

GraphQL Schema

Type System

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

Schema Syntax

  • Optional: String, Todo
  • Mandatory: String!, Todo!
  • Arrays: [String], [Todo]
npm install --global graphcool
graphcool init server
graphcool deploy
graphcool console

graphcool-cli

Todos Schema

// project.graphcool

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

demo

Apollo Client

2.0

Apollo

Cache

Apollo Client

Apollo

Link

Apollo Cache

Packages

  • apollo-cache-inmemory
  • apollo-cache-hermes
  • apollo-cache-persist

InMemoryCache

  • Normalised and flattened
  • this.props.client.cache
  • readQuery, writeQuery
  • readFragment, writeFragment

Add Todo

const query = gql`query AllTodos {
  allTodoes { id text complete }
}`;
const data = store.readQuery({ query });
const newTodo = {
  id: createTodo.id,
  text: input.value,
  complete: false,
  __typename: "Todo"
};
store.writeQuery({
  query,
  data: {
    allTodoes: [...data.allTodoes, newTodo],
  },
});

Apollo Link

Packages

  • apollo-link
  • apollo-link-http
  • apollo-link-ws
  • apollo-link-error
  • apollo-link-batch
  • apollo-link-polling
  • apollo-link-retry

Packages (future)

  • apollo-link-store
  • apollo-link-rest
  • apollo-link-offline

Apollo Link

  • concat(link1, link2)
  • split(expr, link1, link2)
  • from([link1, link2])

Security Headers

import { ApolloLink } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';

const httpLink = new HttpLink({ uri: URI });
const authLink = new ApolloLink((operation, forward) => {
  const token = localStorage.getItem('authtoken');
  if (token) {
    operation.setContext({
      headers: { Authorization: token }
    });
  }
  return forward(operation);
})

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

Using  GraphQL

GraphQL Server

source: blog

Dependencies

Setup

import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';

export const client = new ApolloClient({
  link: new HttpLink({ uri: URI}),
  cache: new InMemoryCache()
});

Bootstrap

// index.js
import { ApolloProvider } from 'react-apollo';
import { client } from './client';

ReactDOM.render(
  <ApolloProvider client={client}>
    <App/>
  </ApolloProvider>, 
  document.getElementById('root')
);
registerServiceWorker();

Todos Schema

schema { 
  query: Query,  
  mutation: Mutation 
}

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

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

query

const TODOS = gql`
  query todos {
    allTodoes { id text complete }
  }`

const withTodos = graphql(TODOS, {
    props: ({ ownProps, data }) => {
      if (data.loading) return { loading: true }
      if (data.error) return { hasErrors: true }
      return {
        todos: data.allTodoes,
      }
    },
})

mutate

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 },
        update: (store, { data: { createTodo } }) => {
          const data = store.readQuery({ query: TODOS });
          data.allTodoes.push(createTodo);
          store.writeQuery({ query: TODOS, data });
        },
      })
    },
  }),
})

demo

Adding Realtime

GraphQL Server

source: blog

Dependencies

Setup

import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';

export const client = new ApolloClient({
  link: setupLink(),
  cache: new InMemoryCache()
});

Setup

function setupLink() {
  const httpLink = new HttpLink({ uri: URI });
  const wsLink = new WebSocketLink({
    uri: URI_WS,
    options: { reconnect: true }
  });
  const isSubscription = ({ query }) => {
    const { kind, operation } = getMainDefinition(query);
    return kind==='OperationDefinition' && operation==='subscription';
  }
  const link = split(
    isSubscription,
    /* if true use  */ wsLink,
    /* otherwise */ httpSecured,
  );
  return link;
}

Todos Schema

schema { 
  subscription: Subscription 
}

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

type Subscription {
  Todo(filter: [CREATED, UPDATED, DELETED]): {
    mutation: [CREATED, UPDATED, DELETED]
    node: Todo
    updatedFields: [String!]
    previousValues: Todo
  }
}

subscribeToMore

// app.component.ts
query.subscribeToMore({
  document: gql`
    subscription {
      Todo(filter: { mutation_in: [CREATED] }) {
        node { id complete text }
      }
    }
  `,
  updateQuery: (state, { subscriptionData }) => {
    ...
  }

subscribeToMore

updateQuery: (state, { subscriptionData }) => {
  const {node} = subscriptionData.data.Todo;

  if (!state.allTodoes.find(todo => todo.id === node.id)) {
    state.allTodoes.push(node);                
  }
  return {
    allTodoes: todos
  }
}

demo

Why use GraphQL?

Some reasons

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

More

gsans/todo-apollo-v2-react