loading...

How Apollo Saves Us Effort in Jump Starting a GraphQL Server

iwilsonq profile image Ian Wilson ・3 min read

How Apollo Saves Us Effort in Jump Starting a GraphQL Server

A couple years ago I spun up my first Node HTTP server with Express. It took only 5 lines of code on my end.

const express = require('express')
const app = express()

app.get('/', function(req, res) {
    res.send({ hello: 'there' })
})

app.listen(3000, () => 'Listening at http://localhost:3000')

This reduced the necessary effort for building server side apps greatly, especially considering we could use our familiar JavaScript!

I must say, a year or two ago, setting up a GraphQL server wasn’t this easy. There was a decision to be made in choosing to build it with Facebook’s Relay or Apollo. Both frameworks have their own opinions about how a GraphQL server should be built and consumed.

In the beginning, starting off with Apollo was a little simpler than Relay, but it still demanded a lot of configuration. There were many GraphQL internals that needed to be exported and used in order to write fairly basic queries.

Now its different — The Apollo ecosystem has developed tremendously since a couple years ago. Setting up a server has become so mind-bogglingly simple, we can do it in almost as few lines as the above example:

import express from 'express'
import { ApolloServer } from 'apollo-server-express'
import { resolvers, typeDefs } from './schema'

const PORT = process.env.PORT || 3500
const app = express()

const server = new ApolloServer({
  typeDefs,
  resolvers,
  playground: true
})

server.applyMiddleware({ app })

app.listen(PORT, () =>
  console.log(`Listening at http://localhost:${PORT}/graphql`)
)

We just define our type definitions and resolver functions in our schema and we are ready to roll. This would be similar to setting up route handlers for every operation we wanted to support, like GET /users/:id or POST /articles.

Here is an example of some type definitions:

export const typeDefs = gql`
  type User {
    id: ID
    name: String
    age: Int
    email: String
    friends: [User]
  }

  type Query {
    users: [User]
  }

  input CreateUserInput {
    id: Int
    name: String
    age: Int
    email: String
    friends: [Int]
  }

  type Mutation {
    createUser(input: CreateUserInput!): User
    updateUser(id: Int!, input: CreateUserInput!): User
    deleteUser(id: Int!): User
  }
`

So rather than define CRUD endpoints to gather and manipulate our users, we just declare the shape of the data and the relations between them.

In order to receive a query or mutation and make calls to the database, we define the resolvers with access to a data model like so:

export const resolvers = {
  Query: {
    users(source, args, context) {
      return userModel.list()
    }
  },
  User: {
    friends(source, args, context) {
      if (!source.friends || !source.friends.length) {
        return
      }

      return Promise.all(source.friends.map(({ id }) => userModel.find(id)))
    }
  },
  Mutation: {
    createUser(source, args, context) {
      return userModel.create(args.input)
    },
    updateUser(source, args, context) {
      return userModel.update(args.id, args.input)
    },
    deleteUser(source, args, context) {
      return userModel.delete(args.id)
    }
  }
}

The model handles the database queries or third-party api calls. This way we can keep the resolvers agnostic to where the data is coming from.

Apollo Server, Internally

Even a few months ago, there were more packages to deal with and more configuration to grok before you could truly do anything.

Defining resolvers took much more effort with setting up internal GraphQL types.

Defining and patching together the schema also took more creativity on the developers side. Apollo basically looked at all of this configuration “busywork” and abstracted it into a package that is easier to get started with— without sacrificing the configuration that you might still want.

Here, we have simply initialized a server instance and applied our express server as a middleware.

server.applyMiddleware({ app })

What happens when we pass our Express instance to Apollo Server?

Usually, when we set up an Express server we need to install

  • body-parser to read JSON payloads
  • CORS to deal with cross origin resource sharing
  • multer or some other multipart middleware to read file uploads

It winds up being a bunch of app.use(someMiddleware) commands inside our server. Apollo Server comes with all of that by default, which is why we hardly need to write any extra code or install more dependencies.

Coming Soon

I wrote another post that deals with setting up a GraphQL server with Apollo. Hopefully it’ll drop soon, but the example repo [is here] in case you’d like to check out the source code that will accompany it.

Working with Apollo and GraphQL has certainly improved my dev experience and made me more productive, hopefully you will find it the same!

If you enjoyed this article and would like to see more in the future, let me know in the comments and give me a follow on Twitter and Medium!

Posted on Aug 1 '18 by:

iwilsonq profile

Ian Wilson

@iwilsonq

I mostly just code or run. Aside from programming I'm generally training for a marathon.

Discussion

markdown guide