🌍 All
About us
Digitalization
News
Startups
Development
Design
2020 Tech Stack: GraphQL Apollo server with React.js
Wojciech Cichoradzki
May 12, 2020・7 min read
Table of Content
GraphQL vs Rest
GraphQL
Apollo GraphQL
React.js
Our verdict
Since 2000, RESTful principles have been the industry standard for building web APIs. While REST solved many problems that previous protocols could not, it has flaws. In modern applications, the data interconnects in complex relations which can create performance issues in product development. This is exactly what GraphQL aims at solving.
GraphQL vs Rest
Based on HTTP, REST is an easy way to communicate between the client application and the server. Data is accessible and modifiable via HTTP methods on specific endpoints. The application is thus decoupled from the server. Custom libraries are also no longer needed. The integration with different platforms is easier.
In a news website, each article has its title, text content, date, author, and some visual content like images or videos. It may also have user’s comments grouped into threads and links to other related stories. The growing complexity in the data relationship graph shows the limits of the traditional REST approach. Each resource has to be accessed separately, often in sequence since one resource is dependable on the other. GraphQL improves the performance.
GraphQL
In 2015, Facebook engineers introduced GraphQL as a brand-new solution for designing APIs. The core concept of GraphQL is to give the developers more granular control over the resources needed by providing a single ‘smart’ endpoint instead of having a number of different endpoints for each resource.
The building of GraphQL APIs is organized in terms of types and fields, not endpoints. Data retrieval methods are queries and any manipulation on the data is a mutation in GraphQL terminology. Since the demand in the industry for a solution like GraphQL was already high, it quickly gained popularity and support from developers.
GraphQL has become popular and offers implementations in all popular programming languages. It has already been adopted by large companies like Twitter, Yelp, The New York Times, Airbnb, and more.
The developers appreciate the robustness and ease of using GraphQL. The 2019 developer survey “State of Javascript” showed that almost 40% of Javascript developers had already tried GraphQL and would use it again. The trend is rising, and we will see more GraphQL adoption in the upcoming years.
Apollo GraphQL
Apollo is a leading GraphQL implementation that provides a set of tools and libraries that help developers build GraphQL applications. The typical Apollo server consists of a set of GraphQL schema definitions and their resolvers. To showcase the capabilities of the GraphQL, we will be building a simple application that encompasses the Apollo Node.js server and React.js client.
The application will be storing user’s read book reviews. Let’s start by creating the basic structure for the project and build our server.
$ mkdir app app/server app/client && cd app/server
$ yarn init
Next, we have to create a database with two tables: Authors and Books.
CREATE TABLE `authors` (
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(256) NOT NULL UNIQUE,
PRIMARY KEY (`id`)
);
CREATE TABLE `books` (
`id` INT NOT NULL AUTO_INCREMENT,
`author` INT NOT NULL,
`title` VARCHAR(512) NOT NULL,
`image` VARCHAR(512) NOT NULL,
`review` VARCHAR(2048) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (`author`)
REFERENCES `authors`(`id`)
ON UPDATE NO ACTION ON DELETE CASCADE
);
To communicate with the database, we will be using the knex query builder library. Let’s install the required dependencies and set up a basic server.
$ yarn add dotenv knex mysql2 apollo-server
$ touch index.js
Within index.js, we are going to create our schema definitions and resolvers. We will be using two types: Author and Book. Besides, our API will expose two queries for each type, as well as a single mutation that adds a new book.
require('dotenv').config();
const { ApolloServer, gql } = require('apollo-server');
const knex = require('knex')({
client: 'mysql2',
connection: {
host: process.env.DB_HOST || 'localhost',
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || '1234',
database: process.env.DB_NAME || 'books_database',
},
});
// The GraphQL schema
const typeDefs = gql`
type Author {
id: Int!
name: String!
}
type Book {
id: Int!
author: Author!
title: String!
image: String!
review: String!
}
type Query {
books: [Book!]
authors: [Author!]
}
type Mutation {
addBook(author: String!, title: String!, image: String!, review: String!): Book!
}
`;
// A map of functions which return data for the schema.
const resolvers = {
Query: {
books: async (parent, args, context, resolveInfo) => {
const result = await knex('books')
.leftJoin('authors', 'books.author', 'authors.id')
.select('*')
.options({ nestTables: true });
return result.map(({ books, authors }) => ({
...books,
author: authors,
}));
},
authors: async () => knex('authors').select('*'),
},
Mutation: {
addBook: async (parent, {
author: authorName, title, image, review,
}) => {
let author = await knex('authors').first().where({ name: authorName });
if (!author) {
const [authorId] = await knex('authors').insert({ name: authorName });
author = {
id: authorId,
name: authorName,
};
}
const newBook = {
author: author.id,
title,
image,
review,
};
const [bookId] = await knex('books').insert(newBook);
return {
...newBook,
id: bookId,
};
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Now, to start the server and test our API with the knex logs exposed, we have to run Node.js on our index.js file:
DEBUG=knex:query node index.js 🚀 Server ready at http://localhost:4000/
Next, if we open http://localhost:4000/, we are welcomed by what is called GraphQL playground. Here, we can easily test our API.
Let's attempt to retrieve all the books using the following command in the GraphQL playground:
query { books { title author } }
At this stage, you should see an empty array as a response, indicating that there are currently no books.
Since we currently don’t have any book stored, the response is just an empty array.
We can add books by executing the addBook mutation. For example, you can use the following command in the GraphQL playground:
mutation { addBook(title: "New Book", author: "John Doe") { title author } }
This will return the added book's title and author in the response.
When we query books again using the same command as before:
query { books { title author } }
we should see the newly created book in the response list.
Our "books" resolver is performing a left join on the ‘authors’ table to retrieve the id and name of the author and pass it to the response. However, when the author field is not requested, the expensive left join operation could be omitted. To achieve such optimization, we can use the graphql-parse-resolve-info package and refactor our resolver, so it checks whether the author field is present in the request.
const {
parseResolveInfo,
simplifyParsedResolveInfoFragmentWithType,
} = require('graphql-parse-resolve-info');
…
books: async (parent, args, context, resolveInfo) => {
const query = knex('books')
.select('*')
.options({ nestTables: true });
const simplifiedFragment = simplifyParsedResolveInfoFragmentWithType(
parseResolveInfo(resolveInfo),
resolveInfo.returnType,
);
if (simplifiedFragment.fields.author) {
query.leftJoin('authors', 'books.author', 'authors.id');
}
const result = await query;
return result.map(({ books, authors: author }) => ({
...books,
author,
}));
},
If we modify the query by removing the author field, like so:
query { books { title } }
we would observe a change in the SQL queries performed. This is because the query has become more specific and now only requests the titles of the books.
Our API is now ready to be used by our client application.
React.js
We initialize the React project by using create-react-app and installing the required dependencies:
cd .. && npx create-react-app client && cd client yarn add apollo-boost @apollo/react-hooks graphql
Now, when running yarn start, a basic starter project should open. Let’s create our GraphQL client and expose it to the sub-components via a context provider. We need to modify index.js like this:
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/react-hooks';
import ApolloClient from 'apollo-boost';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
const client = new ApolloClient({
uri: 'http://localhost:4000',
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);
As a consequence, we need to perform a query to our API and render a list of books. Apollo gives a set of useful React hooks that help us do that. In our case, we will use the useQuery hook with our previously used GraphQL books query attached. Let’s modify our App.js so it fetches and renders the stored books.
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const BOOK_QUERY = gql`
{
books{
id
author{
name
}
title
image
review
}
}
`;
export default function App() {
const { loading, error, data } = useQuery(BOOK_QUERY);
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Error :(</p>;
}
return (
<div className="App">
<h2>My Books</h2>
{ data.books.map(({ id, author, title, image, review }) => (
<div key={id}>
<img src={image} alt={title} />
<p>{ title }</p>
<p>{ author.name }</p>
<p>{ review }</p>
</div>
))}
</div>
);
}
Now, after the refresh, our app successfully renders a list of books. However, the styling is not too appealing. Let’s add the milligram.css library, extract the Book component to a separate module, and add a bit of CSS magic.
Index.html
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.css">
Book.js
export default function Book({ author, title, image, review }: Props) {
return (
<div className="book container">
<div className="row">
<div className="column-10">
<img
src={image}
alt="book cover"
className="cover"
/>
</div>
<div className="column-90">
<h4 className="title">{ title }</h4>
<i>{ author }</i>
<p>{ review }</p>
</div>
</div>
</div>
);
}
Index.css
.App {
margin: 2rem 20%;
}
.book .cover {
width: 100px;
height: auto;
margin-right: 2rem;
}
.book .title {
margin-bottom: 0;
font-weight: bold;
}
After these adjustments and improvements to our GraphQL queries, our data fetching capabilities are more efficient and our application is more responsive.
Our verdict
The simplicity and robustness of GraphQL, especially when combined with Apollo and React, is the best to use in an all-purpose 2020 tech stack. In the coming months, we expect to see a wider adoption and further optimizations by the growing community. Interested in learning more? Check out our blog for more articles on development tools.
Do you have any questions about product development? If so, head to our contact page or drop us a line at hello@start-up.house


You may also
like...

A Practical Guide to Choosing the Right BDD Framework for Your Needs
Choosing the right Behaviour-Driven Development (BDD) framework is key to enhancing collaboration and software quality. This guide explores popular frameworks, selection criteria, and tips for smooth adoption.
Alexander Stasiak
Mar 21, 2024・9 min read

Understanding the Distinct Roles: Scrum Master vs Product Owner
Scrum Master and Product Owner roles are integral to Agile projects but serve different purposes. This guide explains their distinct responsibilities, skills, and collaborative dynamics.
Marek Pałys
Dec 09, 2024・8 min read

Private vs Public Cloud: A Clear Guide to Making the Right Choice for Your Business
Discover the key differences between private and public clouds to make an informed choice for your business. This guide explains their benefits, cost implications, security, and performance to help you find the ideal cloud solution.
Marek Majdak
Sep 17, 2024・9 min read