REST vs GraphQL: How Constraints Determine API Style

Much has been said about REST and GraphQL. Unfortunately, these conversations almost always hover around the idea of one being better than the other — more often than not, GraphQL is presented as the next step towards a RESTless world. Unfortunately, that is misleading, damaging, and misses the greater industry picture.

Today, we’re going to look at just where this so-called “zeitgeist of frustration” comes from, and why it’s incorrect to apply to this to our conversation on web API design styles. We’ll look at the nature of this conversation as a whole, and look to better, more effective methods of choosing the correct technology.

This article is based on the fantastic presentation delivered by Zdenek Nemec at the Nordic APIs 2018 Platform Summit:

Apples and Oranges: API Paradigms

“I’m going to make a few things clear here. One is an architectural style — that is REST. The other is a language and framework. Both can be used to build distributed systems, to build services and APIs. So I think of these things, though [GraphQL and REST] are slightly different things, as API paradigms.”

Before we get into the more detailed conversations regarding the underlying reasoning behind these comparisons, we should talk about the comparison itself. When comparing REST to GraphQL, it’s a mistake to consider this topic as a battle between two similar things. The reality is that REST and GraphQL are alike only in the fact that they are technological solutions – their actual implementations, however, and their underlying structures, approaches, and designs, differ enough to make this comparison an “apples and oranges” one.

We’re not comparing Python and Javascript, here — what we’re actually doing is comparing paradigms, which is an entirely different approach that doesn’t mesh well with the idea of one being better than the other in a general sense. Paradigms are different fundamentally from other types of solutions like languages specifically because of their broad-base usability – a paradigm is a solution for a given problem, not for all problems. Accordingly, a paradigm may, in fact, be appropriate in a specific use case, and thereby “better” than the other, while simultaneously in a separate use case, the opposite is true – in other words, the paradigm should be judged by the situation, not by qualitative or quantitative measures.

For these reasons, comparing GraphQL and REST is less like comparing, say, a Tesla Roadster and a Ford Mustang. In that comparison, we have two technologies that are broadly for the same purpose, and while the user requirements may make one more appropriate than the other, the fundamental ability to move from point A to point B is not accomplished in one against the other. Comparing a paradigm is more like comparing high-speed rail and air travel. For many use cases, air travel is more appropriate – if we were moving freight, however, or moving a distance of 10km, then a train would be much more appropriate. The reasons for each paradigm to exist are not mutually exclusive, so it’s hard to say “why use high-speed rail when we have airplanes?”

Simply put, the “versus” comparison here is ill-fated and malformed.

REST vs GraphQL: The Zeitgeist of Frustration

“Many [articles] are saying something along the lines of “REST in Peace REST” and “hooray GraphQL” But if you look not too long ago in the past, there were similar articles just with different words in the headlines – it was REST and SOAP. […] There are unhappy people, with the current or predominant styles. They have problems, and those problems were not heard by REST providers or SOAP web services provides — so the history is repeating.”

With that in mind, where does this conversation even come from? If each option is worthwhile for a given purpose, why are we tempted to think about it in terms of “all or nothing”? This comparison is rooted in the zeitgeist of frustration felt by developers and users alike for their given scenarios. Early on in the web API space, SOAP was the king for financial APIs and any transactional relationship that required a state. Soon, REST was developed, and the topic de jure was aligned with the idea that REST was the “SOAP-killer”, the technology that would finally free us from the iron stateful grip of the SOAP paradigm.

The reality, of course, is that SOAP was perfect for what it was designed for – and the invention of REST did not change that. REST was a step forward in the technology stack, sure, but it came alongside developments that required stateless processing and relational contracts, and as such, filled a new niche that was developing in the world of APIs. It would have been more appropriate, then, to simply say that REST was a new approach outside of SOAP – not a SOAP-killer, not even a replacement, but a new approach.

This is an important distinction. In many ways, this idea of a solution being better than the other in all cases is actually damaging to the community of users and developers in the API space. Adopting an all-or-nothing relationship to solutions of this type means that developers set out to develop a REST API first — not an API that is properly designed. In many cases, this can be made appropriate through the use of libraries or frameworks, but setting out to make a solution to a problem instead of allowing the problem to define a solution is troubling.

The best solution, then, is to actually look at the constraints at play and form an estimation of value for your API solution, rather than expecting your solution to be universally effective. Constraints dictate solutions and ultimately help guide your choice against what is appropriate, rather than following the zeitgeist of frustrated users all suggesting their own “perfect” implementation.

Architectural Styles

“Architectural style is a set of constraints which, upon applying, will imply a system with certain properties … For example, take a constraint where components in my system have to be decoupled. The client has to be decoupled from the server. That implies, if that constraint holds, that you can independently evolve the client or the server. A constraint “decoupling” implies independent evolution of both components.”

Before we dive into specific types of constraints, we should also consider the architectural style. In many cases, the architecture of the API itself will determine the best approach to your given problem’s solution.

As an API architect building a functional, powerful API, your main role is as a decider and understander. You should first endeavor to understand the paradigm you are operating under – for instance, whether your implementation is a collection of microservices, a messaging API, etc. – and what the implications of that paradigm are. With this in mind, you must make a choice – given your architecture and the development paradigm you have chosen, what specifically do you think is the best option for the given created API ecosystem?

This is extremely important, especially when you consider that REST and GraphQL aren’t even the predominant solutions in the entire API industry. This conversation tends to get blown up by those mired in the minutia of the competing options – in many cases, the enterprise world is still using SOAP. Accordingly, using a RESTful solution when your core service is SOAP and your API requires the storage of a state is foolish.

Constraints Should Determine Style

“Back when the colonists in the 18th century were colonizing america, they were building colonial buildings. They were in an environment where they had a lot of constraints around them. They didn’t have the right technology for building in bad weather. They couldn’t have a big glass window … They were building colonial homes because they were colonists and they had colonial homes. Guess what we are doing today? We say “I really like REST APIs or GraphQL APIs, and I think I’m going to build one.” That’s probably not what we want to be doing. We have some constraints around us that we have to listen to.”

According to Nemec’s model, there are four basic types of constraints:

  • Business Constraints: Business constraints are what the product needs to fulfill. It’s what task needs to be completed, and what the use case is. In essence, these types of constraints are those elements of the API that provides business and operational value and justify the actual existence of the API itself.
  • Complexity Constraints: These constraints are the level of acceptable or required complexity in the solution as implemented. In many cases, an API may be required to be efficient, small, and self-dependent – in those cases, lower complexity is preferred. On the other hand, if the API must obfuscate some of its functionality or encrypt its data streams, this will introduce complexity that must be dealt with. Some of the time, these requirements are determined by components – when the API connects to a wide variety of servers both local and remote, this pure component count will result in a more complex API than a simple network stack might.
  • Domain Constraints: These constraints include business, government, or industry regulations. These types of regulations can define how your API specifically operates – at the very least, it can dictate how data is secured in transit and at rest, which can result in differences in implemented technologies.
  • Cultural Constraints: The culture of your organization, as well as the user base you intend on working with, will often dictate specific implementations as well. One such example would be Conway’s Law, which says that you are essentially destined as an organization to create a system that mimics your internal communication patterns. Accordingly, when you have different parts of an organization that don’t communicate in the same way, this will result in specific language and paradigm choices, and in some cases, introduce additional Domain Constraints.

Ultimately, these constraints are far more important than any technological gains promised by any solution, and as such, will be the framework upon which your choices are predicated.

For more reading, check out our eBook: GraphQL or Bust

Constraints Dictate Properties

“On the other side, if you apply some constraints, you might get some properties.”

Constraints form what your solution looks like in the form of Properties. Properties are the end result of each constraint as applied to the real-world solution, as the constraint predicts, and almost all but determines, the properties of the end system. These properties can be broadly sorted into Performance, Scalability, and Simplicity.

  • Performance Properties: These properties define the performance of the underlying system. This performance can be anything between the speed of each call to the ability to recover from errors.
  • Scalability Properties: These properties determine how easy it is for the implement to scale out. The ability to scale largely determines whether the solution is expensive per node or if it is able to add additional systems without the associated singular overhead incurred by the core system.
  • Simplicity Properties: These properties determine how complex or simple the implementation actually is, whether in terms of code or in terms of physical architecture.

These properties are the product of each constraint ran through the use case, and can help determine the actual solution chosen. Regulatory constraints for encryption or hashing, auditability, transparency, or even traceability of calls could determine the property under which the process is judged. This could, of course, result in complexity, or in some cases, simplicity in the fact that all calls must be routed in a singular, secure fashion.

On the other hand, something like a relational API that ties in rich media through social graphs would result in a simplicity property that requires complex interactions – in this way, complexity is demanded of the system.

Deciding by Constraints and Properties

With all of this in mind, we can see that this is less a question of what is best, and more of what is most appropriate. Considerations when it comes to this space are widely varying, and depend entirely on your specific implementation. These considerations can cause you to adopt different technologies for vastly different reasons and purposes, and thus it’s not appropriate to call any single paradigm “better” than the other.

For instance, if you have a complex web of APIs, REST might be a better option, as you can leverage them for microservice development, and your mapping is already naturally bounded – there is no need for GraphQL to map microservice data in a regular network of APIs. On the other hand, let’s say that same network then outputs to a shim API that is publicly accessible, and this data needs to be leverageable in a wide range of formats as determined by the user – in this case, because GraphQL allows for calling data in a specific format as determined by the user, it is the appropriate option, even if the internal network is based upon REST

Conclusion

Simply put, asking what is best between REST and GraphQL is a faulty consideration. Zdenek Nemec gives a good rundown in his presentation. Per his suggestions, REST is a good choice when you need to build a system focused on longevity, content negotiation, precise authentication, or authorization rate limiting; GraphQL, on the other hand, is more appropriate when doing frontend-backend communication paths, replacing quasi-rest implementations, and providing good developer experience with minimal experience.

In any case, the zeitgeist should not determine your choice in paradigm – only the constraints and their resultant properties should do so. To do anything else is to make an uninformed choice, which has its own costs and negatives.