GraphQL — The What, Why, and How

Daniel Varela
Wiser Tech
Published in
10 min readNov 24, 2018

This past week I had the pleasure of attending the GraphQL Summit and I gained some great insights from developers who have been using it in production over the past few years. This post will cover the What, Why, and How of GraphQL. By the end of this blog, you should know how to connect GraphQL between your frontend and backend. As a bonus, we will be using Apollo Engine to see realtime metrics and and explore our schemas! If you don’t care about the background and want to go straight to coding, skip all the way down to The How.

The What

GraphQL is a query language for your APIs. Much like MongoDB and Postgres are implementations of a database specification, GraphQL is a specification of a query language on which implementations such as the Apollo Client and Relay are built on. It is important to note that GraphQL is not a database — it’s simply a strongly-typed query language which at its very core allows developers the flexibility to cherry-pick the data they need without receiving any extraneous data from the backend.

The Why

Story time. Let’s roll the clock back to the year 2000. Roy Fielding is attending the University of California in Irvine for his doctorate degree and he proposes a resource-oriented architecture for progressing through web resources. This idea would lead to the creation of Representational State Transfer or REST. You can thank Roy for all those GET, POST, PUT, and DELETE aka CRUD requests you make on a daily basis.

Now let’s roll the clock forward to the year 2012. Facebook is an absolute unit and processes millions of REST API calls on a daily basis through their mobile app alone. Their apps are seeing noticeable slowdown and at peak hours their servers begin to crash.

The problem is caused by over-fetching — the act of getting more data out of your RESTful requests than you really need. The problem is compounded by multiple round-trip requests to different resources to build the UI.

Real life historical event of a front-end developer wanting to know the color of a fish and making a GET request for a /fish/:id resource.

The problem is sometimes you just want to know the color of a fish, but you get the whole fish along with the ocean.

The engineering team at Facebook gets together and decides they need a better way to fetch the data they need. They need to tackle two big problems — creating a data layer solution that reduces the amount of data getting transferred over HTTP and keeping developers happy (a monumental task). Despite the hurdles, their hard work pays off and a specification by the name of GraphQL is born.

So what advantages do you get with using GraphQL?

  1. Performance: In a ReSTful architecture, you typically need to make multiple API calls to multiple end points to aggregate data and then filter it down to a normalized, consumable shape for the frontend. With GraphQL you only need to make one query which reduces data transfer over HTTP.
  2. Cleaner APIs: You no longer need hundreds of endpoints. Everything goes through a single endpoint, typically named graphql by convention. This means a cleaner codebase! As a plus, you also get a playground where you can make requests much like Postman does for CRUD.
  3. Communication: You are now enforcing good communication between frontend and backend developers by creating clear and well defined schemas that both teams can agree upon.

Protip from Github’s “lessons learned” — build schemas with a domain specific mindset. Building schemas with a UI-centric mindset may yield poor results and make schema maintenance difficult (YMMV).

  1. Maintainability: With GraphQL there is no v1, v2, v3 of your APIs. If you use the Apollo Engine, you get a progressive API out of the box with CI/CD checks to make sure your changes don’t break your codebase. Also, strongly typed schemas are a requirement, so bugs related to schema mismatch shouldn’t make it to your production environment.

The How

Let’s build a simple full stack application using React, Node, and the Apollo Client. We’ll build a simple set of tables that query outdoor Trails and snowboarding Lifts. The finished product is here.

File structure:

root/
-- api/
-- ui/

Let’s start with the API.

// In your terminal, install all dependencies
npm init -y
npm i -S apollo-engine apollo-server-express express graphql nodemon

Great, now head over to this repo and copy the two JSON files into an api/data folder. These will serve as our “database”.

Create an index.js file and import all the required packages.

// index.js
const
{ ApolloServer, PubSub } = require('apollo-server-express')
const { ApolloEngine } = require('apollo-engine')
const express = require('express')

const lifts = require('./data/lifts.json')
const trails = require('./data/trails.json')

const pubsub = new PubSub()

// an object to be shared by all resolvers
const
context = { lifts, trails, pubsub }

Now let’s add our type definitions below the context object. These will be our schemas. The ! means you will always receive a non-nullable response. We will also add some Queries and Mutations to give us something to play with.

// Take note that some schema fields for Lift and Trail are 
// non-nullable
const
typeDefs = `
type Lift {
id: ID!
name: String!
status: LiftStatus!
capacity: Int!
night: Boolean!
elevationGain: Int!
trailAccess: [Trail!]!
}

type Trail {
id: ID!
name: String!
status: TrailStatus
difficulty: String!
groomed: Boolean!
trees: Boolean!
night: Boolean!
accessedByLifts: [Lift!]!
}
// enum or enumeration types are values which are restricted to the // values provided.
enum LiftStatus {
OPEN
HOLD
CLOSED
}

enum TrailStatus {
OPEN
CLOSED
}
/*
*
GraphQL has queries and mutations. A query is synonymous with a
* GET request while a mutation is synonymous with POST, PUT, and
* DELETE
* */
type Query {
allLifts(status: LiftStatus): [Lift!]!
Lift(id: ID!): Lift!
liftCount(status: LiftStatus!): Int!
allTrails(status: TrailStatus): [Trail!]!
Trail(id: ID!): Trail!
trailCount(status: TrailStatus!): Int!
}

type Mutation {
setLiftStatus(id: ID!, status: LiftStatus!): Lift!
setTrailStatus(id: ID!, status: TrailStatus!): Trail!
}
`

Now it’s time to add our resolvers. It’s important to note that resolvers are just functions that are called by your queries. Each query should have a corresponding resolver.

/*
* query and mutation functions take in 4 arguments

* root, args, context, and info (here we only care about args and
* context which we are destructuring)
* */
const resolvers = {
Query: {
allLifts: (root, { status }, { lifts }) => {
if (!status) {
return lifts
} else {
return lifts.filter(lift => lift.status === status)
}
},
Lift: (root, { id }, { lifts }) => {
const selectedLift = lifts.filter(lift => id === lift.id)
return selectedLift[0]
},
liftCount: (root, { status }, { lifts }) => {
var i = 0
lifts.map(lift => {
lift.status === status ?
i++ :
null
})
return i
},
allTrails: (root, { status }, { trails }) => {
if (!status) {
return trails
} else {
return trails.filter(trail => trail.status === status)
}
},
Trail: (root, { id }, { trails }) => {
const selectedTrail = trails.filter(trail => id === trail.id)
return selectedTrail[0]
},
trailCount: (root, { status }, { trails }) => {
let i = 0
trails.map(trail => {
trail.status === status ?
i++ :
null
})
return i
}
},
Mutation: {
setLiftStatus: (root, { id, status }, { lifts, pubsub }) => {
var updatedLift = lifts.find(lift => id === lift.id)
updatedLift.status = status
pubsub.publish('lift-status-change', { liftStatusChange: updatedLift })
return updatedLift
},
setTrailStatus: (root, { id, status }, { trails, pubsub }) => {
var updatedTrail = trails.find(trail => id === trail.id)
updatedTrail.status = status
pubsub.publish('trail-status-change', { trailStatusChange: updatedTrail })
return updatedTrail
}
}
}

So far we have created our Schemas, Queries, and Mutations, but we haven’t created our server, so let’s do that now. Head over to engine.apollographql.com and create an account and get an API key. This will allow us to see metrics in realtime. You will need to create a service and you can call it whatever you want. Apollo will create a key that you should keep handy in the shape of service:nameOfService:hash.

With your API key in hand lets create the server below our resolvers.

const app = express() // our express server

const server = new ApolloServer({
typeDefs, // our schemas, queries, and mutations
resolvers, // our functions that map to typeDefs
context, // in essence, a global state
tracing: true, // performance monitoring
cacheControl: true // can be monitored by Apollo Engine
})
// wrap apollo server around the express server
server.applyMiddleware({ app })
const engine = new ApolloEngine({
// your api key goes here!
apiKey: 'service:nameOfService:hash'
})

const port = 4000
// start the engine!
engine.listen({
port,
expressApp: app,
graphqlPaths: ['/graphql'] // the endpoint(s) for your graph
}, () => console.log(`Listening on port ${port}!`))

Edit the package.json file with a start script so there is no need to keep restarting the backend: "start": "nodemon -e js,graphql,json"

Now start the server npm start. If everything goes right, you can navigate to localhost:4000/graphql and see a playground. Try running a query and see the response.

// should return some JSON
query getAllLifts {
allLifts {
name
status
night
capacity
id
}
}
Query on the left. The response on the right.

If you got a response, that means everything is working as expected, but we want to see some metrics when we make these requests.

In a new terminal window lets publish our schema.

// First lets install apollo globally with npm
npm i -g apollo
// and finally lets publish our schemas to the engine
apollo schema:publish — endpoint=http://localhost:4000/graphql — key=”YOUR_API_KEY”

Great! Now that we have our API and have it connected to the Apollo Engine, let’s make a simple UI.

Let’s make the UI

We’ll use create-react-app to bootstrap the UI. If you don’t have CRA installed yet, do so now. npm i -g create-react-app.

Now let’s create a basic UI by navigating into your UI folder and running: create-react-app ui. Navigate into the ui folder and run npm start to start the app. It should automatically navigate you to localhost:3000. We will also need to install the graphql node packages — graphql, apollo-boost and react-apollo. Let us install those now. In the terminal, under the ui folder run npm i apollo-boost graphql react-apollo. Here, the graphql dependency allows us to build type-schemas and let us query against them. We won’t be using the graphql dependency directly in the ui, we will use apollo-boost (which requires graphql as a peer dependency) to build the queries we want and react-apollo to execute the actual query within the context of ReactJS.

Lets start with the index.js file inside src/. Replace the contents with the following:

import React from 'react'
import { render } from 'react-dom'
import App from './App'
import ApolloClient from 'apollo-boost'
import { ApolloProvider } from 'react-apollo'
// react-apollo gives us access to the client which should point to // our api endpoint which we built earlier.
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql'
})
render(<ApolloProvider client={client}><App/></ApolloProvider>, document.getElementById('root'))

If you notice we created an instance of the ApolloClient and pass it as a prop to the ApolloProvider. This allows Apollo to access our backend API and execute some queries we are about to write.

Now, lets replace everything in your App.css file with this just to give it barebones styling:

table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}

td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}

tr:nth-child(even) {
background-color: #dddddd;
}

Delete everything in your App.js and import the required packages:

import React  from 'react';
import { Query } from 'react-apollo'
import { gql } from 'apollo-boost'

import './App.css'

The Query import from react-apollo is a React component that takes in a query prop. It should wrap around other components that need the data received from the query. gql is a function that takes in your queries. Lets add some queries!

// add some queries for retrieving all Lifts and all Trails
const
ALL_LIFTS_QUERY = gql`
query {
allLifts {
name
status
}
}
`

const ALL_TRAILS_QUERY = gql`
query {
allTrails {
name
status
}
}
`

Now we have everything we need, all that is left is to render some tables.

const App = () =>
<div>
<table>
<thead>
<tr>
<th> LIFTS </th>
</tr>
</thead>
<tbody>
<Query query={ALL_LIFTS_QUERY}>
{({ loading, data }) => !loading && data.allLifts.map(lift =>
<tr key={lift.name}>
<td key={lift.name}>{lift.name}: {lift.status}</td>
</tr>)}
</Query>
</tbody>
</table>
<table>
<thead>
<tr>
<th> TRAILS </th>
</tr>
</thead>
<tbody>
<Query key="trails" query={ALL_TRAILS_QUERY}>
{({ loading, data }) => !loading && data.allTrails.map(trail =>
<tr key={trail.name}>
<td key={trail.name}>{trail.name}: {trail.status}</td>
</tr>)}
</Query>
</tbody>
</table>
</div>

export default App

Take note how the Query component takes in our gql queries which will retrieve the data. All we are showing is the Lift and Trail name along with a status.

The UI.

If you can see the tables, that means you successfully created a GraphQL API that was accessed by your frontend. This also means the Apollo Engine should have captured the initial render. Navigate to your service at https://engine.apollographql.com and you should see your request on the graph.

It’s alive!

You can also run queries from your playground on localhost:4000 and those queries will show up as well.

Browse around and have fun coding!

--

--