Adding GraphQL into Existing Express APIs

By Zach Silveira

Last night I had an idea. I was tired of continuing development on an express REST api that I had been building out for quite some time, so I added Apollo into my existing api. From this point forward, all new features will be done using GraphQL while the existing REST workflow will continue to be used. Turns out, a lot of people want to know how to do this:

Getting so many likes and replies is what motivated me to make a quick guide on how to do this as quickly as possible.

Why

GraphQL is a superior way of building out APIs, with types for your queries and mutations, you can auto generate types for Flow or Typescript, and have a completely documented, interactive (GrapiQL), api without any extra work. For so many reasons, we've jumped head first into 100% GraphQL on all of our future projects. Like anyone who's been doing applications for the past few years, some of our apps are somewhat legacy. Thankfully, we can add the latest and greatest Apollo server into any of them, lets do this!

Our Existing App

const express = require('express');
const app = express();
const jwt = require('express-jwt');
const routes = require('./src/routes');
const bodyParser = require('body-parser');

app.use(bodyParser.json());

//in a routes file:
app.get('/data', async (req, res) => {
    let data = await Data.query()
      .limit(15);

    res.send(data);
  });

In this case, our existing application looks like any Express app you might be working on yourself. We have a body parser, use jwt for auth, and have a bunch of routes registered.

First Step

Inside the existing app, make a file called graphql.js and install the neccesary apollo packages referenced.

const schema = require('./resolvers');
const bodyParser = require('body-parser');
const { graphqlExpress, graphiqlExpress } = require('apollo-server-express');


module.exports = app => {
  app.use(
    '/graphql',
    bodyParser.json(),
    graphqlExpress(request => ({ schema, context: request.user }))
  );
  app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }));
};

All we are doing is registering graphql and its interactive web viewer like the official documentation tells us. Now, inside your express index, add the following two lines:

const graphql = require('./graphql');
graphql(app);

Pass the app instance of express before calling app.listen.

Add our resolvers

Let's add our root schema file, I created ./resolvers/index.js for this:

const users = require('./users');
const data = require('./data');

const { makeExecutableSchema, mergeSchemas } = require('graphql-tools');

module.exports = mergeSchemas({
  schemas: [
    makeExecutableSchema(users),
    makeExecutableSchema(data)
  ],
});

Our users and data modules will export typeDefs and resolvers. You can read more about makeExecutableSchema on the official docs. You may also be wondering why I use mergeSchemas here. It allows us to define root types in multiple places and merge them into one type. In the past, we had to define

type Query {
  users: [User]
}

In the root schema file. This meant user types would be in resolvers/users/index and the root types would be somewhere else. This has helped with bringing on more developers and not having to wonder if there's some other place holding more types for our api. Now let's make ./resolvers/users/index.js:

const typeDefs = `
  type Query {
    #Get the users in the company, must be an admin
    users: [User] 
  }

  type User {
    id: Int!
    name: String!
    email: String!
  }
`;

const resolvers = {
  Query: {
    users: require('./queries/users'),
  },
};

module.exports = { typeDefs, resolvers };

You can see once again that we colocate all types related to the resolver in the same place. This is why mergeSchemas is awesome. In each resolver I can define the root Query type and what this resolver section will expose. Now if we have a look at queries/users.js:

const User = require('../../models/user');

module.exports = (_, args, user) => {
  return User.query();
};

At this point, we can fire up the express server, open the interactive editer at /graphiql and see the users query in the docs! We've just added a GraphQL endpoint to our existing api with minimal work.

Conclusion

./server/src
├── graphql.js
├── models
│   └── user.js
├── resolvers
│   ├── index.js
│   └── users
│       ├── index.js
│       ├── mutations
│       │   └── user.js
│       └── queries
│           └── users.js

After setting this up, I feel like the above directory structure is a very nice way to go about expanding your api. If you wish to see any user related types, you know resolvers/users has all the types, even the root ones. The structure allows for many queries and mutations on each of your data models with minimal confusion. A new developer could reason about your code in a few minutes of instruction / fiddling with GraphiQL.

Is there something I could have done better? Let me know. At Encryption, code quality and ease of development are two of our most important goals. Apollo and GraphQL have been keys to our success over the past year. I look forward to all of the amazing features the Apollo team continues to add into the project!

As always, tweet @encryptioninc for any feedback or questions.