You don’t need a fancy framework to use GraphQL with React

All you need is a good naming strategy

Samer Buna
Mar 14, 2017 · 12 min read
  • Efficiency. For example, asking servers for only the new data required and then merging new data with old data, or asking servers for only the data used in the visible window and asking for the rest of the data after that.
  • Dealing with failure. For example, employing a standard error handling for failing requests and a standard retry strategy for them.
  • Responsiveness. For example, showing expected data changes optimistically while waiting on a confirmation from the server. Then possibly performing a rollback on those optimistic changes if the server fails to persist the data.
  • Cache management. For example, deciding what to cache, where to cache it, and when to expire something in the cache.
git clone https://github.com/jscomplete/react-blog-examplecd react-blog-examplenpm installnpm run nodemon
npm run webpack

Creating seed data in a GraphQL API

To build a GraphQL server schema that matches the data used by this React application, we’ll use GraphFront.com, which is a free GraphQL backend as a service where we can define data models and get a ready-to-use GraphQL API for them. This will save us some time as this article is not about creating a GraphQL API but rather using one.

  • An Article has the following fields: title as String, date as Date, body as String, authorId as a relation field on Author.
Creating an Author model
Creating an Article model
mutation CreateAuthor($apiKey: String!, $authorInput: AuthorInput!) {
createAuthor(apiKey: $apiKey, input: $authorInput) {
id
}
}
{
"apiKey": "YOUR_API_KEY_HERE",
"authorInput": {
"firstName": "The React",
"lastName": "Team",
"website": "ttps://twitter.com/reactjs"
}
}
mutation CreateArticle($apiKey: String!, $articleInput: ArticleInput!) {
createArticle(apiKey: $apiKey, input: $articleInput) {
id
}
}
{
"apiKey": "YOUR_API_KEY_HERE",
"articleInput": {
"title": "React 16.0 is released",
"date": "10/10/2017",
"authorId": "USE_AUTHOR_ID_FROM_PREVIOUS_MUTATION",
"body": "Lorem ipsum dolor sit amet...."
}
}

Analyzing React components for data requirements

The first step is to understand the React application components hierarchy.

App
ArticleList
ArticleRow
Article
Author
NewArticleForm
  • Every child of the root component will translate into a query or a mutation.
  • Every child after that will translate into a fragment.
  1. A currentArticle which is on object
// In src/components/App.jsApp.GraphQL = `
query GetArticleList($apiKey: String!) {
viewer(apiKey: $apiKey) {
data: articles {
...ArticleListFragment
}
}
}
query GetArticle($apiKey: String!, $articleId: String!) {
viewer(apiKey: $apiKey) {
data: findArticle(id: $articleId) {
...ArticleFragment
}
}
}
${ArticleList.GraphQL}
${Article.GraphQL}
`;
  • We’ve used two top fields from the GraphFront API, articles and findArcticle(id: String!). Those fields map to our need to fetch an array of articles and a single article object. GraphFront requires wrapping all queries in a viewer object that passes the apiKey.
  • The App component does not know anything beyond the type of the two data elements it uses. The details of what data to fetch in the list of articles are managed by the ArticleList component and the details of what data to fetch for the single current article are managed by he Article component. That’s why we used fragments for these details.
  • The fragments used in the queries above are for the direct children of the App component. This will be always the case. A component can only use the data requirements of its children components.
  • We’ve included the GraphQL static properties of all the App’s children components inside App.GraphQL. We will do this in every parent component.
// In src/components/ArticleList.jsArticleList.GraphQL = `
fragment ArticleListFragment on Article {
id
...ArticleRowFragment
}
${ArticleRow.GraphQL}
`;
// In src/components/ArticleRow.js ArticleRow.GraphQL = `
fragment ArticleRowFragment on Article {
id
title
date
}
`;
// In src/components/Article.jsArticle.GraphQL = `
fragment ArticleFragment on Article {
title
date
body
author {
...AuthorFragment
}
}
${Author.GraphQL}
`;
// In src/components/Author.jsAuthor.GraphQL = `
fragment AuthorFragment on Author {
firstName
lastName
website
}
`;
App.GraphQL is now a complete GraphQL document
import axios from 'axios';const GraphQLEndPoint = 'YOUR_GRAPHFRONT_API_ENDPOINT';
const GraphQLApiKey = 'YOUR_API_KEY';
import App from './components/App';export const sendOperation = (operationName, variables={}) => {
return axios.post(GraphQLEndPoint, {
query: App.GraphQL,
variables: {
...variables,
apiKey: GraphQLApiKey,
},
operationName,
}).then(resp => {
const GraphQLData = resp.data.data;
return GraphQLData.viewer.data;
});
};
// In src/components/App.js
// In the componentDidMount function
// Replace:
api.getArticleList()
// With:
api.sendOperation('GetArticleList')

// In the setCurrentArticle function
// Replace:
api.getArticle(articleId)
// With:
api.sendOperation('GetArticle', { articleId })

Adding Mutations

Adding mutations is no different when it comes to using the GraphQL document. However, the process of preparing data for a mutation is mostly an imperative one.

mutation FindOrCreateAuthor(
$apiKey: String!,
$input: AuthorOptionalInput!
) {
mutationData: findOrCreateAuthor(
apiKey: $apiKey,
input: $input,
findFields: ["firstName", "lastName", "website"],
) {
id
}
}
mutation CreateArticle(
$apiKey: String!,
$input: ArticleInput!
) {
mutationData: createArticle(
apiKey: $apiKey,
input: $input
) {
id
...ArticleFragment
}
}
// Change sendOperation in src/api.js// The new data modifier handler function's body:const GraphQLData = resp.data.data;if (GraphQLData.viewer) {
return GraphQLData.viewer.data;
}
return GraphQLData.mutationData;
// Replace addArticle in src/components/App.jsaddArticle = (userInput) => {
const { author: authorInput, ...articleInput } = userInput;
api.sendOperation('FindOrCreateAuthor', {
input: authorInput
}).then(author => {
articleInput.authorId = author.id;
articleInput.date = new Date();
return api.sendOperation('CreateArticle', {
input: articleInput
});
}).then(newArticle => {
this.setState((prevState) => ({
data: {
articles: [...prevState.data.articles, newArticle],
currentArticle: newArticle,
},
newArticleForm: false,
}));
});
};
  • We then send the FindOrCreateAuthor operation with just the author input. This will return an object that has an id attribute.
  • We use the id attribute as the authorId input for the next operation. We add the current date on that input.
  • We send the CreateArticle operation with the prepared input. This operation will return the new article object which we then just need to make React aware of.
samerbuna.com

jsComplete EdgeCoders

We write about the new and leading edge technologies with a focus on JavaScript

Samer Buna

Written by

Author for Pluralsight, O'Reilly, Manning, and LinkedIn Learning. Curator of jsComplete.com

jsComplete EdgeCoders

We write about the new and leading edge technologies with a focus on JavaScript