RESTful APIs, the big lie
Why you might benefit from letting this popular paradigm rest in peace.
Important update
I have added a sequel describing JSON-Pure APIs best practice in detail. But do please read this first.
RESTful APIs are awesome, right?
If you have read an internet developer resume or job posting in the past 10 years, then you might be forgiven if you think that RESTful APIs are gifts bestowed from the heavens by The One True Web Developer Deity. RESTful APIs are everywhere. Even the marketing folks are pushing them in sales material intended for CEOs and Human Resources-type folks.
So how good of an idea are RESTful APIs really? Before we answer that, let’s look at where REST came from, and define RESTful APIs.
Where did REST originate?
REST became popular when it was detailed and promoted by Roy Fielding as part of his doctoral dissertation entitled Architectural Styles and the Design of Network-based Software Architectures in the year 2000. Roy is well known for his contributions to development of the web, especially the HTTP specification.
What are RESTful APIs?
REST is Representational State Transfer, an architectural style intended for building scalable web services. Roy advocated using the request methods he helped define in the HTTP standards to impart meaning to HTTP requests.
Thus the following HTTP requests all have different meanings when using REST:
GET /object/list
POST /object/list
PUT /object/list
There are only a few HTTP request methods. The full list is CONNECT
, DELETE
,
GET
, HEAD
, OPTIONS
, PATCH
, POST
, PUT
, and TRACE
. You may be forgiven if you’ve never heard of a few of these, as some are almost never supported by
client or server software.
Roy also advocated that HTTP response codes he helped define be used to communicate the meaning of the response. There are ~38 HTTP response codes. Below is a complete list. I’ve shortened a few titles in the interest of space:
Method | Method |
---|---|
200 OK | 201 Created |
202 Accepted | 203 Non-Authoritative Info |
204 No content | 205 Reset content |
206 Partial content | |
300 Multiple choice | 301 Moved permanently |
302 Found | 303 See other |
304 Not modified | 306 (unused) |
307 Temporary redirect | |
400 Bad request | 401 Unauthorized |
402 Payment required | 403 Forbidden |
404 Not found | 405 Method not allowed |
406 Not acceptable | 407 Proxy auth required |
408 Timeout | 409 Conflict |
410 Gone | 411 Length required |
412 Preconditions failed | 413 Request entity too large |
414 Requested URI too long | 415 Unsupported media |
416 Bad request range | 417 Expectation failed |
500 Server error | 501 Not implemented |
502 Bad gateway | 503 Service unavailable |
504 Gateway timeout | 505 Bad HTTP version |
The meaning of such an API transaction over HTTP is therefore at the minimum the result of
- the HTTP request method verb, e.g
GET
- the request address, e.g.
/object/list
- the request payload, e.g. form fields
- the response code, e.g.
200 OK
, and - the response payload, e.g. the JSON data.
Many have embraced this paradigm when providing a web service over HTTP, and this is what we refer to here as a RESTful API.
A RESTful API transaction can be more complex than shown above for various reasons, some of which we will discuss below. However, we will ignore the complicating factors associated with network transport and caching since these issues are universal to all communication channels.
RESTful APIs are actually pretty awful
REST is a great mechanism for many things such as content delivery, and it has served us well for two decades. But it’s time to break the silence and admit that the RESTful API concept is probably one of the worst ideas ever widely adopted in web software. Roy is probably a great guy and he certainly had a lot of great ideas. However, I don’t believe that RESTful APIs was one of them.
We will soon review a better way to build internet APIs. But before we can fully appreciate that solution, we must first understand the 5 primary problems with RESTful APIs that make them expensive, tedious, and error-prone. Let’s get started!
Problem #1: There is little agreement on what a RESTful API is
Ever notice how nobody calls their API “RESTpure”? Instead they call it “RESTful” or “RESTish”. That’s because nobody can agree on what all the methods, payloads, and response codes really mean.
Consider, for example, when we might use the 200 OK
response code.
Should we use it to indicate the successful update of a record, or should we
use 201 Created
? It seems we really should use a code like 250 Updated
but that code doesn’t exist. And can anyone out there explained to me
what 417 Expectation failed
really means? I mean besides Roy?
The vocabulary of HTTP methods and response codes is too vague
and incomplete to get agreement on meanings. No governing body - at least to my
knowledge - has convened to set things straight. What one company defines as
the meaning of 200 OK
is bound to vary in subtle and annoying ways
from 200 OK
from another company. Which means a RESTful
pattern isn’t as predictable as we might like.
If this were the only problem, I’d probably be writing RESTful APIs today. But the problems keep coming…
Problem #2: The REST vocabulary is not fully supported
Even if we could agree on the meaning of the vocabulary of REST,
we run into another practical problem: most client and server applications
don’t support all verbs or response codes for the HTTP
protocol. For example, most web browsers have limited support for
PUT
or DELETE
. And many server applications often
don’t properly support these methods either.
So how do we work around these application limitations? One common method developers use with browsers is to embed the intended verb into a browser form. Which means that the REST request now includes:
- an HTTP request method, e.g.
POST
- a request address, e.g.
/object/list
- a what-we-actually-intended-to-use request method embedded in
the request payload, e.g.
DELETE
- and a request payload itself, e.g. form field data.
Response codes aren’t handled much better.
Different web browsers (and servers) often interpret response codes
very differently. For example, if a 307 Temporary redirect
code is
encountered, one browser might allow the client JavaScript to consider the
response and cancel it before acting upon it. Another browser might not
allow the client JavaScript to consider the response code at all.
The only truly reliable response codes across all software are 200 OK
and
500 Internal server error
. After that, support varies from pretty good
to abysmal. Because of this, we often see what-we-actually-intended-to-use
response codes embedded in the response payload too.
Of course, even if we could get everyone to agree on what REST is, and magically fix all the not-ready-for-REST software connected to the internet, we’d still have another problem: the REST vocabulary.
Problem #3: The REST vocabulary is not rich enough for APIs
The REST vocabulary of methods and response codes is simply too limited to effectively communicate the wide variety of requests and responses required across all applications. Imagine we create an application where we want to send a “render complete” response back to an HTTP client. But we can’t do that using an HTTP response code, because (a) one doesn’t exist and (b) HTTP is not extensible. Bummer. I guess we are back to embedding what-we-actually-intended-to-use into the response payload.
There’s another problem: we don’t have one vocabulary, we have three.
HTTP response codes are numbers (200, 201, 202, etc)
that don’t directly correlate with the HTTP request methods (GET
,
POST
, etc). And our payload is typically in JSON. Executing a REST transaction
is like sending a package addressed in Swahili, with English content, and getting
delivery confirmation by message drums. This complexity is a great source of confusion
and errors. Which brings us to the next problem: debugging.
Problem #4: RESTful APIs are very hard to debug
If you’ve ever worked with a RESTful API, you know they are almost impossible to debug. That’s because we have to look at 7 different locations to piece together what’s happening in a complete transaction:
- The HTTP request method, e.g.
POST
- The request address, e.g.
/object/list
- The what-we-actually-indended-to-use request method embedded
in the request payload, e.g.
DELETE
- The “real message” in the request payload, e.g. form data.
- The response code, e.g.
200 OK
- The what-we-actually-indended-to-use response code embedded
in the response payload, e.g.
206 Partial content
. - The “real message” in the response payload
Not only do we have two very limited vocabularies that no one agrees upon or fully supports, but now we get to look in 7 different places to try to fully understand and debug a transaction. The only thing that could make this worse is if REST were completely tied to one protocol and not applicable for any other communication channel. That, of course, is our very next problem.
Problem #5: RESTful APIs are usually tied to HTTP
RESTful APIs break one of the fundamental laws about good communications: the message content should be independent of the transmission channel. Mixing of the two is a time-honored way to confuse our audience.
The typical intermingling the HTTP protocol of with the meaning of the transactions makes RESTful APIs completely non-portable. Moving a RESTful API from HTTP to some other transmission method requires disentangling and restructuring information from 7 different locations we use to encode the full meaning of RESTful the transactions.
Happily there is a much better solution that avoids or mitigates almost all the problems with RESTful APIs. We will refer to this solution here as JSON-Pure APIs.
The way forward: JSON-Pure APIs
JSON-Pure APIs fix most of the problem we’ve just discussed.
- They use just one transmission method to send a request - typically
POST
for HTTP or asend
for WebSockets. - They completely separate the request content from the transmission mechanism. All errors, warnings, and data are placed in the JSON request payload.
- They use only one response code to confirm proper receipt of a message -
typically
200 OK
for HTTP. - They completely separate the response content from the transmission mechanism. All errors, warnings, and data are placed in the JSON response payload.
- They are easy to debug since transaction information is found in easy-to-read JSON inside the payloads using a single, domain specific vocabulary.
- They can easily be moved or shared between transmission channels such as HTTP/S, WebSockets, XMPP, telnet, SFTP, SCP, or SSH.
JSON-Pure APIs came about because web developers found that RESTful APIs are not very browser or developer friendly. The separation of message and transmission channel results in an API that is usually faster, and almost always more reliable, easier to use, easier to port, and easier to debug. Today, if you use the Twitter API, for example, masochists can choose the RESTful API. The rest of us can use the JSON-Pure API (they call it the “Web API”).
I’ve been asked numerous times over the last decade to use a RESTful API instead of JSON-Pure. The last time I almost had to support a RESTful API was in 2011. Thankfully, the server team agreed to provide an alternate JSON-Pure API along with the RESTful variant by simply stuffing their HTTP methods and response codes into the JSON. After a few months, everyone I knew who used the API had switched to the JSON-Pure version. It was just that much better.
The future of JSON-Pure APIs
UPDATE: I have added a sequel describing JSON-Pure APIs best practice in detail. Certainly, the recommendations aren’t optimal for all situations, but they do result in a the type of API I frequently need for use with responsive Single Page Web Applications. As always, your mileage may vary. But if your use case is like mine, you might just want to make your next API “RESTless” and “go Pure”.
Cheers, Mike