This is How to Build a GraphQL API with Redis (and Apollo Server)

In this post, we will build a GraphQL API with Redis on Node.js using Apollo Server.

What you'll need:

  1. I am using Debian 9 Stretch (but this should work with your distro);
  2. Your favorite text editor (I am using Sublime Text 3)

These are all the steps (incase you want to skip ahead).

  1. Install Node.js
  2. Build Node.js Server
  3. Install Apollo Server
  4. Build GraphQL API with Apollo Server
  5. Install Redis
  6. Setup GraphQL with Redis

And here is the graphql redis apollo server repo if you want to bootstrap or fork this project.

Let's get on with it.

Installing Node.js

Start by updating the distro (make sure curl package is installed).

        
$ sudo apt update && sudo apt upgrade
$ sudo apt install curl wget
        
      

Then get and run the latest 8.x branch of Node.js from nodesource.

        
$ cd ~
$ curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
        
      

You will be prompted to run install nodejs.

You can either install nodejs with npm package manager:

        
$ sudo apt-get install -y nodejs
        
      

Or install nodejs with yarn package manager:

        
$ curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
$ echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
$ sudo apt-get update && sudo apt-get install yarn
        
      

Once installation finishes, test nodejs and npm or yarn version (I am using yarn):

This should return the versions now installed.

        
$ node -v && yarn -v # or npm -v
v9.11.1
1.7.0
        
      

Building the Node.js Server

First we initialize the server, as with any Node.js project. Here, I am calling the project graphql-redis-server.

Go to the directory you want to create your server. Create your project folder.

        
$ cd apps
$ mkdir graphql-redis-server && cd graphql-redis-server
        
      

Follow the prompts to initialize your project with a package.json file.

        
$ yarn init
yarn init v1.7.0
question name (graphql-redis-server): graphql-redis-server
question version (1.0.0):
question description: GraphQL Redis Apollo Server
question entry point (index.js): server.js
question repository url: https://github.com/sproutera/graphql-redis-server.git
question author: solo@sproutera.com
question license (MIT): MIT
question private:
success Saved package.json
Done in 123.09s.
        
      

Note that I have called my entry point as server.js, you can leave the default.

You can look at the package.json file created inside your project folder.

        
$ tree
.
└── package.json

0 directories, 1 file
$ cat package.json
{
  "name": "graphql-redis-server",
  "version": "1.0.0",
  "description": "GraphQL Redis Apollo Server",
  "main": "server.js",
  "repository": "https://github.com/sproutera/graphql-redis-server.git",
  "author": "solo@sproutera.com",
  "license": "MIT"
}

        
      

We can now install nodemon as a dev dependency. This will automatically restart our server after every change, instead of us doing it manually.

.
        
$ yarn add --dev nodemon
        
      

This also installs all the other dependencies in our package.json

Installing Babel.js with babel-cli

Next we need to install Babel.js so that we can use ES2017+ syntax and still get browser compatible javascript.

Start with installing babel-cli as a development dependency.

        
$ yarn add --dev babel-cli babel-preset-env babel-preset-stage-3
        
      

babel-cli by itself doesn't do much, it needs plugins to do almost all of its work. Here we are adding two plugins: babel-preset-env and babel-preset-stage-3.

Passed without configurations, babel-preset-env is exactly the same as the deprecated babel-preset-latest (or babel-preset-es2015, babel-preset-es2016, and babel-preset-es2017 together).

You can configure it to only include the polyfills and transforms needed for the browsers you want to support. Compiling only what's needed can makes your life easier.

We will include polyfills and code transforms needed for our current node version. We are targeting node.

Create a new .babelrc file at the root of you project.

        
$ touch .babelrc
        
      

Add the following presets.

        
{
  "presets": [
    ["env", {
      "targets": {
        "node": "9.11.1"
      }
    }]
  ]
}
        
      

After setting the presets, we can now add a start script for our server in our package.json using nodemon and babel-node. babel-cli also added babel-node in our project for us.

Open package.json at your project root and add the following the start script right after main:

        
...

"main": "server.js",
"scripts": {
  "start": "nodemon --exec babel-node server.js"
},

...
        
      

We still cannot start our Node.js server yet. We need a server.js for that.

Installing Apollo Server

Apollo is a suite of tools to create GraphQL servers to consume GraphQL APIs. The tools provided by Apollo are three; Apollo Client, Apollo Server and Apollo Engine.

Apollo Client consumes your API from the frontend, usually through frameworks such as Vue.js and React.

Apollo Client helps you connect a GraphQL schema to an HTTP server in Node. It sits at your backend as part of your GraphQL server interfacing the GraphQL backend with clients, usually through Apollo Client.

Apollo Engine is a hosted SaaS using in production GraphQL server for caching, performance reporting, load measurement and error tracking amongst many other features.

Although they work together at various levels, each of these tools can be used independently. And they are all compatible with the GraphQL standard Specification.

What About express-graphql?

Well. Apollo Server and express-graphql do almost the same thing. Both libraries are middlewares for Express.

Apollo now recommends Apollo Server instead of express-graphql for your latest projects. Switching your project from express-graphql to Apollo Server should be trivial.

Apollo Server can be used with many server, we want to use it with Express.

To build our GraphQL API with Apollo Server in the next section, we will start with their express quick start example.

To install apollo-server-express and graphql-tools, first add the following packages:

        
$ yarn add apollo-server-express graphql-tools graphql express body-parser
        
      

Building the GraphQL API with Apollo Server

Create the server.js file at the root your project and add apollo server express.

        
import express from 'express';
import bodyParser from 'body-parser';
import { graphqlExpress } from 'apollo-server-express';

const myGraphQLSchema = // ... define or import your schema here!
const PORT = 4000;

const app = express();

// bodyParser is needed just for POST.
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema }));

app.listen(PORT);
        
      

Note that I have changed to port:4000, because my clients usually run on port:3000. You can use any open port you want.

Generating GraphQL Schema

This is where we will need graphql-tools. To generate and use our schema, we will first describe it with in typeDefs with GraphQL language syntax, then add some resolver functions, then combine and use them with makeExecutableSchema.

Describing GraphQL schema

Add the following in your server.js, after the imports.

        
import typeDefs from './schema';
        
      

Create a schema.js file in your project root.

        
$ touch schema.js
        
      

And add the following query inside it.

        
export default `

  type Query {
    hello: String
  }

`;
        
      

We need some resolvers before we can test this.

Adding the Resolvers

Add the following in your server.js, after the imports.

        
import resolvers from './resolvers';
        
      

Create a resolvers.js file in your project root.

        
$ touch resolvers.js
        
      

And add the following a resolver for our hello Query as an object inside it.

        
export default {

  Query: {
    hello: () => 'Hello World!'
  }

}
        
      

Finally we can now use our schema.

Executing GraphQL Schema with makeExecutableSchema

To do this, we first need to import makeExecutableSchema from graphql-tools

        
import { makeExecutableSchema } from "graphql-tools";
        
      

Now we can add our schema and resolvers to our server.js.

Add them just after the imports.

        
export const schema = makeExecutableSchema({
  typeDefs,
  resolvers
})
        
      

Delete the Remove myGraphQLSchema definition and also from the /graphql endpoint so it uses the imported schema.

        
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));
        
      

We can also add a graphiql API endpoint to test out our Graph API.

Add graphiqlExpress to the apollo-server-express import as follows:

        
import { graphiqlExpress, graphqlExpress } from "apollo-server-express";
        
      

The we add the following endpoint /graphiql after the /graphql endpoint (just before app.listen).

        
...

app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))

app.listen ...
        
      

Here's how my server.js looks.

        
$ cat server.js
import express from 'express';
import bodyParser from 'body-parser';
import { graphiqlExpress, graphqlExpress } from 'apollo-server-express';
import typeDefs from './schema';
import resolvers from './resolvers';
import { makeExecutableSchema } from "graphql-tools";

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

const PORT = 4000;

const app = express();

// bodyParser is needed just for POST.
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }));

app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))
app.listen(PORT);
        
      

Now, start the server:

        
$ yarn start
        
      

If the server complains it cannot get graphql-tag, just install the dependency directly.

Our GraphQL API should now be ready for testing.

        
$ yarn start
yarn run v1.7.0
$ nodemon --exec babel-node server.js
[nodemon] 1.17.5
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: *.*
[nodemon] starting `babel-node server.js`
        
      

Open localhost:4000/graphiql.

If everything went well, you should see the Graphiql IDE pop up.

Here's mine:

Testing the GraphQL API

Now we can test our hello query.

Write our test query on the left of the IDE.

        
{
  hello
}
        
      

This should return a JSON object to the right of the IDE:

        
{
  "data": {
    "hello": "Hello World!"
  }
}
        
      

Perfect! Exactly what we expected.

We've build our first graph query:

        
http://localhost:4000/graphiql?query=query%20%7B%0A%20%20hello%0A%7D
        
      

Installing Redis with redis-cli

Redis is an open source high-performance in-memory data structure store than can be used as a database, cache or message broker.

Redis is most loved database by developers worldwide, meaning that most developers want to continue working with it than any other database. As a cache, Redis is better and more efficient than memcached.

Redis comes with a simple command line tool redis-cli that provides a simple command-line interface to a Redis server.

In GraphQL, Redis is mostly used for the data persistent layer and with graphql subscriptions.

Let's install Redis.

First we make sure we have the necessary build tools.

        
$ sudo apt install build-essential tcl -y
        
      

Installing Redis from the aptitude Repo

This code be done by running the following command:

        
$ cd ~
$ sudo apt install redis-server -y
        
      

Alternatively, we can build Redis from source.

Building and Installing Redis from Source

First we need to download the latest source from redis.io, then we extract and test it to make use we are ready to compile.

        
$ cd ~
$ wget http://download.redis.io/redis-stable.tar.gz
$ tar -xvzf redis-stable.tar.gz
$ cd redis-stable
$ make test
        
      

If all tests passed without errors, we can compile and install the build.

        
$ make
$ sudo make install
        
      

Once install finishes you can test your install with redis-cli.

        
$ cd ~
$ sudo systemctl status redis
● redis-server.service - Advanced key-value store
   Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled)
   Active: active (running) since Tue 2018-06-12 15:56:31 EAT; 18h ago
     Docs: http://redis.io/documentation,
           man:redis-server(1)
  Process: 754 ExecStartPost=/bin/run-parts --verbose /etc/redis/redis-server.post-up.d (code=exited, status=0/SUCCESS)
  Process: 740 ExecStart=/usr/bin/redis-server /etc/redis/redis.conf (code=exited, status=0/SUCCESS)
  Process: 721 ExecStartPre=/bin/run-parts --verbose /etc/redis/redis-server.pre-up.d (code=exited, status=0/SUCCESS)
 Main PID: 753 (redis-server)
    Tasks: 3 (limit: 4915)
   Memory: 2.6M
      CPU: 1min 49.593s
   CGroup: /system.slice/redis-server.service
           └─753 /usr/bin/redis-server 127.0.0.1:6379

...
$ redis-cli
127.0.0.1:6379>
        
      

For Redis, the default port is port:6379. If you want to change this port, or hit an error, you might need to explore more configuration options, such as creating a user and /or the systemd config to get it working.

Redis is now properly setup.

We can return to our project root and restart our server.

        
$ cd  ~/apps/graphql-redis-server
$ yarn start
        
      

Setting Up GraphQL with Redis using ioredis

The next thing we need to do is setup Redis in our Graphql-Apollo Server.

If you are migrating from node_redis, you might be interest in this wiki.

Node Redis (redis) vs IO Redis (ioredis)

You are probably wondering, "Why not node_redis?"

These are the two popular redis clients for Node.js.

IO Redis is often perceived as a major improvement over node_redis (e.g. support for sentinel and cluster (completely missing in node_redis) and much better pubsub and Lua support).

The two libraries might be getting consolidated soon!

We will start by installing the ioredis Node.js client.

        
$ yarn add ioredis
        
      

Then jump to our server.js to include it. Then we connect to Redis to start using it.

        
import redis from "ioredis";

...

const redis = new Redis()

...


app.use('/graphql', bodyParser.json(), graphqlExpress({ schema, context: { redis } }));

...
        
      

With ioredis, when a new Redis instance is created, a connection is established at the same time.

After creating a connection, we are passing our const redis to the resolvers using with context so that we can access Redis methods in our schema.

Testing the GraphQL with Redis API

To test our API, we can start by creating a mutation in our schema.

        
export default `

type Query {
  get(key: String!): String
}

type Mutation {
  set(key: String!, value:  String!): Boolean!
}


`;
        
      

For our test, we revise your Query to get a value from a key.

We also want a mutation to return a boolean i.e. whether or not we set the non-nullable key and value correctly.

We also make the same changes to our resolvers.

        
export default {

  Query: {
    get: (parent, {key}, {redis}) => {
      try {
        return redis.get(key)
      } catch (error) {
        return null
      }
    }
  },

  Mutation: {
    set: async (parent, {key, value}, {redis}) => {
      try {
        await redis.set(key, value)
        return true
      } catch (error) {
        console.log(error)
        return false
      }
    }
  }
}
        
      

The arguments in each resolver are spelt out when being passed in but can just as easily be passed with the args. Same with the context.

The Mutation is asynchronious. In ioredis we don't have to use extra libraries to make this work asynchroniously, you might recall using bluebird with node_redis.

We are now ready to test our graphql-redis-server API.

Make sure your Redis server is up and running.

Open or reload http:localhost:4000/graphiql.

You can first try to get a random key which should return a null.

And then try to set a key and value with the mutation. Which should return a Boolean according to our schema

Finish things off with another query to get the name we just added above with the key.

Perfect! I am getting my name!

In a follow-up post, I might take you through deploying this GraphQL with Redis API using Apollo Engine.

The graphql-redis-server repo is here!