GraphQL vs REST: Overview

Translations of this material:

into English: GraphQL vs REST: обзор. 2% translated in draft.
Submitted for translation by Big_Shark 08.03.2017

Know English?

Why don't you want to start to translate this material? You can invite your friends to help you.

* :
 

All can see and participate is translation


Text

A few months back I wrote a comparison between RPC and REST for Smashing Magazine, and now I want to talk about the differences between REST and GraphQL: the new kid on the block.

GraphQL is incorrectly considered by some to be a "replacement" to REST. GraphQL is a newer concept, being released by Facebook publicly in 2015, whereas REST was a dissertation published by Roy Fielding in 2000, popularized by companies like Twitter (quite inaccurately) in 2006.

This article aims to cover a few notable differences, and make the following points:

REST and GraphQL are totally different

GraphQL isn't a magic bullet, nor is it "better"

You can definitely use both at the same time

GraphQL is dope if used for the right thing

A few quick differences

REST is an architectural concept for network-based software, has no official set of tools, has no specification, doesn't care if you use HTTP, AMQP, etc., and is designed to decouple an API from the client. The focus is on making APIs last for decades, instead of optimizing for performance.

GraphQL is a query language, specification, and collection of tools, designed to operate over a single endpoint via HTTP, optimizing for performance and flexibility.

One of the main tenants of REST is to utilize the uniform interface of the protocols it exists in. When utilizing HTTP, REST can leverage HTTP content-types, caching, status codes, etc., whereas GraphQL invents its own conventions.

Another main focus for REST is hypermedia controls (a.k.a HATEOAS), which lets a well designed client run around an API like a human runs around the Internet; starting with a search for "How to complete my tax returns", reading a perfectly relevant article, and after a few clicks ending up on BuzzFeed article about Miley Cyrus throwing Liam Hemsworth a "Weed-Themed" birthday party.

If your API is not using hypermedia controls, then GraphQL could be a more relevant approach, because you weren't really using REST anyway.

This article will not attempt to point out a winner, but we're going to look at a few areas where the two differ. Don't get mad that a lot of the sections say "it depends", because the winner in each section really depends on what your API is doing, and how. GraphQL comes out stronger in some areas, REST in others, and sometimes they're both kinda terrible. Let's dig in!

Is the API More Than Data Transfer?

One of the most common tasks REST APIs provide is CRUD via JSON, but it can do plenty more than that, such as file uploads.

Uploading an image in the HTTP body can look a little something like this:

POST /avatars HTTP/1.1

Host: localhost:3000

Content-Type: image/jpeg

Content-Length: 284

raw image content

Leveraging a cool part of HTTP (and therefore REST), API developers can support application/json requests on the same endpoint to handle the upload slightly differently, and offer URL-based uploads too:

POST /avatars HTTP/1.1

Host: localhost:3000

Content-Type: application/json

{

"image_url" : "https://example.org/pic.png"

}

Generally the APIs I have worked on have enjoyed both, which is super handy as iOS often sends photos directly from local files, and web clients often send a URL to the user's Facebook display picture.

If we were talking about uploading videos or other large files, I would (as this article suggests) switch to another approach and have a dedicated service which handles the upload, leaving the main API to only accept metadata; title, description, tags, etc.

This is the approach you are forced to take with GraphQL, because you can only speak to GraphQL in terms of fields:

POST /graphql HTTP/1.1

Host: localhost:3000

Content-Type: application/graphql

mutation addAvatar {

addAvatarFromUrl(image_url: "https://example.org/pic.png") {

id,

image_url

}

}

Some will argue that this is more "clean", and it is, it's very clean, but being forced to create another service is overkill for smaller images, especially early on. Another approach is to upload directly to Amazon S3, forcing a dependency on clients and potentially letting your tokens leak, or… use multipart uploads, which are a super hacky approach that depends on if the server and various clients can even support it.

This is one area where REST holds strong. Some would say that REST handling CRUD and arbitrary stuff is confusing, but this is a core tenant of what makes REST so useful. A REST API can do anything, not just send fields backwards and forwards - even if that is how REST is often used.

Both GraphQL and REST Prefer Evolution

One false advertised benefit of GraphQL I've seen suggested (in quite a few locations) is that you "never have to version anything."

How versioning works in GraphQL. (source: http://graphql.org/)

How versioning works in GraphQL. (source: http://graphql.org/)

The suggested approach is to add new fields and deprecate old ones, which is a concept well known in REST as evolution.

Deprecating, communicating to third-parties, monitoring usage, and removing at an acceptable time, is exactly what many REST APIs have been doing forever.

Although GraphQL and REST can (and should) version via evolution just as easily, GraphQL really helps API developers out when it comes to deprecations.

GraphQL makes Deprecations Awesome

One area where GraphQL excels is to make monitoring field usage incredibly easy at a technical level. GraphQL clients are forced to specify the fields they want returned in the query:

POST /graphql HTTP/1.1

Host: localhost:3000

Content-Type: application/graphql

{

turtles(id: "123") {

length,

width,

intelligence

}

}

Tracking this would be trivial, but a REST API acts a little differently. Whilst all REST APIs make the base endpoint available via /turtles/123, not all APIs offer sparse fieldsets: /turtles/123?fields=length,width,intelligence. Of those that do offer it, it's almost always optional.

If a REST API client calls /turtles/123, then they could be using any field in that response. Imagine the API plans to get rid of intelligence (because all turtles are geniuses), how do the API developers know which clients are using that field?

An RPC approach is to make a new /getTurtle2 endpoint (or /v2/turtles/123 in a RPC API pretending to be a REST API), and tell people to use that new one.

One approach in REST APIs is to email warnings to whatever email address was entered when the client signed up for OAuth tokens (like Facebook previously did), maybe offering a feature flag so various clients can flip the switch when they are ready.

Another approach would be to create a new version of the resource, meaning instead of calling GET with Accept: application/vnd.turtlefans.com+v1+json they should start calling with Accept: application/vnd.turtlefans.com+v2+json.

All of the above approaches suffer the same issue, and that is that an entire version can be overkill for a simple change, and you might be forcing the developers to look into a version upgrade without needing to.

For example, if the client is requesting application/vnd.turtlefans.com+v1+json and the API is removing intelligence in v2, the API developers know not to drop v1 until the last of the clients are upgraded. Sadly, if clients are calling v1 but not using the intelligence field, the API developers have no idea! The API developers only know that the clients want v1, not what they're using of that v1 resource.

GraphQL makes it easy to track specific field usage to a client, meaning API owners can reach out to only those clients using fields that are heading out, or for internal projects you could have errors thrown in development/staging environments. I had a vague brain fart about doing this latter option for non-GraphQL HTTP APIs, but have not had a chance to see it through yet.

That could help in some situations, but would certainly be baked into things the same way it is in GraphQL.

GraphQL making field deprecation easier was a point brought to my attention by Tom Clark, super-smart Head of Devops at WeWork.

GraphQL Puts Client Performance First

GraphQL is always the smallest possible request, whilst REST generally defaults to the fullest. It's common practice to offer options like ?fields=foo,bar or partials. Google recommend doing this for HTTP APIs, whatever that's worth.

Even if a REST API returns only a basic partial by default, there are still more bits being transferred over the wire by default, than with the GraphQL approach. If a client needs a field, they request it, and if the API adds a new field, clients don't get it, unless they discover that field in a blog post or whatever and add it to the GraphQL query.

REST Makes Caching Easier At All Levels

Caching for HTTP, common usage in REST APIs, and different types of caching are huge topics, and something that I had to split out of this article. I'll post a followup shortly, which will be posted on the APIs You Won't Hate Newsletter.

In an endpoint-based API, clients can use HTTP caching to easily avoid refetching resources, and for identifying when two resources are the same. The URL in these APIs is a globally unique identifier that the client can leverage to build a cache. In GraphQL, though, there's no URL-like primitive that provides this globally unique identifier for a given object. It's hence a best practice for the API to expose such an identifier for clients to use. – Source: graphql.org

REST over HTTP uses a whole pile of HTTP conventions that make existing HTTP clients, HTTP cache proxies, etc., all work easily to benefit both API clients and API servers, but with GraphQL… tough. Reorganize your data stores, use a bunch of Redis, and hope clients are caching too.

GraphQL is a Query Language First

A very fundamental difference here of course is that only one of these is a query language. REST APIs are often created initially simple, then slowly more and more query language-like features are tacked on over time.

The most reasonable way to provide arguments for queries in REST is to shove them in the query string. Maybe a ?status=active to filter by status, then probably sort=created, but a client needs sort direction so sort-dir=desc is added.

Some APIs use also end up with arguments in the query string that are not related to filtering, more like options. In the GraphQL example they specify the unit they'd like to see a height returned in:

{

human(id: "1000") {

name

height(unit: FOOT)

}

}

{

"data": {

"human": {

"name": "Luke Skywalker",

"height": 5.6430448

}

}

}

That's pretty darn handy, and it stops the confusion of ?status=active being a filter but ?unit=foot being a display option. I have seen ?filter:status=active, ?filter[status]=active, etc., but this is still a bit of a mess.

In these scenarios, GraphQL beats the pants off of REST APIs that try to hand-roll their own query language functionality, but I would suggest hand-rolling your own query language is a bad idea anyway. GraphQL gives you a query language syntax, and SDKs for your programming language to fetch the right models for that, but so do things like OData, a project with the slogan "A Better Way to REST".

This is another example of folks saying that REST cannot do something, just because many REST APIs don't do it, or because its implemented poorly by many that do. Saying that, remember that the more customisation an endpoint-based API adds to the request, the fewer cache hits are likely at a network caching level, making the REST/HTTP approach less rewarding, and forcing you down the route of the application caching and database reorganisation that GraphQL forces anyway.

Pages: ← previous Ctrl next
1 2