REST anti-patterns
--
In the past few years I’ve been working and studying RESTful APIs, and I have seen some common mistakes in different projects and forums, then I decided to write this post based on some experiences and on stuff I read.
Here are some anti-patterns, their explanation and examples.
URI not very RESTful
Your URI does not explain whats going on, simple like that.
RESTful APIs are about resources, when we’re building our URIs, we need to tell a story about that resource, looking at the URI the consumer must understand all about the given resource, where it came from, which is its identifier, which options it has.
Let’s say we need to create an account, what is the best way to represent this operation?
Below I’ve divided some examples in wrong and correct. Which one makes more sense to you?
Wrong
- POST /accounts/create
- POST /createAccount
Correct
- POST /accounts
We need to look at the whole thing. We shouldn’t have verbs in the URI. The action must be specified by the HTTP method (more about this below). So saying POST to accounts means to create an account.
If you look at the wrong examples, both of them have verbs on the URI.
Using wrong HTTP methods
The HTTP methods must be used to give the intent of the action that is happening. If you are returning information, you must use GETS for example.
Below is a list of actions and the respective HTTP methods. These are the most common ones.
- GET — Retrieve records
- POST — Create records
- PUT — Update whole records
- PATCH — Update pieces of records
- DELETE — Delete records
- OPTIONS — Discover options for a resource
- HEAD — Retrieve headers of a resource or resources
Having said that, mistakes such as below are often seen:
Wrong
- POST /accounts/4402278/delete
- POST /deleteAccount?accountNumber=4402278
Correct
- DELETE /accounts/4402278
The wrong example is doing a POST on accounts for a given id asking a delete option explicitly on the URI. The second wrong example is more suitable for RPC (we’ll talk about RPC below).
It’s somehow clear, however you are doing it wrong because HTTP has a explicit method for deleting resources, which is DELETE.
There are many other cases which could make this post even bigger, but here is just the challenging.
Hurting Idempotency
No matter how many times you call GET on the same resource, the response should always be the same and no change in application state should occur.
- Idempotent methods: GET, PUT, OPTIONS
- Non Idempotent methods: POST
What about the method DELETE? If you DELETE /accounts/4402278 twice, below are two points of view, which one you buy?
- The accounts will not be deleted many times … thinking this way it is idempotent
- The second time the resource will not be found and should return a 404 Not found, this way it’s not idempotent anymore
The second explanation seems more reasonable, as you are trying to delete a resource that does not exist, idempotency is properly implemented here.
Here is a cool video explaining idempotency.
Ignoring status codes
If your API only returns 200 (OK) or 500 (Internal Server Error), you are hurting the HTTP response codes.
The status codes were created to give the consumer an overall status of the response.
It means we need to be careful when choosing status codes to represent the state of the response, it needs to reflect exactly what happened on the server side.
Wrong:
- GET /accounts/123456 (and there is no matching record) response: HTTP status 200 (ok) with a body saying it’s not found.
Correct:
- GET /accounts/123456 (and there is no matching record) response: HTTP status 404 (not found)
If you are getting a resource as the example above, and there is no matching record, it should return 404, because it was not found. If you are returning a 200(ok) with a message in the body saying “Not found”, you are doing it wrong, the status code says “ok” but the message says “not found”.
Status codes will also help to give more clarity on the responses of your API. In many cases consumers only have to parse the status code to know exactly what happened, many times it’s much simpler than parsing responses bodies.
HTTP status codes
Here are some of the most used/important, in my humble opinion :)
Here you can see the HTTP statuses with some funny representation :)
Ignoring caching
It is easy to ignore the caching by including a header “Cache-control: no-cache” in responses of your API calls.
However HTTP defines a powerful caching mechanism that include ETag, If-Modified-Since header, and 304 Not Modified response code.
They allow your clients and servers to cache or negotiate fresh copies of the resource and through caching or proxy servers increase you application’s scalability and performance.
Ignoring hypermedia
If your API calls send representations that do not contain any links, you are most likely breaking the REST principle called HATEOAS.
Hypermedia is the concept of linking resources together allowing applications to move from one state to another by following links on the body of the responses.
If you ignore hypermedia, it is likely that URIs must be created at the client-side by using some hard-coded knowledge, which is bad because if you have more than one consumer, all consumers have to implement the same rules, and it’s possible that you may have a bug somewhere.
More on HATEOAS
HATEOAS stands for Hypermedia As The Engine Of Application State
It means that:
- Consumers interact with an application through hypermedia provided dynamically by the API
- Current state of the application is defined by your data and the links on your payloads.
- Consumers must have a generic understanding of hypermedia.
- Allows the server functionality to evolve independently.
- Interaction is driven by hypermedia, rather than out-of-band information.
In the example below we have a resource called “account” which has 100.00 on it.
{
"accounts": [
{
"accountNumber": "4502278",
"balance": 100.00,
"links": [
{"rel": "deposit", href: "/account/4502278/deposit"},
{"rel": "withdraw", href: "/account/4502278/withdraw"},
{"rel": "transfer", href: "/account/4502278/transfer"},
{"rel": "close", href: "/account/4502278/close"}
]
}
]
}
Let’s say now the owner of the account is on “the red” at this moment. the API should block some actions and show the payload such as:
{
"accounts": [
{
"accountNumber": "4502278",
"balance": -60.55,
"links": [
{"rel": "deposit", href: "/account/4502278/deposit"}
]
}
]
}
Of course, if you have a negative balance, and if a attempt to transfer money is made, the API should block it … I’m not even sure why I mentioned it :D
Confusing REST with RPC
Confusing REST with RPC is a common mistake.
It’s possible that REST in its purest way will not cover all your needs building your API.
Following the accounts example, let’s imagine we have the account number 4402278 and we need to build a endpoint to close the account.
How would you do it? What’s a good RESTful way to do it? There is no HTTP method for the close action. So what can we do? Below is my suggestion.
- POST /accounts/4402278/close
This would be the most correct way. It’s not 100% REST, however we’re reusing the resource and saying close the account 4402278.
If you really think about it, it’s not REST anyway, however it’s clear, we’re working under a specific resource and it clearly tells you that you are closing the account number 4402278.
REST is about semantics and clarity. When the consumer put the eye on your documentation (swagger is not documentation, more on this here) it should be clear which endpoint does what.
Some companies use the notation _ before a RPC action like closeAccount?accountNumber=4402278, it’d be like this:
- POST /_closeAccount?accountNumber=4402278
The idea here is to easily identify a RPC call in a REST world0,
.
Ignoring MIME types
If resources returned by API calls only have a single representation, you are probably only able to serve a limited number of clients that can understand the representation.
If you want to increase a number of clients that can potentially use your API, you should use HTTP’s content negotiation.
It allows you to specify standard media types for representations of your resource such as XML, JSON or YAML.
Conclusion
When building your APIs:
- Be coherent
- Require headers
- Use Standards (example json api)
- Build well designed URIs
- Return coherent status codes
- Use correct HTTP methods
- Care about idempotency
- Think about the semantic of the URIs
I really hope I helped you identifying some common mistakes and I also hope the tips given here will help you when designing your APIs.
Feel free to comment here if you wanna discuss more about it.