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
My React (Native) app speaks GraphQL
By Guillaume Diallo-Mulliez
My React (Native) app speaks GraphQL
A presentation of GraphQL and how to connect a React (Native) app to it using Apollo Client.
- 817