The argument for more than just REST | Part 1 - REST is too simple
Ethan Celletti
Posted on October 21, 2019
PART 1 - REST is too simple
TL;DR - REST has its place in simple object state manipulation but is not robust enough to be the only API standard to use in more modern/complex web applications. The problem is everyone uses it and it doesn't seem to be changing anytime soon. Also there is a link to my #Hacktoberfest REST alternative library at the end.
REST is the JavaScript of the web API world
- It's easy to use for basic applications
- Most developers use it and know at least the basics
- It breaks down with complex applications (raw JavaScript)
- It doesn't seem to have any alternatives
- The 'network effect' makes it hard to be able to switch to something new
I wont be going over the REST standard in this article, but there are plenty of resources to look at on the web.
So what's good about REST?
- The API is on top of HTTP, allowing it to use what already exists in a browser
- GET requests are so easy to use. This method type is essentially just a URL of where something is, so it can EASILY be fetched by copying and pasting into a browser address bar or in the command line with something like cUrl
- Most API requirements in front-end applications are CRUD operations on resources and REST is designed around that
- It is common for resources to have some sort of hierarchy and using REST route patterns (/parent_object/{id}/child_object/{id}) makes that easy
- Allows the use of JSON transfer which is great because it directly translates into JSON objects when calling from JavaScript, no XML/binary parsing
In other words, it's simple and easy to implement. There is a reason that the web only seems to use REST, it gets the job done and is easy to learn. With web 2.0 applications, a new standard was needed that designed around JavaScript, HTTP and CRUD operations. But over the years, web applications are getting more and more complicated and with that a basic API standard can start to break down. REST has failed to evolve and has fractured. Different developers have their own implementation and way of approaching REST to overcome the lack of a solid foundation.
What's not good about REST then?
Errors/Status Codes
Error handling is one of the first things a developer needs to do when coding with REST. Depending on the developer there can be multiple ways of implementing:
Basic Implementation:
if statusCode == 200
//parse response and return object
else
//throw an error
I will admit I have done this. It's not a great option because it does not handle any complicated cases BUT most of the time it doesn't need to. It's not a great way to do things but it can get the job done.
Better Implementation:
if statusCode is 2XX
//parse response and return object
else if statusCode is 3XX
//handle redirection
else if statusCode is 4XX
//update the HTML to show problem fields or throw an error
else
//throw an error
Okay, things are getting better. There are separate checks for each of the code types and we are able to handle cases such as a bad request and if we need to redirect. We aren't quite there yet though. By REST's standard 200 should contain a body, 201 is if a new resource is created, 204 is if there is no body, etc... Should a developer be checking that it is 200 vs 201 vs 204? It might depending on the API implementation but it really just breaks down to 'Does it matter?'. 99% of the time a developer will only care that the request was successful or not, not if the exact code is used.
Tedious Implementation:
if statusCode is 200
//Successful
else if statusCode is 201
//Created
else if statusCode is 202
//Accepted
else if statusCode is 203
//Non-Authoritative Information
else if statusCode is 204
//No context
...
else
//Throw error
One could argue that this is the best implementation because it handles all the cases. I would argue that it's a giant waste of time and is tedious as hell. Most of the status codes don't even apply to ANY request a developer would ever need to build against. If someone doesn't believe that to be true, I would redirect them to the status code 418 I'm a teapot.
Desired Implementation:
if response has error:
//handle error
else
//return object
It all breaks down to 'Why?'. All a developer wants to know is if it failed or not, not to do tedious IF statements to handle every single case. Even if a developer does have complicated error types for a certain request, HTTP status codes still wont be good enough on their own. In most cases HTTP status codes are a burden rather than helpful. Custom error codes that are returned in the body for a specific API would make implementing a lot clearer and easier. That way all the codes would actually mean something and need to be handled.
PATCH
PATCH is hard
PATCH is a very cool concept but NO ONE implements it. PATCH is hard for a server side developer to code and it is seen more as a luxury than a necessity. There are some libraries that can help with this and do some translation, but it still has a large cost on the whole stack. A POST/PUT method would replace the entire resource versus a PATCH method would allow the updating of each property individually. Not only does that require more work around logic validating which properties are specified but if there is validation checking, the current state of an unspecified property has to be pulled from the database. Also if the PATCH is implemented as a list of operations, it's even more complicated to be able to do all of the operations in a transaction instead of just doing one update. Its a big jump to allow for PATCH rather than just a PUT.
PATCH is fragmented
When it is implemented, developers build it based off what their needs are. Since there is no unified way of implementing it it can be designed in different ways. Two common approaches:
- The request being a 'partial' PUT where fields are ignore if not specified
- The request is a list of operations to perform on a specific field of the resource
The following examples will show what the different requests look like.
Goal
Original:
{
'field1': 1,
'field2': 1
}
Want:
{
'field1': 1,
'field2': 2
}
PUT
{
'field1': 1,
'field2': 2
}
PUT has to specify all of the fields or they are assumed to have a NULL value. So the original value for 'field1' must be sent as well.
PATCH as a 'partial PUT'
{
'field2': 2
}
Essentially a PUT but when a field is not specified then it is not updated. Personally I use this because I have never want an overly complicated PATCH. I don't see a lot of value in the PUT specification, if I want to set a value to NULL I will specify it. Because of this I usually don't even use the PATCH method type but override the PUT type instead.
PATCH as operations
[
{ "op": "replace", "path": "/field2", "value": 2 }
]
This a series of commands to be run on the server. It can be quite powerful but it is not easy to implement or use. Even though this may be the 'true' PATCH, very few actually implement it because it's usually too much effort for something that wont be used to its full extent.
There are other ways someone could implement PATCH but the point is that there is no single standard for it. Developers will implement it in the easiest way based off their requirements.
Tied to HTTP
Even though earlier I mentioned that HTTP was an advantage of REST, that was specifically for simple applications. When it comes to modern web applications, being tied to something designed for the transfer of HTML is not an advantage. Features such as headers and status codes (as mentioned above) only make things more complicated and confusing. With programming, abstraction has always been a key feature to follow. It shouldn't matter what is transferring the REST request to/from the server, rather what should matter is the contents of the request. Are things such as compression and cookies important? Yes, but that should be an HTTP concern, not a REST one. REST and HTTP just get smashed together which can lead to some inconsistencies and over complication more on this below. It's one thing to have it tied to HTTP on the front-end, but REST is used for server-to-server communication too. This is where it really breaks down because so much of the HTTP options are just superfluous complications rather than helpful.
Where To Put Things
There are too many options to where a developer can put request and response information. This isn't a problem for most cases but it breaks down when things get complicated. Where does information go that isn't strictly apart of the standard?
Example: Paging Requests
Query String
GET /Users?page=1&page_size=10
OR
GET /Users?limit=10&offset=10
HTTP Header
GET /Users
PAGE: 1
PAGE_SIZE: 10
Request Body (Note the method type is now POST)
POST /Users
BODY: { 'page': 1, 'page_size': 10 }
Example: Paging Responses
Body
{ 'items': [], 'total_count': 1000 }
OR
{ 'items': [], 'prev': '/Users?page=0&page_size=10', 'next': '/Users?page=2&page_size=10' }
Headers (Body is just a list of items)
X-Paging-Total-Count: 1000
OR
X-Paging-Prev: '/Users?page=0&page_size=10'
X-Paging-Next: '/Users?page=2&page_size=10'
Sometimes too many options can be good but also can muddy the waters of what is the right way or the standard.
Misc missing features
- Complicated GET requests that need to send over a request body
- Fire and forget or starting Background tasks
- Custom methods that do not revolve around a resource
- Upsert requests
- Multiple requests in one HTTP call
So if it's so bad, why hasn't REST been replaced?
Why hasn't JavaScript? Why has JavaScript spread beyond the browser like in NodeJS or being used as a scripting language in small applications? That may be answered by asking another question: Why wouldn't it? If someone has to program in JavaScript for a web application and if JavaScript can run anywhere, it seems compelling to be able to share code through the whole stack. Also developers already know how to program in JavaScript, so why bother training developers on other languages or integrate multiple different systems together? I'm aware there are holes in this argument such as JavaScript is a language and much more complicated than an API standard. People aren't so simple, but the concept still valid, companies don't want to juggle many languages or standards. Also standards are STANDARDS, they are designed to be singular so everyone can follow one pattern instead of multiple. The problem comes down to an imperfect working standard. In order to replace a standard, it seems there has to be a very good reason to switch but it seems like there hasn't been enough of a reason yet.
Will REST ever be overthrown?
Not anytime soon, it has its place and multiple options can fragment the web ecosystem. Even if a new standard is better, its a hard sell to tell customers/project managers that REST would not be used. People fear change and aren't willing to take risks if what is being used now is safe and the norm. A lot of times its not about what is best for the implementing or the consuming developers, but what works and what is known.
If anyone has any other arguments for/against REST, let me know below.
Stay tuned for Part 2 where I will be going over alternatives for REST.
My REST alternative library #Hacktoberfest
Posted on October 21, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.