GraphQL is not only for Backend

When we hear about GraphQL, it’s often mentioned as the new way to retrieve data from the server. The silver bullet that will replace REST APIs. This is true, GraphQL can do an amazing job to run the new generation of server APIs but this is not the whole picture.

From the homepage of the official website, GraphQL is described as “A query language for your API” where you:

  • First, describe your data
  • Then, ask for what you want
  • Finally, obtain predictable results

We quickly make the assumption that it allows us to query server APIs using HTTP requests but if we take a deeper look at the official JavaScript implementation, we understand that this is not totally true, GraphQL is a query language for any Data Provider whether it’s local or remote.

Many Tools have been developed to ease the use of GraphQL server-side: From Relay, Apollo Data Toolsexpress-graphql etc… but few people focused on using it on the client. While you’ll probably get the most of it, if it’s used server-side, using GraphQL on the client can be valuable as well and often, using GraphQL on the server requires a big investment where you probably have to rewrite your entire application to make the transition.

A typical React/Redux application

In the React Ecosystem, a large number of applications are using Redux to store their data in a global state tree. How can we use GraphQL to improve the architecture of such applications without rewriting our application from scratch?

So first, let’s try to take a look at a typical React/Redux Application (A Todo Application of course). We’ll probably have:

  • A reducer that “stores” the data in the global state: todos reducer,
  • An action creator used to retrieve todos from the server: fetchTodos

In most cases, This action creator will rely on redux-thunk to trigger a network request and dispatch the action once the results retrieved.

  • A selector getTodos responsible of retrieving the todos from the global state

And to link all of these items together, we would use react-redux to bring our data and action creators to our component. A complete TodoList React Component could look like that:


import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchTodos } from './actions';
import { getTodos } from './selectors';
class TodoList extends Component {
componentWillMount() {
this.props.fetchTodos();
}
render() {
const { todos } = this.props;
return (
<div>
{ todos.map( todo =>
<Todo key={ todo.id } text={ todo.text } done={ todo.done } />
) }
</div>
);
}
}
const TodoListContainer = connect(
state => {
return {
todos: getTodos( state );
};
},
{ fetchTodos }
)( TodoList );
export default TodoListContainer;

view raw

todos.jsx

hosted with ❤ by GitHub

Declarative data fetching

Let’s take a deeper look at this component. It has three responsibilities:

  • It triggers an action creator to fetch Data
  • It uses a selector to retrieve Data from the redux tree
  • And it renders the Data

React’s main purpose is providing a declarative API, to express how your data get transformed into DOM elements, but in our example above, the fetching and data retrieval is imperative. This means it’s the responsibility of the persons who write the component to trigger the data fetching requests and eventually handles data refreshing. And in this area, Relay has been doing a great job to transform these imperative calls into a declarative API. What if we could provide a declarative API (similar to Relay) without the need of rewriting our APIs to use GraphQL? and without even using Relay?

We want our component to be written like this:


import React, { Component } from 'react';
import { query } from 'react-graphql-redux';
const TodoList = ( { data: { todos } } ) => {
return (
<div>
{ todos.map( todo =>
<Todo key={ todo.id } text={ todo.text } done={ todo.done } />
) }
</div>
);
};
export default query(`
{
todos {
id
text
done
}
)
`)( TodoList );

view raw

todos.jsx

hosted with ❤ by GitHub

If we need more data such as the authenticated user, our query could be:


// … our component
export default query(
{
todos {
id
text
done
}
user {
isLoggedin
username
}
`)( TodoList );

view raw

todos.jsx

hosted with ❤ by GitHub

Basically, we use a Higher Order Component called query to declare that our component needs a list of todos, and for each todo, we want its id, text, and done attributes. We don’t mind how this data is fetched from the server or retrieved from the redux store, we just declare what data the component needs and it will be made available as a prop.

How is this possible? 

Ok! this sounds awesome right! but isn’t this too difficult to achieve, how do we match each part of this query to its corresponding actions / selectors?

We need a tree resolution algorithm where we match each node of the tree to a function that fetches and retrieves this data. And you know!! this is exactly the purpose of GraphQL. A way to match a query to resolvers.

Ok! Let’s setup GraphQL to resolve the query above:


const createGraph = store => {
// A GraphQL schema (used to validate the data)
const schema = `
type Todo {
id: Int,
text: String,
done: Boolean
}
type Query {
todos: [Todo]
}
`;
// The resolver of our todos tree node
const todosResolver = () => {
// Trigger the fetch action creator
const state = store.getState();
const todos = getTodos( state );
if ( ! todos ) store.dispatch( fetchTodos() );
// Return retrieved data from the store
return todos;
};
// The root of our GraphQL setup
const root = {
hello: () => 'hello world',
todos: todosResolver,
// Append any other todos here
};
return {
schema,
root
};
}

view raw

graph.js

hosted with ❤ by GitHub

The idea here is for every node of our query, we write:

1- A schema: A good side effect of using GraphQL is the schema which will be used to validate our data. This ensures a valid data is provided to all your React Components.

2- A resolver: A function with the following shape:


const myResolver = ( resolverArgs ) => {
// If the data is not present or is old, let's trigger a refresh
if ( shouldIRefreshTheData( resolverArgs ) ) {
store.dispatch( fetchData( resolverArgs ) );
}
// Retrieve the data from the state
const state = store.getState();
return myDataSelector( state );
}

view raw

resolver.js

hosted with ❤ by GitHub

Make our GraphQL resolvers available for the query HoC

Now that our GraphQL setup is ready, we can make it available on the React context, that way the query  HoC can use it to provide data to the wrapped components. This is the exact same technique used by react-redux to make the store available for connected components.


import { render } from 'react-dom';
import { GraphProvider } from 'react-graphql-redux';
import App from './app';
// create your redux store
const store = // …
// define your GraphQL schema and root (see above)
const { schema, root } = createGraph( store );
// render your app wrapped in the GraphProvider
render(
<GraphProvider store={ store } schema={ schema } root={ root }>
<App />
</GraphProvider>
, document.getElementById('root')
);

view raw

index.jsx

hosted with ❤ by GitHub

Now, you are ready to go! wrap your components with query HoC.

Is this worth it?

If you’re working on a small application with dozen of actions and selectors, probably not. Just stick with the imperative fetching, but If your application is big enough, I would say, this is certainly worth a try.

Good benefits

I already mentioned the fact that using GraphQL allows you to validate your data, you are certain that the data provided to your components would always be valid against the defined schema.

Another good side effect is the possibility to use GraphiQL. If you’re not familiar with it yet, it’s a web interface which allows you to test your GraphQL queries before using them in your components. Also, it allows you to navigate through your data which increases data discoverability a lot. You won’t lose your time looking or rewriting existing redux selectors, if the data is already available, you’ll just find it in GraphiQL.

Library

You can find the query HoC and the GraphProvider  in this library, but it’s easy enough to write on your own if you prefer to.

Who is using this approach already?

Me:). I don’t know if anyone else is but we’re considering using it for Calypso, the new WordPress.com front-end. Here is the PR

Time to get your hands dirty! and let me know if you have any thoughts or concerns on this 🙂


4 responses to “GraphQL is not only for Backend”

    • We don’t have a precise time frame for this. The PR is in place, though, since it’s an architecture change, it will need more reviews to be shipped. Hopefully it will land in some weeks.

  1. VERY interesting article and idea. I’ll be giving react-graphql-redux a go this weekend.
    You really should push forward with the project – it seems like it has tons of potential.
    react-redux does not offer the loveliest DX out there.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Discover more from Riad Benguella

Subscribe now to keep reading and get access to the full archive.

Continue reading