April 1, 2016

The Apollo server

Jonas Helfer

Jonas Helfer

Last week on Building ApolloSashko Stubailo wrote about the client-side cache we’re building for GraphQL; this week I want to give you a short overview of what we’re going to do on the server side.

If you don’t know what GraphQL is, you might want to check out “Intro to GraphQL”.

The GraphQL reference implementation, graphql-js, is great: It contains pretty much everything you need to build your own GraphQL server in JavaScript. Unfortunately, just having all the parts doesn’t mean that building your own server is easy. GraphQL introduces so many new concepts that it’s easy to get lost and not know where to start. That’s where Apollo Server comes in.

Our goals for the project are simple:

  • Make building a GraphQL server simple and straightforward
  • Provide great documentation and guidance for building GraphQL servers
  • Provide great tooling: error messages, logging, profiling and more

How hard can it really be?

If you’ve never tried to build a GraphQL server yourself, you’re probably wondering: how hard could it really be? Let me give you an example for how you would define a schema with one type in graphql-js:

A schema definition for graphql-js

That’s quite a bit of code for a simple schema!

Whenever possible, code should be structured in such a way that it is as close as possible to our mental model of it. Every time we have to translate between our code and our mental model, it takes effort. We all know that the way the code looks in our source reflects the way we think about it. If the code is messy and hard to read, it’s much more likely that we’ll be unsure about what exactly it’s doing. That makes the code much harder to debug, and it makes mistakes much more likely. The primary goal of Apollo server should therefore be to make the structure of the code as clear as possible.

The graphql-js schema makes the whole schema appear as an inseparable blob. When you’re new to GraphQL and see this example, it will at best lead you to structure your code badly, and at worst it will turn you away from GraphQL. I think that’s completely unnecessary, because GraphQL itself actually has a very nice underlying structure. All we need to do is have that structure reflected in our code.

The structure of a GraphQL server

After trying out different ways to structure our code, we realized that a GraphQL server basically consists of three parts:

  1. A type system
  2. A set of stateless resolve functions
  3. A set of loaders

We’ll go through each of them one by one.

The type system

The first part of a GraphQL server is its type system. The type system defines which types exist, how they relate to each other and how they can be queried through the schema. For the example above, this is our type system definition:

A schema in GraphQL notation. “!” indicates a required argument

It’s very straight forward to see from the above type definition that a query for a Person takes exactly one (required) argument of type ID.

The resolve functions

If you’ve read the examples above carefully, you will have noticed that there is one piece of information missing that was in the schema above: the resolve field of Query.person.

Resolve functions tell the GraphQL server how to respond to a request for a certain field of that type. In the example above, there is only one resolve function, Query.person. That function tells the server that it should make a request to the backend and then parse it as JSON. The server will wait until that promise is fulfilled, and then pass the resulting object obj on to the Person type for handling. The Person type doesn’t define any resolve functions, so it expect obj to have the three properties id, name and age.

So if we define just the resolve function, it would look like this:

The resolve function for the schema above

With this and the type system above, our server has all the information it needs to respond to the following query:

query {
  person(id: 5)

So far so good, but that’s only two parts! What about the loaders I was talking about earlier?

The loaders

The example above will work, but it’s missing a critical distinction: the way the data is loaded should be separated from the resolve functions. There are a couple of reasons for that:

  • When your backend changes, you’ll only have to change one loader, not many resolve functions
  • One GraphQL query may include the same backend data in more than one place. Having one loader allows you to avoid duplicate code.
  • One GraphQL query might make many requests to the same backend, some of which overlap. Using a loader allows you to batch and merge requests where possible.
  • Separating the resolve functions and the data loading makes both components much easier to test.

So here’s what the above example should look like:

Separating resolve functions from loaders helps with clarity and maintainability

This doesn’t do any batching or caching yet, but with this separation it’s easy to integrate something like Dataloader.

Amazingly enough, graphql-js contains all the parts needed to generate a complete schema from the parts above with only about 10 lines of code! You can check out our github repo, if you don’t believe it.

It’s noting that we are not the first ones to come up with this idea, graph.ql does pretty much the same thing, and it was in turn inspired by graphql-schema-gen.

What’s next?

Providing a nice way to structure your GraphQL server is just the first step. For a great developer experience, here are some more things which we’re working on.

Guides and documentation:

  • Write a simple but complete guide that developers can follow to build a GraphQL server, similar to the Meteor guide. The guide will include information on setting up, debugging, authentication, etc.

Error handling, logging, profiling:

  • Make it easy to find bugs in resolve functions and data loaders by providing useful error messages for common and uncommon mistakes.
  • Make logging information about errors and profiling information easy.

Performance monitoring:

  • Integrate logging and performance monitoring to answer the following questions:
  1. Why is my query taking so long? Which backend causes the delay?
  2. Which parts of query X are hitting the cache vs. a backend?
  3. What queries are putting the most load on my server?

And more…

  • A library of loaders for various backends. You can customize these for your app.
  • Easy caching across requests to avoid unnecessary load on the backend.

That’s a lot of features, so I may have forgotten a few. If you can think of something else that should be part of a good GraphQL server library, let me know! Some of these could — and arguably should — be in graphql-js, but they are not right now. Putting them into Apollo Server first, will give us a chance to introduce, test and refine new features. Once the features have proven themselves, we can submit a pull request upstream.

I’m really excited to be working on this project, but I can’t do it all alone. If you are excited about it too, please don’t hesitate — get involved! Comment here, check out the repo, file an issue or submit a pull request. There are so many possibilities!

So… see you on github!?

Read what we’re learning about GraphQL, data loading, and JavaScript as we work on a new data stack in our new Medium publication, Building Apollo.

Written by

Jonas Helfer

Jonas Helfer

Read more by Jonas Helfer