First, a disclaimer. This post is about me solidifying some understanding I think I have achieved. I am still learning here, so some of this is likely wrong or the wrong interpretation. Take it with a grain of salt. If you know better, feel free to politely tell me what I got wrong in the comments. Thanks!
I have spent a lot of time in my study of “REST” thinking that HATEOAS is just something that people who like to over-engineer would use. I could not understand the value of just including a link to the resource in its payload, as most examples of HATEOAS do to demonstrate. I mean, it is nice to have it when you get a list of resources, but…
I understood that there might be some value in creating links to actions but I did not understand how it would make sense to maintain that kind of flexibility. It felt like it would cost way more than it would save. Even if you were taking a long view. Who would build a magical client that would just figure out what it all meant?
But I had an epiphany. It is not about future proofing or some magical client. It is about communicating business options to the client. It is taking your “REST API” and making it more than just a fancy database interface with CRUD operations.
Suppose I am creating an API for a conference submission application. The basic operations are fairly clear. I need to be able to accept a submission, show a list of submissions, and show a single submission.
But there are other things a user of my API needs to be able to do:
- Edit a submission, if they own it.
- Add a vote to a submission, if they are an organizer reviewing the submissions.
- Delete a submission, if they have the right to do so.
- Accept the submission for the conference, if they are an organizer.
There could be more operations, but this is a pretty good starting point.
Obviously, some of these operations require authorization. You still need to secure your endpoints and prove the user can do the thing they are trying to do. Don’t get caught up in that. We can assume that your endpoint and infrastructure know how to keep people from doing things they are not allowed to do.
Standard Case
Any old user/client sends a GET to http://example.com/api/submissions/0001, they get this back:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Id": "0001", | |
"Title": "An Awesome Talk", | |
"Abstract": "Magnificent abstract goes here…", | |
"Status": "Submitted", | |
"Links": [ | |
{ | |
"Href": "http://example.com/api/submissions/0001", | |
"Rel": "self", | |
"Method": "GET" | |
} | |
] | |
} |
From this, you can see that the only valid operation they can perform is retrieving the submission.
“Author” Case
If I am logged in to the site (I’ve been given a security token, for example), when I send a GET to the same endpoint for a talk I submitted:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Id": "0001", | |
"Title": "An Awesome Talk", | |
"Abstract": "Magnificent abstract goes here...", | |
"Status": "Submitted", | |
"Links": [ | |
{ | |
"Href": "http://example.com/api/submissions/0001", | |
"Rel": "self", | |
"Method": "GET" | |
} , | |
{ | |
"Href": "http://example.com/api/submissions/0001", | |
"Rel": "change-submission", | |
"Method": "PUT" | |
} | |
] | |
} |
Now I see that there is an operation I can perform – “change-submission”. In my client code, I can look at the list of links to see if I should allow the user to edit the submission. I don’t have to know the logic for allowing submission – the server has already figured that out and told me I’m allowed to edit. I do need to know what “change-submission” means, so has to be a part of the contract and documentation of the resource.
Organizer Case
What if I’m an organizer?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"Id": "0001", | |
"Title": "An Awesome Talk", | |
"Abstract": "Magnificent abstract goes here...", | |
"Status": "Submitted", | |
"Links": [ | |
{ | |
"Href": "http://example.com/api/submissions/0001", | |
"Rel": "self", | |
"Method": "GET" | |
}, | |
{ | |
"Href": "http://example.com/api/submissions/0001/vote", | |
"Rel": "add-vote", | |
"Method": "POST" | |
} | |
] | |
} |
Right now, I can only vote. While voting is open it doesn’t make sense for me to accept the submission. The client doesn’t need to know those rules, it only needs to look at the links to see what options it should present.
Now that I understand that HATEOAS gives me a way to let the client know what it can do without it having to have any knowledge of my services internal rules, I see the value. It is clear to me that if you find yourself in a situation where HATEOAS feels like more overhead than it is worth, you probably have a CRUD API. That might be OK. But it is good to know where you are.