Good morning/afternoon folks.
I am trying to implement HATEOAS in JSON:API.
My understanding is that HATEOAS is a vital part of a REST API, being the main (only, for some people) way to deal with states and state transitions.
If this is indeed the case, I feel it’s something that should be supported out-of-the-box by JSON:API, or with specific examples that would make an implementer’s life a bit easier. Unfortunately I haven’t found anything relevant in the documentation, so it seems I will need to improvise.
OK, griping over. So this is what I’ve come up with after quite a few hours of reading up on both HATEOAS and JSON:API.
My use case is very specific, so I’ll just generalise this to an example containing an “article” resource, which can transition to one of “Review” or “Reject” states instead.
The initial GET request is:
GET /article/123145
to which the API responds with 200 OK, with the following body:
{
data: {
type: "article",
id: "123145",
attributes: {
....
},
relationships: {
....
}
}
links: {
related: {
rel: "status",
href: "/publication/123145/status",
params: {
type: ["Review", "Reject"],
method: "POST",
expectedAttributes: ["ownerId"]
}
}
}
}
So what I have done is, use the links
top-level property of the response, and more specifically its related
property, to provide a list of the available next states for the resource, along with some necessary (or not) parameters.
How to store the status is one question, but since the status “resource” will contain more info, such as startDate, endDate, owner etc. , I am leaning towards making it a complex attribute.
In the above example I am returning a related
link object, which contains:
- a
rel
value ofstatus
(which is RFC8288 compliant, as required) - a
href
value of the URL that should be used to manipulate the resource’s state - a
params
object, which provides information on how to use the above URL.
The logic behind this, is to enable the client to make a JSON:API request to the URL specified in href
, in order to effect the state transition.
In the params
object, I’m providing:
- the
method
of the request (POST
in this example), - the
type
(a required property in a JSON:API request), which contains an array of the allowed next states, - and the
expectedAttributes
property, an array containing additional parameters for the state transition.
The client, in order to set the article’s status to Review, will be expected to send a request like below:
POST article/123145/status
{
data: {
"type": "Review",
"attributes": {
ownerId: { < Some reference ID, like 5e4dba36a8148d06045b4b2a > }
}
}
}
To which the API will respond with 200 OK
.
My questions are the following:
- Is the above approach reasonable / compliant? Do you see any obvious flaws with it?
- How can the API convey to the client the data types expected for the
expectedAttributes
? Could I possibly leverage thedescribedby
property of the parent object somehow? I appreciate that a “true” REST API should not rely on any OOB communication, so how can I contain all necessary information in theparams
object?
That’s it. I would greatly appreciate any feedback on insights that you folks might share with me, as I’ve fried my brain enough over the last few days!
Cheers,
Spyros