GraphQL with React and Relay in Restorando

José Fresco

JavaScript Developer at Restorando

@jfresco82

The problem

Public API

Redo Core

The problem

Public API

Redo Core

Mobile API

The problem

Public API

Redo Core

Ad-hoc code

Mobile API

The problem

Public API

Redo Core

Ad-hoc code

M-API v1

Ad-hoc code

M-API v2

The problem

Public API

Redo Core

Ad-hoc code

M-API v1

Ad-hoc code

M-API v2

Ad-hoc code

M-API v3

The solution

Public API

Redo Core

Ad-hoc code

M-API v1

Ad-hoc code

M-API v2

Ad-hoc code

M-API v3

GraphQL

The solution

Public API

Redo Core

Ad-hoc code

M-API v1

Ad-hoc code

M-API v2

Ad-hoc code

M-API v3

GraphQL

The solution

Redo Core

GraphQL

The solution

Merge all APIs in a single GraphQL endpoint

GraphQL

GraphQL

It's a language created by Facebook

 

Query or alter data in a remote repository

GraphQL

Been used in Facebook mobile apps since 3+ years

GraphQL

{
  restaurant (id: 6) {
    name,
    address
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "address": "Sucre 676"
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    address
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "address": "Sucre 676"
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    address
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "address": "Sucre 676"
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    address,
    phone
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "address": "Sucre 676",
      "phone": "1148315716"
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    address,
    phone,
    cuisines {
      name
    }
  }
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "address": "Sucre 676",
      "phone": "1148315716",
      "cuisines": [
        {
          "name": "De autor"
        }
      ]
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    primary_photo
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    primary_photo (version: SQUARE)
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    primary_photo (version: SQUARE) {
      url
    }
  }
}

GraphQL

{
  restaurant (id: 6) {
    name,
    primary_photo (version: SQUARE) {
      url
    }
  }
}
{
  "data": {
    "restaurant": {
      "name": "Sucre",
      "primary_photo": {
        "url": "http://imagescdn.r..."
      }
    }
  }
}

Demo

Schema

The only source of truth

Schema

Defines queryable entities

Knows how to fetch data

Schema

Defines mutable entities

Knows how to alter data

Schema

Represents the data in a graph

Schema

Data source agnostic

Tasks

Set up a schema

Build a GraphQL endpoint

Setting up schema

npm install --save graphql

Setting up schema

new GraphQLSchema({
  query: new GraphQLObjectType({
    ...
  }),
  mutations: new GraphQLObjectType({
    ...
  }),
})

Setting up schema

new GraphQLSchema({
  query: new GraphQLObjectType({
    ...
  }),
  mutations: new GraphQLObjectType({
    ...
  }),
})

Setting up schema

new GraphQLSchema({
  query: new GraphQLObjectType({
    ...
  }),
  mutations: new GraphQLObjectType({
    ...
  }),
})

Setting up schema

new GraphQLSchema({
  query: new GraphQLObjectType({
    ...
  }),
  mutations: new GraphQLObjectType({
    ...
  }),
})

Setting up schema

{
}

Setting up schema

{
  name: 'Query',
  description: 'Root query'
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
  
  })
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
    
    }
  })
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
      description: 'Restaurant representation',
    }
  })
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
      description: 'Restaurant representation',
      type: RestaurantType,
    }
  })
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
      description: 'Restaurant representation',
      type: RestaurantType,
      args: {
        id: {
          type: new GraphQLNonNull(GraphQLID)
        }
      }
    }
  })
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
      description: 'Restaurant representation',
      type: RestaurantType,
      args: {
        id: {
          type: new GraphQLNonNull(GraphQLID)
        }
      },
      resolve: resolveFn
    }
  }
}

Setting up schema

{
  name: 'Query',
  description: 'Root query',
  fields: () => ({
    restaurant: {
      description: 'Restaurant representation',
      type: RestaurantType,
      args: {
        id: {
          type: new GraphQLNonNull(GraphQLID)
        }
      },
      resolve: resolveFn
    }
  }
}

GraphQL Schema

Self documented

Introspective

GraphQL endpoint

npm install --save express-graphql

GraphQL endpoint

import express from 'express'
import graphqlHTTP from 'express-graphql'
import mySchema from '../schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  graphiql: true
}))

GraphQL endpoint

import express from 'express'
import graphqlHTTP from 'express-graphql'
import mySchema from '../schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  graphiql: true
}))

GraphQL endpoint

import express from 'express'
import graphqlHTTP from 'express-graphql'
import mySchema from '../schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  graphiql: true
}))

GraphQL endpoint

import express from 'express'
import graphqlHTTP from 'express-graphql'
import mySchema from '../schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  graphiql: true
}))

GraphQL endpoint

import express from 'express'
import graphqlHTTP from 'express-graphql'
import mySchema from '../schema'

const app = express()

app.use('/graphql', graphqlHTTP({
  schema: mySchema,
  graphiql: true
}))

GET /graphql: GraphiQL

POST /graphql

POST /graphql HTTP/1.1
Content-Type: application/json

{
  "query": "{ restaurant(id:6) { name, address, cuisines { name } } }"
}

POST /graphql

POST /graphql HTTP/1.1
Content-Type: application/json

{
  "query": "{ restaurant(id:6) { name, address, cuisines { name } } }"
}

POST /graphql

POST /graphql HTTP/1.1
Content-Type: application/json

{
  "query": "{ restaurant(id:6) { name, address, cuisines { name } } }"
}

POST /graphql

POST /graphql HTTP/1.1
Content-Type: application/json

{
  "query": "{ restaurant(id:6) { name, address, cuisines { name } } }"
}

POST /graphql

Thoughts on GraphQL

RFC spec in development

Implementations on 13+ languages

Awesome alternative to REST APIs

Facebook & community backed

Performant Promises handling

Relay

Relay

Framework for building data-driven React apps

Fetches data from a GraphQL endpoint

Flux pattern implementation

Centralized store

Caching, smart diffs, auto-retries

Wraps React components into Containers

Creating containers

Creating containers

Creating containers

Creating containers

<Header />
<Details>
  
  <Info />
  


  <Photos />
  
  <Badges />
</Details>

Creating containers

<Header />
<Details>
  <Info />
  <Photos />
  <Badges />
</Details>
{
  restaurant (id: 6) {
    name,
    description,
    pictures,
    opening_hours,
    cuisine,
    ...
  }
}

Creating containers

class Header extends React.Component {
  render () {
    return (
      <h1>{this.props.restaurant.name}</h1>
    )
  }
}

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Creating containers

const Header = props => (
  <h1>{props.restaurant.name}</h1>
)

export default Relay.createContainer(Header, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        name
      }
    `
  }
})

Fragments

{
  restaurant (id: 6) {
    name,
    description,
    pictures,
    opening_hours,
    cuisine,
    ...
  }
}
fragment on Restaurant {
  name
}
fragment on Restaurant {
  description
}
fragment on Restaurant {
  pictures
}
fragment on Restaurant {
  opening_hours,
  cuisine
}

Fragments

fragment on Restaurant {
  name
}
fragment on Restaurant {
  description
}
fragment on Restaurant {
  pictures
}
fragment on Restaurant {
  opening_hours,
  cuisine
}
<Header />
<Details>
  <Info />
  <Photos />
  <Badges />
</Details>

Fragments

const Details = props => (
  <div>
    <Info />
    <Photos />
    <Badges />
  </div>
)
export default Relay.createContainer(Details, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        ${Info.getFragment('restaurant')},
        ${Photos.getFragment('restaurant')},                
        ${Badges.getFragment('restaurant')},
      }
    `})

Fragments

const Details = props => (
  <div>
    <Info />
    <Photos />
    <Badges />
  </div>
)
export default Relay.createContainer(Details, {
  fragments: {
    restaurant: () => Relay.QL`
      fragment on Restaurant {
        ${Info.getFragment('restaurant')},
        ${Photos.getFragment('restaurant')},                
        ${Badges.getFragment('restaurant')},
      }
    `})

Thoughts on Relay

Unstable API (e.g., issue #112)

Hard to set up (routers, Babel plugin, scripts)

Hard to debug

It's a framework, not a library

Evolution of Flux pattern

Isomorphic apps with isomorphic-relay

Works on React Native out of the box

Resources

Questions?

Thanks!

José Fresco

@jfresco82

engineering.restorando.com

github.com/restorando

graphql-relay

By José Fresco

graphql-relay

  • 904