KEMBAR78
GraphQL, Redux, and React | PPTX
Graph QL
Problems with REST API
GET id -------------------> data
GET id2 -------------------> more data
GET data.id ------------------> even more data
Multiple Round Trips
● In order to descent into the object graph, multiple
round trips to the server are required.
● Critical for Mobile Environment
Over fetching
Unless specifically
designed,
you might receive needless
information
Compatibility and Versioning
http://api.myservice.com/v1/users
http://api.myservice.com/v2/users
http://api.myservice.com/v3/users
.
.
Backend APIs
are storage-centric
Front-end APIs
are product-centric
Graph Application Layer Query Language
(not Storage Query Language)
“A better solution that can replace restful server and mvc framework”
Basically JSON without the values
No custom endpoints
Clients ask exactly what they want
person:{ name:, address: }
Graph QL
● User-defined type system - “schema”
● Server describes what data is available
● Clients describe what data they require
● User-defined data fetching (“how do I get an article”)
● Front end request independent to backend
How Graph QL
Works
GraphQL has a type system
Three main “types” required to get an
endpoint up and running.
1. A type for the model
2. A type for the query
3. A type for the schema
In reality, it is more complex than this.
(forget about it for now)
Our Data
var goldbergs = {
1: {
character: "Beverly Goldberg",
actor: "Wendi McLendon-Covey",
role: "matriarch",
traits: "embarrassing, overprotective",
id: 1
},
2: {
character: "Murray Goldberg",
actor: "Jeff Garlin",
role: "patriarch",
traits: "gruff, lazy",
id: 2
},
3: {
character: "Erica Goldberg",
actor: "Hayley Orrantia",
role: "oldest child",
traits: "rebellious, nonchalant",
id: 3
},
4: {
character: "Barry Goldberg",
actor: "Troy Gentile",
role: "middle child",
traits: "dim-witted, untalented",
id: 4
},
5: {
character: "Adam Goldberg",
actor: "Sean Giambrone",
role: "youngest child",
traits: "geeky, pop-culture obsessed",
id: 5
},
6: {
character: "Albert 'Pops' Solomon",
actor: "George Segal",
role: "grandfather",
traits: "goofy, laid back",
id: 6
}
}
Model Type
Model Type is pretty much a mirror image
of each Goldberg in our goldbergs data
--------->
var goldbergType = new GraphQLObjectType({
name: "Goldberg",
description: "Member of The Goldbergs",
fields: {
character: {
type: GraphQLString,
description: "Name of the character",
},
actor: {
type: GraphQLString,
description: "Actor playing the character",
},
role: {
type: GraphQLString,
description: "Family role"
},
traits: {
type: GraphQLString,
description: "Traits this Goldberg is known for"
},
id: {
type: GraphQLInt,
description: "ID of this Goldberg"
}
}
});
1. Create a new instance of GraphQLObjectType, “Goldberg”
2. Under “fields”: Each “type” indicates the type expected
(e.g. string (GraphQLString) for character, int (GraphInt) for id.)
3. “description” fields are good for self-documentation
Query Type
The query type tells how we will query
our data
var queryType = new GraphQLObjectType({
name: "query",
description: "Goldberg query",
fields: {
goldberg: {
type: goldbergType,
args: {
id: {
type: GraphQLInt
}
},
resolve: function(_, args){
return getGoldberg(args.id)
}
}
}
});
function getGoldberg(id) {
return goldbergs[id]
}
1. Query Type is an instance of GraphQLObjectType, it just serves
a different purpose.
2. Create a new query field called goldberg and set its “type” to the
goldbergType we created earlier. Our new goldberg field will accept
an id as an assignment.
3. When we resolve our query we return the output of a function
getGoldberg()
Schema Type
Schema type combines them all together.
/* creating schema instance */
var schema = new GraphQLSchema({
query: queryType
});
/* serve our schema */
var graphQLServer = express();
graphQLServer.use('/', graphqlHTTP({ schema: schema,
graphiql: true }));
graphQLServer.listen(8080);
console.log("The GraphQL Server is running.")
{
goldberg ( id : 2 ) {
id,
character
}
}
{
“data” : {
“goldberg” : {
“id” : 2,
“character” : “Murray Goldberg”
}
}
Let’s Test
Go to GraphiQL IDE and test it yourself.
(GraphiQL IDE is just like postman of RESTful API for GraphQL)
React and
Redux
Let’s delete the “hello world” from
static/index.html and add a new message
using React in index.js:
| -- app
| -- actions
| -- components
| -- reducers
In the “reducers” folder we’ll create a new
file called reducer.js where we’ll work on
our reducer function.
import React from "react";
import ReactDOM from "react-dom";
const Main = React.createClass({
render: () => {
return (
<div>
<p>hello react!</p>
</div>
)
}
});
ReactDOM.render(
<Main />,
document.getElementById("example")
);
React and
Redux
We’ll be using the Immutable module for
our state so that we can form some good
habits.
Our state has two fields — one to let us
know if we are in the middle of a
query/awaiting a response from the server
and another that contains the response
data.
Next we plug our immutableState into a
reducer function:
import Immutable from "immutable";
const immutableState = Immutable.Map({
fetching: false,
data: Immutable.Map({})
})
--------------------------------------------------
export const queryReducer = (state = immutableState,
action) => {
switch (action.type) {
case "STARTING_REQUEST":
return state.set("fetching", true);
case "FINISHED_REQUEST":
return state.set("fetching", false)
.set("data",
Immutable.Map(action.response.data.goldberg));
default:
return state
}
}
Store
Back in index.js we want to create a store
out of our reducer and feed it to our main
component.
We’ll need to import the reducer we just
created along with some helpers from
redux and react-redux.
We also need the redux-thunk middleware
to help us later on when we need to make
some requests.
import React from "react";
import ReactDOM from "react-dom";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import { queryReducer } from
"./app/reducers/reducers.js";
import thunkMiddleware from "redux-thunk";
/* First, we apply the redux-thunk middleware:*/
const createStoreWithMiddleware = applyMiddleware(
thunkMiddleware
)(createStore)
/* Then we wrap our Main component in the Redux
Provider and pass our queryReducer into
createStoreWithMiddleware.*/
ReactDOM.render(
<Provider
store={createStoreWithMiddleware(queryReducer)}>
<Main />
</Provider>,
document.getElementById("example")
);
Actions
In the “actions” folder we’ll create a new
file called actions.js.
We need to create two actions to dispatch
to our reducer,
one for “STARTING_REQUEST”
and one for “FINISHED_REQUEST”:
const startingRequest = () => {
return {
type: "STARTING_REQUEST"
}
}
const finishedRequest = (response) => {
return {
type: "FINISHED_REQUEST",
response: response
}
}
Actions
The great thing about the redux-thunk
middleware we applied to our store earlier
is that when an action returns a function
that function is injected with dispatch().
We’ll get to use dispatch() twice in a new
action called getGraph:
When getGraph() is called we dispatch
startingRequest() to indicate the start of a
new query. We then begin the async
request (note the “application/graphql”
content type in the header) and when our
query is complete we dispatch
finishedRequest() with the results of our
query.
export const getGraph = (payload) => {
return dispatch => {
dispatch(startingRequest());
return new Promise(function(resolve, reject) {
let request=new XMLHttpRequest();
request.open("POST", "/graphql", true);
request.setRequestHeader("Content-Type",
"application/graphql");
request.send(payload);
request.onreadystatechange = () => {
if (request.readyState === 4) {
resolve(request.responseText)
}
}
}).then(response =>
dispatch(finishedRequest(JSON.parse(response))))
}
}
Component
For now we’ll create an empty Query
component:
We need to hook up our component with
our store and the dispatch method by
creating a container component and using
the react-redux connect() helper.
import React from ‘react’;
import { connect } from ‘react-redux’;
import { getGraph } from ‘../actions/actions.js’;
let Query = React.createClass({
render() {
return (
<div>
</div>
)
}
});
const mapStateToProps = (state) => {
return {
store: state
}
};
export const QueryContainer = connect(
mapStateToProps
)(Query);
let Query = React.createClass({
componentDidMount() {
this.props.dispatch(
getGraph("{goldberg(id: 2) {id, character, actor}}")
);
}
});
Component
We’ll also add the elements that our
response data will fill and a button to
submit additional queries. We want to
know if we are in the middle of a query so
we’ll grab our fetching field from our state
and display it on the page.
let Query = React.createClass({
componentDidMount() {
this.props.dispatch(
getGraph("{goldberg(id: 2) {id, character, actor}}")
);
},
render() {
let dispatch = this.props.dispatch;
let fetchInProgress =
String(this.props.store.get('fetching'));
let queryText;
let goldberg = this.props.store.get('data').toObject();
return (
<div>
<p>Fetch in progress: {fetchInProgress}</p>
<h3>{ goldberg.character }</h3>
<p>{ goldberg.actor }</p>
<p>{ goldberg.role }</p>
<p>{ goldberg.traits }</p>
<input ref={node => {queryText = node}}></input>
<button onClick={() => {
dispatch(getGraph(queryText.value))}
}>
query
</button>
</div>
)
}
});
Component
With that done, the last thing we have to
do is plug our QueryContainer component
into our Main component.
In index.js:
import { QueryContainer } from “./app/components/Query.js”;
// And replace “hello react!”
const Main = () => {
return (
<div>
<QueryContainer />
</div>
)
};
● https://learngraphql.com/basics/introduction
● https://medium.com/@thisbejim/getting-started-with-redux-and-graphql-
8384b3b25c56#.x08ia9y7m
Resources used in this
Presentation
Go to GraphiQL IDE and test it yourself.
(GraphiQL IDE is just like postman of RESTful API for GraphQL)

GraphQL, Redux, and React

  • 1.
  • 2.
    Problems with RESTAPI GET id -------------------> data GET id2 -------------------> more data GET data.id ------------------> even more data
  • 3.
    Multiple Round Trips ●In order to descent into the object graph, multiple round trips to the server are required. ● Critical for Mobile Environment
  • 4.
    Over fetching Unless specifically designed, youmight receive needless information
  • 5.
  • 6.
  • 7.
    Graph Application LayerQuery Language (not Storage Query Language) “A better solution that can replace restful server and mvc framework”
  • 8.
    Basically JSON withoutthe values No custom endpoints Clients ask exactly what they want person:{ name:, address: } Graph QL
  • 9.
    ● User-defined typesystem - “schema” ● Server describes what data is available ● Clients describe what data they require ● User-defined data fetching (“how do I get an article”) ● Front end request independent to backend How Graph QL Works
  • 10.
    GraphQL has atype system Three main “types” required to get an endpoint up and running. 1. A type for the model 2. A type for the query 3. A type for the schema In reality, it is more complex than this. (forget about it for now)
  • 11.
    Our Data var goldbergs= { 1: { character: "Beverly Goldberg", actor: "Wendi McLendon-Covey", role: "matriarch", traits: "embarrassing, overprotective", id: 1 }, 2: { character: "Murray Goldberg", actor: "Jeff Garlin", role: "patriarch", traits: "gruff, lazy", id: 2 }, 3: { character: "Erica Goldberg", actor: "Hayley Orrantia", role: "oldest child", traits: "rebellious, nonchalant", id: 3 }, 4: { character: "Barry Goldberg", actor: "Troy Gentile", role: "middle child", traits: "dim-witted, untalented", id: 4 }, 5: { character: "Adam Goldberg", actor: "Sean Giambrone", role: "youngest child", traits: "geeky, pop-culture obsessed", id: 5 }, 6: { character: "Albert 'Pops' Solomon", actor: "George Segal", role: "grandfather", traits: "goofy, laid back", id: 6 } }
  • 12.
    Model Type Model Typeis pretty much a mirror image of each Goldberg in our goldbergs data ---------> var goldbergType = new GraphQLObjectType({ name: "Goldberg", description: "Member of The Goldbergs", fields: { character: { type: GraphQLString, description: "Name of the character", }, actor: { type: GraphQLString, description: "Actor playing the character", }, role: { type: GraphQLString, description: "Family role" }, traits: { type: GraphQLString, description: "Traits this Goldberg is known for" }, id: { type: GraphQLInt, description: "ID of this Goldberg" } } }); 1. Create a new instance of GraphQLObjectType, “Goldberg” 2. Under “fields”: Each “type” indicates the type expected (e.g. string (GraphQLString) for character, int (GraphInt) for id.) 3. “description” fields are good for self-documentation
  • 13.
    Query Type The querytype tells how we will query our data var queryType = new GraphQLObjectType({ name: "query", description: "Goldberg query", fields: { goldberg: { type: goldbergType, args: { id: { type: GraphQLInt } }, resolve: function(_, args){ return getGoldberg(args.id) } } } }); function getGoldberg(id) { return goldbergs[id] } 1. Query Type is an instance of GraphQLObjectType, it just serves a different purpose. 2. Create a new query field called goldberg and set its “type” to the goldbergType we created earlier. Our new goldberg field will accept an id as an assignment. 3. When we resolve our query we return the output of a function getGoldberg()
  • 14.
    Schema Type Schema typecombines them all together. /* creating schema instance */ var schema = new GraphQLSchema({ query: queryType }); /* serve our schema */ var graphQLServer = express(); graphQLServer.use('/', graphqlHTTP({ schema: schema, graphiql: true })); graphQLServer.listen(8080); console.log("The GraphQL Server is running.")
  • 15.
    { goldberg ( id: 2 ) { id, character } } { “data” : { “goldberg” : { “id” : 2, “character” : “Murray Goldberg” } } Let’s Test Go to GraphiQL IDE and test it yourself. (GraphiQL IDE is just like postman of RESTful API for GraphQL)
  • 16.
    React and Redux Let’s deletethe “hello world” from static/index.html and add a new message using React in index.js: | -- app | -- actions | -- components | -- reducers In the “reducers” folder we’ll create a new file called reducer.js where we’ll work on our reducer function. import React from "react"; import ReactDOM from "react-dom"; const Main = React.createClass({ render: () => { return ( <div> <p>hello react!</p> </div> ) } }); ReactDOM.render( <Main />, document.getElementById("example") );
  • 17.
    React and Redux We’ll beusing the Immutable module for our state so that we can form some good habits. Our state has two fields — one to let us know if we are in the middle of a query/awaiting a response from the server and another that contains the response data. Next we plug our immutableState into a reducer function: import Immutable from "immutable"; const immutableState = Immutable.Map({ fetching: false, data: Immutable.Map({}) }) -------------------------------------------------- export const queryReducer = (state = immutableState, action) => { switch (action.type) { case "STARTING_REQUEST": return state.set("fetching", true); case "FINISHED_REQUEST": return state.set("fetching", false) .set("data", Immutable.Map(action.response.data.goldberg)); default: return state } }
  • 18.
    Store Back in index.jswe want to create a store out of our reducer and feed it to our main component. We’ll need to import the reducer we just created along with some helpers from redux and react-redux. We also need the redux-thunk middleware to help us later on when we need to make some requests. import React from "react"; import ReactDOM from "react-dom"; import { createStore, applyMiddleware } from "redux"; import { Provider } from "react-redux"; import { queryReducer } from "./app/reducers/reducers.js"; import thunkMiddleware from "redux-thunk"; /* First, we apply the redux-thunk middleware:*/ const createStoreWithMiddleware = applyMiddleware( thunkMiddleware )(createStore) /* Then we wrap our Main component in the Redux Provider and pass our queryReducer into createStoreWithMiddleware.*/ ReactDOM.render( <Provider store={createStoreWithMiddleware(queryReducer)}> <Main /> </Provider>, document.getElementById("example") );
  • 19.
    Actions In the “actions”folder we’ll create a new file called actions.js. We need to create two actions to dispatch to our reducer, one for “STARTING_REQUEST” and one for “FINISHED_REQUEST”: const startingRequest = () => { return { type: "STARTING_REQUEST" } } const finishedRequest = (response) => { return { type: "FINISHED_REQUEST", response: response } }
  • 20.
    Actions The great thingabout the redux-thunk middleware we applied to our store earlier is that when an action returns a function that function is injected with dispatch(). We’ll get to use dispatch() twice in a new action called getGraph: When getGraph() is called we dispatch startingRequest() to indicate the start of a new query. We then begin the async request (note the “application/graphql” content type in the header) and when our query is complete we dispatch finishedRequest() with the results of our query. export const getGraph = (payload) => { return dispatch => { dispatch(startingRequest()); return new Promise(function(resolve, reject) { let request=new XMLHttpRequest(); request.open("POST", "/graphql", true); request.setRequestHeader("Content-Type", "application/graphql"); request.send(payload); request.onreadystatechange = () => { if (request.readyState === 4) { resolve(request.responseText) } } }).then(response => dispatch(finishedRequest(JSON.parse(response)))) } }
  • 21.
    Component For now we’llcreate an empty Query component: We need to hook up our component with our store and the dispatch method by creating a container component and using the react-redux connect() helper. import React from ‘react’; import { connect } from ‘react-redux’; import { getGraph } from ‘../actions/actions.js’; let Query = React.createClass({ render() { return ( <div> </div> ) } }); const mapStateToProps = (state) => { return { store: state } }; export const QueryContainer = connect( mapStateToProps )(Query); let Query = React.createClass({ componentDidMount() { this.props.dispatch( getGraph("{goldberg(id: 2) {id, character, actor}}") ); } });
  • 22.
    Component We’ll also addthe elements that our response data will fill and a button to submit additional queries. We want to know if we are in the middle of a query so we’ll grab our fetching field from our state and display it on the page. let Query = React.createClass({ componentDidMount() { this.props.dispatch( getGraph("{goldberg(id: 2) {id, character, actor}}") ); }, render() { let dispatch = this.props.dispatch; let fetchInProgress = String(this.props.store.get('fetching')); let queryText; let goldberg = this.props.store.get('data').toObject(); return ( <div> <p>Fetch in progress: {fetchInProgress}</p> <h3>{ goldberg.character }</h3> <p>{ goldberg.actor }</p> <p>{ goldberg.role }</p> <p>{ goldberg.traits }</p> <input ref={node => {queryText = node}}></input> <button onClick={() => { dispatch(getGraph(queryText.value))} }> query </button> </div> ) } });
  • 23.
    Component With that done,the last thing we have to do is plug our QueryContainer component into our Main component. In index.js: import { QueryContainer } from “./app/components/Query.js”; // And replace “hello react!” const Main = () => { return ( <div> <QueryContainer /> </div> ) };
  • 24.
    ● https://learngraphql.com/basics/introduction ● https://medium.com/@thisbejim/getting-started-with-redux-and-graphql- 8384b3b25c56#.x08ia9y7m Resourcesused in this Presentation Go to GraphiQL IDE and test it yourself. (GraphiQL IDE is just like postman of RESTful API for GraphQL)