Back View all Articles

Why We Are Moving off REST and Implementing GraphQL

Posted by Seth Carney on November 17th, 2020

Why we decided to transition to GraphQL

When we started building Courier, we investigated GraphQL, but the options for running a serverless version of Apollo (the technology we wanted to use) were limited and less stable. Since we don’t run EC2 or have dedicated servers, that was a major consideration for us. However, that’s changed quite substantially since we first looked at Apollo. Since then, we’ve been able to start transitioning both our internal and external APIs to GraphQL. I’ll explain some of the reasoning behind this below.

Limitations of REST

REST has been around for a long time, and today it’s still the most standard, widely-accepted way to write an API. REST is a specification that sits on top of HTTP. API calls are structured around objects (like profiles, preferences, and templates) using dedicated HTTP endpoints. For example, if you wanted to expose a way to programmatically manipulate your user profiles, you might have a REST endpoint user/{userId} which can be queried to perform CRUD operations using HTTP GET, POST, DELETE, etc. Writing a REST API is pretty straightforward — but REST can be tricky to use as an API consumer. 

First, REST wasn’t designed for complex sequences of operations that don’t fit neatly into the CRUD bucket. It’s not easy to update two objects at the same time, for example, and even retrieving data in certain scenarios can require multiple calls and branching logic, as one endpoint might have to call another one. One of the other downsides of a REST is that it puts a lot of responsibility on the API consumer (which may be your internal developers or your external clients) to know how the underlying data is structured. That’s not optimal for several reasons. 

The API calls aren’t oriented to the common actions that the user wants to take, they’re structured rigidly around your objects. That means that someone might have to call the same REST endpoint to set a label and to add a collaborator, even though these are two completely different use cases. Another reason it’s not a good idea to structure your API around how your data is organized is because things change. Changes to your data are inevitable, and it’s hard to adapt REST APIs to these changes (although in case you find yourself in this situation, you might want to check out our techniques for standardizing your REST APIs).

Advantages of GraphQL

GraphQL is a query language with a very developer-friendly approach to building APIs. It is based on the idea that the API consumer shouldn’t have to know anything about how the data is stored internally. Instead, you describe your data’s relational schema and the consumer can query that nested data from a single endpoint that never changes. GraphQL also conforms to the idea of CQRS, or command-query responsibility separation, which means the way you query data is different from the way you mutate data. 

One of the things I like best about GraphQL is that as a side-effect of implementing it, you’re forced to live by some of those rules of software engineering that you really should be living by. You have to think about your data holistically, and you don’t end up with a bunch of poorly-designed endpoints lying around as the result of shortcuts you took to meet deadlines. 

Because of how it’s built, GraphQL is really good at versioning: you can mark functionality deprecated, and you can change the underlying infrastructure without breaking existing integrations (and without the consumer even knowing). GraphQL also has a solid caching layer, which reduces our total operational costs because we end up not hitting our database as much. Because we’re a serverless shop, we will actually be implementing our caching layer through ElastiCache.

Using GraphQL in Courier

How we decided which technology to use

As I mentioned earlier, we’ve done research on the options for implementing GraphQL and kept an eye on the possible solutions for some time. The two best options that emerged for our use case were AppSync from AWS and Apollo GraphQL. We evaluated AppSync because we’re an AWS customer, we use cloud formations, and it was appealing to be able to stand something up quickly. But there were some core security choices we made when implementing multi-tenancy in Cognito that made the switch to AppSync more difficult. We realized that AppSync wasn’t really going to work for us unless we changed some of those fundamental decisions.

But that wasn’t the only reason we decided to go with Apollo. Compared to AppSync, which uses the Apache Velocity templating language (VTL), Apollo is just JavaScript. When we work with it we don’t have to do a lot of the mental context-switching that happens when you use different languages. Not to mention, Apollo is popular for a reason, it’s a rock-solid product that’s constantly evolving and has a growing and supportive community of users. Another reason we chose Apollo is for Apollo Federation, which will help us grow our Graph without affecting our performance as our product scales.  

Roadmap for transitioning to GraphQL

Right now, we’ve moved some of our internal APIs to GraphQL, such as the infrastructure for accessing users and tenants. We’re also building all new features with GraphQL as well.

While it will be some time before we move all of our internal APIs to GraphQL, we have plenty of important candidates for this transition. One use case that illustrates this well is autosave during template creation. When you are editing a template in Studio, you can add blocks, add or remove channels, or add conditions (just to name a few examples) and as soon as you make a change it gets autosaved. Behind the scenes, these edits are funneled through a common processor. 

One of the problems in REST is that it’s difficult to do partial updates. The various components end up having to send the whole template resource when they want to update a single field. Yes, you can implement PATCH endpoints, but those also come with their own complications. When you factor in doing validation on the full object with every call, autosave has the potential to become an expensive operation. Moving autosave operations to GraphQL mutations will help us solve this problem outside the constraints of a traditional REST API design and more closely represent the types of actions our users are taking.

As we move all of our internal infrastructure to GraphQL, our ultimate goal is to expose a GraphQL interface to our customers, along with an explorer that will make it so consumers can interact with our schema right from the browser. 

If you’re interested in working with GraphQL, we’re hiring! Check out some of our open roles.

Author
Seth Carney

Courier
GitHubtwitterLinkedInFacebookInstagram