GraphQL is to REST what Javascript is to C/C++/Rust | wundergraph

GraphQL is to REST what Javascript is to C/C++/Rust

Jens Neuse

Jens Neuse

Founder @ wundergraph

In my recent article “Why not use GraphQL” (https://wundergraph.com/blog/why_not_use_graphql), I discussed a lot of the most common misconceptions about GraphQL. I keep hearing people compare the two as if they were exclusive concepts which can replace each other. Some people might have interpreted this as if I’m arguing against using GraphQL when quite the opposite is the case. I’m just not buying into the general “GraphQL solves XYZ problem and is better than REST” thing.

Instead, I’d like to give a detailed explanation of why the whole “GraphQL vs. REST” discussion doesn’t make sense. I’ll give you an in-depth view of why I believe the relationship between GraphQL and REST is much more like the one between Javascript & lower-level languages like C/C++ and Rust. Reading this will prepare you very well for discussions around choosing the right API style and implementation.

Why GraphQL vs. REST discussions are misleading

You usually hear that REST is an architectural style and that APIs who follow these rules should be called RESTful APIs. Then you’ll hear that GraphQL, on the other hand, is a Query Language, a specification, a concrete implementation, hence something completely different than a RESTful API. While true, in reality, there are more similarities than you might think, especially when it comes to using both styles of APIs. After drawing the boundaries, most articles comparing REST & GraphQL divert into various directions, highlighting how GraphQL is better in this and that way. This is usually where I start with disbelief.

Why would anybody compare an architectural style with a Query language? On the surface, it sounds like we’re comparing apples to oranges. Maybe there’s a gap between what people say and what they mean. Maybe they don’t mean REST but rather JSON over HTTP and instead of comparing it to GraphQL, the Query language, they mean GraphQL over HTTP in the way Apollo, a vendor, established it.

Instead of another comparison, I’d like to first evaluate if GraphQL conforms to the RESTful API style.

Are GraphQL APIs RESTful?

To find out, we can have a look at the dissertation of Roy Fielding. Another option is to evaluate the Richardson Maturity Model and apply it to GraphQL.

Let’s start with Roy Fielding’s dissertation.

Client-Server

GraphQL has a clear distinction between client and server.

Stateless

GraphQL, in most cases, operates in a stateless model. It’s not enforcing this model but almost all implementations are stateless. An implementer might operate GraphQL in a stateful environment but GraphQL itself would still be stateless.

Cache

GraphQL is perfectly cacheable. There are various ways of caching GraphQL operations on the server or the client. Lots of tooling exists to support this. Additionally, if you’re using persisted Queries, e.g. with Relay, you can even cache GraphQL operations using standard caching mechanisms supported by any browser or proxy. This is implied by the fact that the GraphQL specification itself doesn’t talk about the transport. The GraphQL specification talks about execution but doesn’t say if GraphQL should be used over HTTP or not. In that regard, it’s not even clear if you should say that GraphQL is cacheable. Implementations allow it, the spec doesn’t deny it.

Uniform Interface

GraphQL clients and servers exist in many languages with even more frameworks and tools. A GraphQL client doesn’t care if the implementation is written in Java, Rust or Golang. As long as both client and server conform to the GraphQL specification, they can work together perfectly fine.

Layered System

You can add intermediate proxies and cache servers however you want. As explained earlier, if you’re using persisted Queries, all intermediates in the request chain can use standard cache caching mechanisms.

You might be thinking that persisted Queries are not the norm. However, looking back at the origins of GraphQL, you should acknowledge that Facebook, the inventors of GraphQL, always used it together with Relay and therefore used persisted Queries by defaults. It’s companies like Apollo who made sending GraphQL Queries over HTTP POST requests popular.

On the other hand, the GraphQL specification doesn’t mention persisted queries. Again, the spec is also not talking about the transport. So, depending on the implementation and how you use GraphQL, a layered system is possible.

Code on Demand

Have you ever seen a Next.JS application? Each page is only loading the required Javascript for that particular page. Only when you navigate to other pages, you’ll download more Javascript to be also able to make them interactive. Does this have anything to do with GraphQL? Not really. GraphQL, being a Query language, is more focused on transferring data between client and server.

Resources and Resource Identifiers

GraphQL has a rich type system to describe resources.

Representations

With GraphQL you can use Queries to retrieve a representation of a resource. You can also perform actions on that resource by the help of mutations. Of course, there’s no unique URI to retrieve and operate on a representation. However, is that not just an implementation detail?

Working through Roy Fielding's dissertation, one might be thinking that GraphQL follows almost all rules. I’ve left out a few which are not affecting GraphQL, e.g. content negotiation because GraphQL simply doesn’t operate at this level. It’s fair to say that GraphQL only follows a subset of the RESTful API style while on the other hand, it’s not working against any of the rules.

Let’s have a look at the Richardson Maturity Model for further Investigation

Richardson Maturity Model

Level 0 - RPC

At Level 0, an API should use HTTP to call a remote procedure on another computer. This is exactly what GraphQL is doing.

Level 1 - Resources

Instead of sending all requests to the same endpoint, Level 1 requires to use different URIs to interact with specific resources. If you know one thing about GraphQL, it’s that there’s only one endpoint so it’s impossible to reach Level 1, isn't it? Well, isn't the URI just metadata to identify the resource? Doesn’t GraphQL have a rich type system to achieve the same thing with just one URI? You might be disagreeing here but to me, this looks like an implementation detail.

Level 2 - HTTP Verbs

To retrieve a resource we should only use the verb GET while mutating data requires us to send a POST or PUT, deleting data a DELETE, etc... GraphQL has a similar concept. Retrieving data is indicated by the keyword “query” whereas mutating data is prefixed with the operation name “mutation”. While not using HTTP Verbs, GraphQL uses a similar model to distinguish between various actions.

Once again, I’d like to refer to persisted Queries. They allow you to use the GET verb for Queries and the POST verb for Mutations.

To me, it’s once again an implementation detail but on the surface, you might hold the opinion that GraphQL doesn’t conform to Level 2 as well as Level 1.

Level 3 - Hypermedia Controls

Hypermedia controls are like the unsafe package in Rust, extremely powerful but also unforgiving. HATEOAS enables HTTP clients to indefinitely traverse a self-documenting API to explore an API in the same way as a user could do on the web. It doesn’t just sound impressive. The concept sounds too good to be true. It seems that Hypermedia As The Engine Of Application State is so powerful that most users shied away from it and stayed at level 2. GraphQL intentionally ignored Level 3. The engineers at Facebook seemed to not appreciate the power of the concept.

Summary - Are GraphQL APIs RESTful

GraphQL doesn’t follow all the constraints of Fielding's dissertation. At the same time, it’s also not implementing all Levels of the model from Richardson. However, it’s also hard to say that GraphQL is completely the opposite of REST.

REST & GraphQL

REST is a lot more powerful, more flexible and gives you more freedom to achieve many different tasks. GraphQL on the other hand leaves out a lot of the power, is more focused on specific tasks and has some smart solutions like the type system to improve developer productivity.

The same applies when you look at C/C++/Rust and Javascript. Browsers need to be very secure and performant. It makes a lot of sense to use a language like C++ or Rust to achieve this. Implementing a single page application on the other hand has completely different requirements. We value developer productivity over correctness and performance.

Persisted Queries have a similar effect on GraphQL as Webassemly to Javascript. WASM lets your write code in a more correct and performant way for the browser at the expense of additional complexity during development. Persisted Queries solve many problems with GraphQL, making it more performant and secure. Similar to WASM, it requires additional tooling to implement this pattern.

Implementing a data-driven API using just REST leaves a lot of room. This is because REST is so powerful and flexible. You can implement auth flows using REST while at the same time it’s easy to upload files and images and request a resource in different formats. This flexibility can also be a burden when all you want to do is implement a data-driven API.

Chosing the right tool for the right job

If you feel confident making the right decisions to implement a proper REST API I have nothing to argue against it. I’d expect you to document your API and offer an Open API Specification to make using your API as easy as possible.

You should also know that for authentication and file uploads there’s barely any reasonable choice except a REST API. However, when it comes to tasks that don’t require such flexibility, one should always choose a simple and bulletproof approach. GraphQL intentionally limits your flexibility. It has a clear focus on data-driven APIs to power modern applications for the web and mobile. If you’re building APIs for these platforms, you should either be extremely confident in what you’re doing and use “unsafe packages” or stay within the “standard library”. In that regard, there’s nothing you couldn’t do with REST what you can do with GraphQL. As we learned, GraphQL is a subset of REST. If you can say for sure that the features of GraphQL are enough to solve your problem, why would you want to have the additional burden of flexibility?

Conclusion

Race cars have a very small margin at which they operate well. Professional drivers must be trained to not immediately crash. On the other hand, race cars are not designed to be driven in traffic jams and cities with curbstones, as they are way too low. Additionally, race cars are very expensive to operate. You need a professional team to prepare the car for a race and maintain it well. Said team must be trained. You need expensive gear to operate the car.

So, if all you need is a car to get from home to work through traffic jams, you would not think of using a race car. A low maintenance electric car might make a lot more sense.

In the same way, many projects don’t require to be implemented in a language like Rust. Rust allows you to build extremely safe and performant applications but this comes at a cost. Rust is not as easy to learn and master as Javascript. Rust gives you a lot of flexibility and power whereas Javascript with its virtual machine limits what you can do. On the other hand, it’s said virtual machine that also makes developers lives a lot easier as they don’t have to worry about memory management. In Rust, you have to understand memory ownership. This is a challenge but also a great help to write correct concurrent code. Javascript on the other hand abstracts away this problem. You might never be able to write asynchronous code with Javascript at the same level as Rust, but this is intentional. Rust could be used to extend the functionality of Javascript. In that regard, it’s useless to discuss if either Rust or Javascript are better. They solve problems at different layers and can work together very well, e.g. through Webassembly.

We should apply the same reasoning to REST and GraphQL. GraphQL lets you query relational data very easily and gives API consumers the flexibility to access the API in their own very specific way. If you don’t have relational data, if you don’t have many API consumers, GraphQL is probably not a good fit.

Implementing OAuth2 and OIDC flows using GraphQL doesn't make sense. People tried to implement file uploads using GraphQL, but why? REST with its flexibility is designed for such use cases. Do you know you will benefit from using HATEOAS? You better know what you’re doing but why not.

On the other hand, if you’re building applications for the modern web and mobile devices, you’re most probably using modern frameworks like React or Vue who emphasize a component-based UI approach. Each component is different, presents and requires different data. GraphQL, with its focus on data, enables you to decouple the logic of consuming and providing data. API providers get freed from thinking about every use case. They can focus on a well-designed schema and optimize the resolvers. API consumers on the other hand are free to query the graph the way they want. They don’t have to ask for new endpoints every now and then just because the API does not yet cover their use case. It’s not that it’s not possible to do all this with REST. GraphQL just gives you a very neat abstraction on top that makes your life easier when all you do is deal with data.

Closing thoughts

Instead of arguing in favour of one technology you should be aware of the pros and cons of the available tools and select the best fit depending on the situation.

I believe that a lot of the constraints established by Roy Fielding stand the test of time. In an ideal world, we could have the best of both worlds. A Hybrid API style that feels like GraphQL but behaves like REST Level 2. This is what we’re building at WunderGraph.

What if we treat the GraphQL Schema as an intermediate API. Instead of having the client talk GraphQL over the wire, we could keep GraphQL entirely on the server and expose each GraphQL Operation as an endpoint. Because Operations are based on the GraphQL Schema we’re able to generate typesafe clients/SDKs in any language. Here’s how the workflow would look like: Write a bunch of Operations in GraphQL Playground, automatically create endpoints for all operations and generate the equivalent typesafe client in your language of choice. This gives your tailor-made SDKs for every use case. As a side effect, because we’re talking REST Level 2 over the wire we’re compatible again with standard caching mechanisms of browsers and proxies. This eliminates the need for a rich GraphQL client with a normalized cache.


Subscribe to our newsletter to never miss new blog posts or announcements.