How we’re using GraphQL and Apollo at Replicated

In recent months we’ve been making the transition from using REST APIs to using GraphQL, which has enhanced our development workflow in a variety of ways. If you haven’t heard of GraphQL, it’s an open-source data query language that allows you to request multiple types of data, all using a single endpoint. In addition to transitioning to GraphQL, we also use Apollo to help interface with and build our GraphQL APIs.

Now that we’ve been using both Apollo and GraphQL for several months during the build process of some of our new products, we decided to host a meetup at our LA office to talk about our transition and what we’ve learned. Here are some of the highlights.

Initial impressions of GraphQL

We began using GraphQL about 9 months ago. Starting roughly 4 months ago, all our new services are written with GraphQL, where possible. So far, we’ve found that GraphQL gives us a big productivity boost over our previous REST+Redux model:

  • Client code is simpler, frontend projects are smaller and flatter, and we spend noticeably less time writing state-management code.
  • With REST, we had 4 or 5 different ways to pass parameters between client/server, and different endpoints in the same service often employed slightly different conventions for requests and responses.
  • On the server side, we’ve found some of the lazy evaluation semantics of GraphQL enable us to improve performance and eliminate unnecessary queries. But doing this cleanly requires some design considerations, especially when using a typed language on the server side. The benefits are definitely there, but you don’t quite get them for free.

Apollo Engine/Server

Our stack on the server side consists of:

  • NodeJS + Express
  • Apollo Server
  • Apollo Engine for caching and telemetry

The most interesting thing here is Apollo Engine, Apollo’s transparent proxy that sits in front of your GraphQL server. It executes queries, sending deep APM metrics to for service monitoring and traffic analysis. We’re optimistic that we’ll soon be able to deploy Apollo Engine as part of our production GraphQL infrastructure.

Apollo Client

Apollo Client is a platform that integrates with almost all modern front-end frameworks, including React. It was designed specifically to be used with GraphQL, making it easy to fetch data from your API.

For most of our previous projects, we used Redux to manage our data store, which could get complicated really fast. It was really easy to lose track of where data was going and to which component—and having to create actions, reducers, constants, etc., for everything was a pain. It led to a large file tree, with Redux files containing hundreds of lines of code.

Thankfully, with Apollo Client, we’ve reduced that significantly. Instead of having to pass data to our components via Reducers and merging data into the state, in Apollo Client you simply write your query, hook it up to your component, and the data is merged into the Apollo Client store automatically. The same goes for mutations—just hook up to your component and use it! Simple.

Apollo Client is also extremely simple to set up, requiring only an ApolloProvider wrapper component around your existing application. This makes it easy to incrementally adopt Apollo Client, without having to completely overhaul your front-end.

Transitioning from REST to GraphQL with Apollo Client

Although we’ve transitioned a few of our REST APIs to GraphQL, there are some situations where we have to consume both a REST API and our GraphQL API. In these cases, we turn to a the Apollo-link-rest package, which allows you to wrap your GraphQL queries and mutations in Apollo Client with a @rest directive, and consume the response the same way you would your GraphQL API data.

Using this package to transition between REST and GraphQL is extremely helpful, but not necessarily a viable long-term solution. Apollo Client was built around the idea that you only need one API to get the data you need, maybe two. Using Apollo-link-rest for more than one REST API isn’t possible on a single Apollo Client, meaning multiple clients have to be instantiated. In that case, Apollo-link-state can also be used with state management tools like Redux to merge state existing outside of the Apollo store.

Both packages are excellent transition tools for consuming both GraphQL APIs and REST APIs in the same application.

Things we still want to explore

We’ve been making excellent inroads with GraphQL and Apollo, but we’ve still got more to learn. During our meetup, we heard about some newer Apollo and GraphQL features that we’re interested in learning more about and potentially integrating into our stack.

Persisted queries

This is a newer, built-in feature of Apollo that crawls your client source and maps generated IDs to your GraphQL queries and mutations. This is great because as an application scales, the text data alone for all queries and mutations can increase bandwidth usage. Using persisted queries, however, only references to those queries are actually sent from the client to the server.

This is also great for whitelisting specific queries, and preventing unintended queries from ever being executed.

Schema decorators

Schema decorators allow you to add features to your API using reusable modules, without having to add more code to your resolvers or any custom logic. This can be great for things like adding metadata to your schema, or cache control. Schema decorators are essentially interfaces that return functions that apply to your schema, which makes them easy to implement in a variety of languages.

This feature is still in development, but it’s one we’re definitely keeping an eye on! You can read more about schema decorators here.

Lessons learned, and a couple of gotchas

One of the most common things developers have said about Apollo and GraphQL is that they’ve made building applications and APIs way more efficient and fun—and we agree! Both have helped us prototype and build features at Replicated much faster, and we’re excited to see what new features GraphQL and Apollo introduce over time.

In the meantime, here are some lessons we’ve learned about our latest implementations:

  • Apollo Engine is really powerful, but still a growing framework.
  • Lazy Fetching is a great feature of GraphQL, but you need to design for it.
  • Organizing schema around products/features, keep schema near your native type definitions -- we’ve found Decorators + IOC patterns to be helpful here.
  • Apollo Client is still under active development so expect frequent changes.
  • Apollo Cache has a few knobs and dials, so it can require some tuning and manual cache-busting to get it configured correctly.

As we continue to work with GraphQL and Apollo, we’ll continue to share more of our build process. If you have any tips you’d like to share, please reach out to us at (somewhere) and teach us something!