My React (Native) app speaks GraphQL

=

+

+

Underfetching
&
Overfetching

Guillaume Diallo-Mulliez

Architecte Développeur

guitoof

@Guitoof

My React (Native) app speaks GraphQL

What's GraphQL ?

Some cool features

How to connect my React Native app to a GraphQL API ?

What's GraphQL ?

A query language for your APIs 

Underfetching

[
  {
    "id": "UG9rZW1vbjowMDE=",
    "name": "Bulbasaur",
    "image": "https://img.pokemondb.net/artwork/bulbasaur.jpg"
  },
  {
    "id": "UG9rZW1vbjowMDQ=",
    "name": "Charmander",
    "image": "https://img.pokemondb.net/artwork/charmander.jpg"
  },
  {
    "id": "UG9rZW1vbjowMDc=",
    "name": "Squirtle",
    "image": "https://img.pokemondb.net/artwork/squirtle.jpg"
  },
]


    "id"
    "name"
    "image"

 
    "id"
    "name"
    "image"
  
  
    "id"
    "name"
    "image"
  
http://rest.pokedex.com/pokemons

Overfetching

http://rest.pokedex.com/pokemon/:id
[
  {
    "id": "UG9rZW1vbjowMDE=",
    "name": "Bulbasaur",
    "image": "https://img.pokemondb.net/artwork/bulbasaur.jpg"
  },
  {
    "id": "UG9rZW1vbjowMDQ=",
    "name": "Charmander",
    "image": "https://img.pokemondb.net/artwork/charmander.jpg"
  },
  {
    "id": "UG9rZW1vbjowMDc=",
    "name": "Squirtle",
    "image": "https://img.pokemondb.net/artwork/squirtle.jpg"
  },
]
http://rest.pokedex.com/pokemons
{
    "id": "UG9rZW1vbjowMDE=",
    "name": "Bulbasaur",
    "image": "https://img.pokemondb.net/artwork/bulbasaur.jpg",
    "classification": "Seed Pokémon",
    "types": [
      "Grass",
      "Poison"
    ],
    "resistant": [
      "Water",
      "Electric",
      "Fighting",
    ],
    "maxCP": 951,
    "maxHP": 1071,
    "weight": {
      "minimum": "6.04kg",
      "maximum": "7.76kg"
    },
    "height": {
      "minimum": "0.61m",
      "maximum": "0.79m"
    },
},





    "types": [
      "Grass",
      "Poison"
    ],

Single Endpoint

http://rest.pokedex/pokemons

http://rest.pokedex/types

http://rest.pokedex/pokemons/25/attacks

http://rest.pokedex/pokemons/5/types

http://rest.pokedex/pokemons/42/evolutions

Rest

GraphQL

http://gql.pokedex/graphql

Fetch what

you need

A Graph structure

Auto Documented

Multilingual

GraphQL Users

More on GraphQL ?

?

What's

Apollo ?

A GraphQL client designed to  make it easy to build UI components that fetch data with GraphQL

What's Apollo ?

Component

GraphQL API

Apollo Client

Container

Component

Container

Component

Container

Apollo Client users ?

First steps ?

Initialization

$ npm install apollo-client react-apollo apollo-link-http apollo-cache-inmemory
const client = new ApolloClient({
  link: new HttpLink({ uri: 'http://pokedex-graphql-api/' }),
  cache: new InMemoryCache()
});

export default class App extends React.Component {
  render() {
    return (
      <ApolloProvider client={client}>
        <PokedexApp />
      </ApolloProvider>
    );
  }
}
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloProvider } from 'react-apollo';

Import

Configure

Install

              apollo-client
const client = new ApolloClient({
  

});

import { ApolloClient } from 'apollo-client';
$ npm install apollo-client react-apollo apollo-link-http apollo-cache-inmemory
                                         apollo-link-http
$ npm install apollo-client react-apollo apollo-link-http apollo-cache-inmemory
                                                          apollo-cache-inmemory
$ npm install apollo-client react-apollo apollo-link-http apollo-cache-inmemory
                            react-apollo
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloProvider } from 'react-apollo';

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


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



import { ApolloProvider } from 'react-apollo';
const client = new ApolloClient({
  link: new HttpLink({ uri: 'http://pokedex-graphql-api/' }),
  cache: new InMemoryCache()
});

export default class App extends React.Component {
  render() {
    return (
      <ApolloProvider client={client}>
        <PokedexApp />
      </ApolloProvider>
    );
  }
}

  link: new HttpLink({ uri: 'http://pokedex-graphql-api/' }),
const client = new ApolloClient({
  link: new HttpLink({ uri: 'http://pokedex-graphql-api/' }),
  cache: new InMemoryCache()
});

export default class App extends React.Component {
  render() {
    return (
      <ApolloProvider client={client}>
        <PokedexApp />
      </ApolloProvider>
    );
  }
}


  cache: new InMemoryCache()
const client = new ApolloClient({
  link: new HttpLink({ uri: 'http://pokedex-graphql-api/' }),
  cache: new InMemoryCache()
});

export default class App extends React.Component {
  render() {
    return (
      <ApolloProvider client={client}>
        <PokedexApp />
      </ApolloProvider>
    );
  }
}








      <ApolloProvider client={client}>
        
      </ApolloProvider>

Component

GraphQL API

Apollo Client

Container

apollo-link-http

apollo-cache-inmemory

react-apollo

Queries

 

 

Mutations

GET

 

 

 

 

 

POST

Fetching

&

Posting

Queries

Fetching Data









                pokemons


                                    item.image
                     item.name










    const { pokemons } = this.props.data;


          data={pokemons}


              <Image source={{ uri: item.image }}  />
              <Text>{item.name}</Text>





import React, { Component, Fragment } from 'react';
import { FlatList, Text, View } from 'react-native';

export default class Pokedex extends Component {
  render() {
    const { pokemons } = this.props.data;
    return (
        <FlatList
          data={pokemons}
          renderItem={({ item }) => (
            <Fragment key={item.id}>
              <Image source={{ uri: item.image }}  />
              <Text>{item.name}</Text>
            </Fragment>
          )}
        />
    );
  }
}

Component

GraphQL API






  query {
    pokemons(first: 9) {
      id
      name
      image
    }
  }

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import Pokedex from './Pokedex.component';

const GET_POKEMONS_QUERY = gql`
  query {
    pokemons(first: 9) {
      id
      name
      image
    }
  }
`;

export default graphql(GET_POKEMONS_QUERY)(Pokedex);

Apollo Client

Container

class Pokedex extends Component {
    render() {
        const { pokemons } = this.props.data;
        ...
    }
}


        const { pokemons } = this.props.data;

Queries

List Component

GraphQL API

Apollo Client

Container

Detail Component

Container

const GET_POKEMONS_QUERY = gql`
  query GetPokemons {
    pokemons(first: 9) {
      id
      name
      image
    }
  }
`;
const GET_DETAILED_POKEMON_QUERY = gql`
  query GetDetailedPokemon($id: Int!) {
    pokemon(id: $id) {
        id
        name
        classification
        height {
          minimum
          maximum
        }
        weight {
          minimum
          maximum
        }
        evolutions {
          id
          name
        }
    }
  }
`;

Queries

Posting Data

Mutations

Mutations

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
import Pokemon from './Pokemon.component';

const GIVE_NICKNAME_MUTATION = gql`
  mutation GiveNickname($pokemonId: ID!, $nickname: String) {
    giveNickname(pokemonId: $pokemonId, nickname: $nickname) {
      id
      name
      nickname
      image
    }
  }
`;

export default graphql(GIVE_NICKNAME_MUTATION)(Pokemon);





  mutation GiveNickname($pokemonId: ID!, $nickname: String) {
    giveNickname(pokemonId: $pokemonId, nickname: $nickname) {
      id
      name
      nickname
      image
    }
  }

Component

GraphQL API

Apollo Client

Container

export default class Pokemon extends Component {

  _givePokemonNickname = () => {
    this.props.giveNickname({
      variables: {
        pokemonId: this.props.pokemonId,
        nickname: this.state.nickname
      }
    })
  }

  render() {
    return (
        ...
        <Button title="Rename" onPress={this._givePokemonNickname}
    );
  }
}


  _givePokemonNickname = () => {
    this.props.giveNickname({
      variables: {
        pokemonId: this.props.pokemonId,
        nickname: this.state.nickname
      }
    })
  }















        <Button title="Rename" onPress={this._givePokemonNickname}

export default class Pokemon extends Component {

  _givePokemonNickname = () => {
    this.props.giveNickname({
      variables: {
        pokemonId: this.props.pokemonId,
        nickname: this.state.nickname
      }
    })
  }

  render() {
    return (
        ...
        <Button title="Rename" onPress={this._givePokemonNickname}
    );
  }
}

Updated Component

update data after mutation

refetchQueries

update

optimisticResponse

mutation

response

update queries

mutation

response

mutation

Going further ?

       Loading

class Pokedex extends Component {
    render() {
        const { pokemons } = this.props.data;
        ...
    }
}


        const { pokemons } = this.props.data;


                          loading
        if (loading) return <ActivityIndicator />;
class Pokedex extends Component {
    render() {
        const { pokemons, loading } = this.props.data;
        
    }
}


                          loading

       Error handling

class Pokedex extends Component {
    render() {
        const { pokemons } = this.props.data;
        ...
    }
}


        const { pokemons } = this.props.data;


                          error
        if (error) return <ErrorComponent error={error} />;
class Pokedex extends Component {
    render() {
        const { pokemons, error } = this.props.data;
        
    }
}


                          error

       Flexible Caching

export default compose(
  graphql(GET_POKEMONS_QUERY, {
    options: {
        fetchPolicy: 'cache-and-network'
    }
  }),
)(Pokedex);

cache-first

cache-and-network

network-only

cache-only

Ready for Take-off ?

Optimisic UI

Subscriptions

Pagination

Authentication

Dev Tools (web)

a few more features ...

GraphQL

Useful links

Apollo

Thank You