Getting started with Redux and GraphQL

When GraphQL was announced it was clear to everyone that it was going to be awesome. The community waited patiently to sink their teeth into it and finally the technical preview was released in all its glory.

Chances are that you, like me, found this technical preview far more difficult to comprehend than you anticipated. This was probably compounded by the complexity of Relay.

I feel your pain. My brain melted and I told myself I would return to give it another go. And I did! This time I focused solely on GraphQL and kept everything else as simple as possible.

Sharing is caring

This tutorial serves as a quick setup for anyone who wants to muck around with GraphQL from a Redux app. We’ll reduce complexity right down and then you can take it from there.

We’ll be using Redux instead of Relay (if you aren’t familiar with Redux checkout the legendary Egghead series), es5 on the server rather than es6/babel-node, and all our GraphQL goodies will be as simple as possible.

Let’s set up our project.

The project

Create a new folder (I’ll call mine graphql-app).

We’ll need a package.json file, npm can help us with that.

npm init

We also need to install some modules for our server: graphql-js, express-graphql, express, webpack, and webpack-dev-server.

npm install --save graphql express express-graphql webpack webpack-dev-server 

Again, we want to keep complexity right down so we’ll be writing our server code in es5 rather than es6 to avoiding transpiling.

Create a server.js file and we’ll import the modules we just installed.

var webpack = require(‘webpack’);
var WebpackDevServer = require(‘webpack-dev-server’);
var express = require(‘express’);
var graphqlHTTP = require(‘express-graphql’);
var graphql = require(‘graphql’);
var GraphQLSchema = graphql.GraphQLSchema;
var GraphQLObjectType = graphql.GraphQLObjectType;
var GraphQLString = graphql.GraphQLString;
var GraphQLInt = graphql.GraphQLInt;

You can see that we are also defining variables for some of GraphQL’s types, which we will use in a moment.

We are going to create some data for GraphQL to retrieve. For no other reason than it being hilarious we’ll use the characters from The Goldbergs for our data.

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

GraphQL

GraphQL is comprised of a type system and for simplicity’s sake — this is the mental model I used to understand it — we’ll say there are 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 far more complex than this but this will do for the purpose of this tutorial.

Model Type

We are going to build a “model type” which 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"
}
}
});

We create a new instance of GraphQLObjectType and name it “Goldberg”.

Under “fields”: Each “type” indicates the type expected, e.g. string (GraphQLString) for character, int (GraphQLInt) for id.

You might also have noticed the “description” fields — GraphQL is awesome for self documentation. We’ll be able to see this in action when we use the interactive GraphiQL IDE that ships with express-graphql.

Query Type

The “query type” specifies how we’ll 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)
}
}
}
});

The “query type” is still an instance of GraphQLObjectType, it just serves a different purpose.

We create a new query field called goldberg and set its “type” to the goldbergType we created earlier. Under args you can see that our new goldberg field will accept an id as an argument.

When we resolve our query we return the output of a function called getGoldberg():

function getGoldberg(id) {
return goldbergs[id]
}

Here we use the id from the query to return a Goldberg from our data.

Schema type

Finally, the “shema type” brings it all together.

var schema = new GraphQLSchema({
query: queryType
});

Serving the shema

We can use express and the graphqlHTTP middleware to serve our shema.

var graphQLServer = express();
graphQLServer.use('/', graphqlHTTP({ schema: schema, graphiql: true }));
graphQLServer.listen(8080);
console.log("The GraphQL Server is running.")

With graphiql set to true we can easily test our work:

node server

Got to http://localhost:8080/ and we’ll see the GraphiQL IDE work its magic.

If we issue this query:

{ 
goldberg(id: 2) {
id,
character
}
}

We should get back the following:

{
"data": {
"goldberg": {
"id": 2,
"character": "Murray Goldberg"
}
}
}

Make a few different queries, it’s fun!

Note: In the top right hand corner of the screen there is a button labeled “Docs”. If we click that we can read the documentation we added earlier in the “description” fields. Go ahead and explore the documentation!

Serving the app

To serve the front end of our app we need to install babel, babel-loader, and a couple of babel presets.

npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 babel-preset-react

Create a new file called .babelrc, this will tell babel what presets we want to use:

{
"presets": ["es2015", "stage-0", "react"]
}

Create a new index.js file, we can leave it blank for now.

Create a new folder called “static” and in that folder we’ll add an index.html file with the following:

<div id="example"></div>
<script src="/static/bundle.js"></script>
<h3>hello world</h3>

At this point our project structure should look like this:

graphql-app
| -- index.js
| -- server.js
| -- package.json
| -- .babelrc
| -- static
| -- index.hml

In server.js we need to configure webpack to use babel when bundling our project.

Underneath graphQLServer.listen(8080):

var compiler = webpack({
entry: "./index.js",
output: {
path: __dirname,
filename: "bundle.js",
publicPath: "/static/"
},
module: {
loaders: [
{ test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
}
});

Webpack will take index.js and write a es6 transpiled version to /static/bundle.js.

Next we create a new WebpackDevServer to serve our bundled project:

var app = new WebpackDevServer(compiler, {
contentBase: "/public/",
proxy: {"/graphql": `http://localhost:${8080}`},
publicPath: "/static/",
stats: {colors: true}
});
app.use("/", express.static("static"));
app.listen(3000);
console.log("The App Server is running.")

The proxy field adds our existing GraphQL server to our app server so that we can query it from within our app without running into any CORS issues.

Let’s start it all up:

node server

Head to http://localhost:3000/, we should see the message “hello world”.

Head to http://localhost:3000/graphql and we should see the GraphiQL IDE again.

React and Redux

To add React and Redux to our app we’ll need a few extra modules: React, Redux, React-Redux, Redux-Thunk, and Immutable.

npm install --save react react-dom redux react-redux redux-thunk immutable

Note: Because we’ve setup webpack with babel we can write our frontend in es6, woohoo!

Let’s delete the “hello world” from static/index.html and add a new message using React in index.js:

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")
);

Start the server again, head back to http://localhost:3000/ and we should see our message.

Reducer

Let’s add a new folder to our project called “app” with a couple of subfolders.

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

We’ll be using the Immutable module for our state so that we can form some good habits.

import Immutable from "immutable";
const immutableState = Immutable.Map({
fetching: false,
data: Immutable.Map({})
})

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:

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

When the “STARTING_REQUEST” action is dispatched the “fetching” state is set true.

When the “FINISHED_REQUEST” action is dispatched the “fetching” state is set false and the “data” state is set to our response data.

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")
);

Success, our store is created!

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

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:

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))))
}
}

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.

Component

In the “components” folder we’ll create a new file called Query.js.

We need to import react, some helpers from react-redux, and the getGraph function we just created.

import React from ‘react’;
import { connect } from ‘react-redux’;
import { getGraph } from ‘../actions/actions.js’;

For now we’ll create an empty Query component:

let Query = React.createClass({
render() {
return (
<div>
</div>
)
}
});

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.

const mapStateToProps = (state) => {
return {
store: state
}
};
export const QueryContainer = connect(
mapStateToProps
)(Query);

In our Query component we’ll need to access the componentDidMount lifecycle method so that we can fetch our data as our component mounts.

let Query = React.createClass({
componentDidMount() {
this.props.dispatch(
getGraph("{goldberg(id: 2) {id, character, actor}}")
);
}
});

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>
)
}
});

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>
)
};

We’re done! Now when we run our app we can make GraphQL queries to our hearts content. Try the following query and see what you get: {goldberg(id: 4) {id, character, actor, traits}}

Thanks

Thanks for reading, I hope you found this useful. You can check out the source code here. Now go build something impressive with Redux and GraphQL!

Additional thanks to Dan Abramov for pointing out a mistake I made in this tutorial.

Resources

http://graphql.org/
https://github.com/facebook/graphql
https://github.com/graphql/graphql-js
https://github.com/graphql/graphql-relay-js
https://facebook.github.io/relay/
https://github.com/relayjs/relay-starter-kit
http://redux.js.org/
https://egghead.io/series/getting-started-with-redux
https://facebook.github.io/immutable-js/