Upgrading Express GraphQL Server from ApolloServer v1 to ApolloServer v2


In this article, we would be learning how to upgrade a GraphQL server built with apollo-server-express to ApolloServer v2.0.

ApolloServer v2 introduced some changes to writing GraphQL servers. This new API for building GraphQL servers simplifies the process and it’s less dependent on external libraries. In fact, you can now write your GraphQL server without needing to run a separate HTTP Server and you would be doing this with fewer lines of code.

Introducing the new apollo-server-express

Express developers that prefer using ApolloServer as a middleware in their server implementation, apollo-server-express v2 comes with some clean updates to your codebase.

from the ApolloServer v2 docs:

  • You no longer need to import body-parser to set up apollo-server-express.
  • You no longer need to import makeExecutableSchema from graphql-tools.
  • You no longer need to import graphqlExpress and graphiqlExpress from apollo-server-express.
  • You should pass in typeDefs and resolvers as parameters to an instance of Apollo Server.
  • If the server is only functioning as a GraphQL server, it’s no longer necessary to run your own HTTP server (like express).

Let’s explore a typical ApolloServer v1 implementation:

'use strict'
const express = require('express')
const bodyParser = require('body-parser')
const { graphqlExpress } = require('apollo-server-express')
const schema = require('./data/schema')
const jwt = require('express-jwt')
require('dotenv').config()
const PORT = 3000
// create our express app
const app = express()

const auth = jwt({
  secret: process.env.JWT_SECRET,
  credentialsRequired: false
})
// graphql endpoint
app.use(
  '/api',
  bodyParser.json(),
  auth,
  graphqlExpress(req => ({
    schema,
    context: {
      user: req.user
    }
  }))
)

app.listen(PORT, () => {
  console.log(`The server is running on http://localhost:${PORT}/api`)
})

source code from Chimezie Enyinnaya *here*

Firstly, Express 4.16+ comes with it’s own implementation of body-parser, so it’s “not needed” in server implementation. By the way, body-parser is used to make form-data available to req.body (learn more about how this works here). However, ApolloServer has the body-parser package in it’s core implementation, so you do not need to add it to your code.

Secondly, graphqlExpress was used here to access request headers which is how you typically construct GraphQL options in v1. ApolloServer comes with it’s own implementation, and essentially says “hey, use my constructor and create context BASED on the request”.

 //ApolloServer.js
createGraphQLServerOptions(req, res) {
  const _super = Object.create(null, {
    graphQLServerOptions: { get: () => super.graphQLServerOptions }
  });
  return __awaiter(this, void 0, void 0, function* () {
    return _super.graphQLServerOptions.call(this, { req, res });
  });
}

Next stop is the schema. The ApolloServer documentation says that:

You no longer need to import makeExecutableSchema from graphql-tools.

The Schema

'use strict'
const { makeExecutableSchema } = require('graphql-tools')
const resolvers = require('./resolvers')
// Define our schema using the GraphQL schema language
const typeDefs = `
  type User {
    id: Int!
    username: String!
    email: String!
  }
  type Query {
    me: User
  }
  type Mutation {
    signup (username: String!, email: String!, password: String!): User
    login (email: String!, password: String!): String
  }
`
module.exports = makeExecutableSchema({ typeDefs, resolvers })

Creating schema in apollo-server v1, required you import makeExecutableSchema from graphql-tools. ApolloServer API reference says that the graphql-tools library enables the creation and manipulation of GraphQL schema.
Once again, ApolloServer exports all the functions from graphql-tools.

//index.ts
export * from 'graphql-tools';

This means that you don’t need makeExecutableSchema as the ApolloServer constructor has it’s functionality. However, in more complicated cases, where makeExecutableSchema is required, you can find it in the apollo-server package.
If you’re not using graphql-tools for defining schemas, what is apollo-server-express v2 proposing we use? Say hello to gql (graphql-tag). The documentation for gql says that its “a JavaScript template literal tag that parses GraphQL query strings into the standard GraphQL AST.”

Finally, ApolloServer v2 comes with it’s own method for adding middleware to your servers.

//index.js
 applyMiddleware(_a) {
      var { app } = _a, rest = __rest(_a, ["app"]);
      app.use(this.getMiddleware(rest));
  }

This eliminates the need for graphqlExpress.

Let’s now rewrite the server with what we’ve learnt so far.

The schema

const { gql } = require('apollo-server-express')
// Define our schema using the GraphQL schema language
const typeDefs = gql`
  type User {
    id: Int!
    username: String!
    email: String!
  }
  type Query {
    me: User
  }
  type Mutation {
    signup (username: String!, email: String!, password: String!): User
    login (email: String!, password: String!): String
  }
`
module.exports =  typeDefs

The server

const express = require('express')
const { ApolloServer } = require('apollo-server-express');
const resolvers = require('./data/resolvers');
const typeDefs = require('./data/schema');
const jwt = require('express-jwt')
require('dotenv').config()
const PORT = 3000
// create our express app
const app = express()
const path = '/api'
const auth = jwt({
  secret: process.env.JWT_SECRET,
  credentialsRequired: false
})
app.use(path, auth);
server.applyMiddleware({
  app, path
})
// graphql endpoint
const server = new ApolloServer({
  typeDefs,
  resolvers,
  context: ({ req }) => {
    user: req.user
  }
})
app.listen(PORT, () =>
  console.log(`? Server ready at http://localhost:4000${server.graphqlPath}`)
)

Writing GraphQL Server without Express

ApolloServer v2 enables developers to quickly setup a graphql server without needing any http server framework.
Let’s implement the server above without express.
Install the apollo-server and graphql packages.
yarn add apollo-server graphql

const { ApolloServer } = require('apollo-server')
const jwt =  require('jsonwebtoken')
const typeDefs = require('./data/schema')
const resolvers = require('./data/resolvers')
require('dotenv').config()
const { JWT_SECRET } = process.env
const getUser = token => {
    try {
        if (token) {
            return jwt.verify(token, JWT_SECRET)
        }
        return null
    } catch (error) {
        return null
    }
}
const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req }) => {
        const token = req.get('Authorization') || ''
        return { user: getUser(token.replace('Bearer', ''))}
    },
    introspection: true,
    playground: true
})
server.listen({ port: process.env.PORT || 4000 }).then(({ url }) => {
    console.log(`? Server ready at ${url}`);
  });

Summary

ApolloServer v2.0 (currently 2.17.0) has simplified writing servers for GraphQL developers with these changes, and since it’s open-source we are expecting more improvements in the coming years.
Check the documentation for more information.
Source code for server without express


Share on social media

//