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
- 905