I am working on a project whose client uses REST/HATEOAS to get and modify data.
Here is a part of the class diagram: class diagram.
A hal+json HATEOAS response of the request /group/12345 would look like:
{
"id": "12345",
"_links": {
"self": {
"href": "/group/12345"
},
"roles": {
"href": "/group/12345/roles"
}
}
}
To remove a role from this group I could simply execute a DELETE on the specific role. (because every role has to be in exactly one group and moving a role to another group shouldn't be allowed).
So I would add a link with the rel "drop" to the role. Therefore the client knows if or rather when a DELETE request is allowed:
{
"id": "67890",
"_links": {
"self": {
"href": "/roles/67890"
},
"users": {
"href": "/roles/67890/users"
},
"drop": {
"href": "/roles/67890"
}
}
}
So to delete a user the client looks for the drop link. If no link is found, the delete is not allowed. Otherwise it executes a DELETE request on the found link.
But what should I do if I want to remove a user from a role?
I cannot simply delete the user. The role -> user relation is not an aggregation.
How can I tell the client if removing a user from a role is allowed?
To remove a user from role I would use DELETE /groups/12345/roles/67890/users/ABC. And to delete the user I would use DELETE /users/ABC.
So where should I put the "remove user from role" link?
Thank you in advance :)
Links typically have three parts
A context identifier
a link relation type
a target identifier
Frequently, the context identifier is implied by the... umm... context, rather than being made explicit.
From what I can see, hal+json doesn't have a mechanism for explicitly specifying the context identifier, which would mean that you need to rely upon the implicit approach.
That suggests that if you want to be communicating about removing a user from a role, then you need a context that implies the right identifier.
You would normally do this in one of two ways
have a chain of links that leads to a representation of the user in that role, and include the drop role link relation in the list of links
embed a representation of the user in some other representation, and include the drop role link within that embedded resource.
For example, you might have a representation of /roles/67890/users that looks something like
_links: {
self: {
href: /roles/67890/users
}
}
_embedded: {
users: [ {
_links: {
self: /users/ABC
/role/remove : /groups/12345/roles/67890/users/ABC
}
} ,
... ]
}
Related
I think the answer to this question is great because it explains a lot about HAL: How to handle nested resources with JSON HAL?
However it does not fully answer the question (at least for me). Assuming we have a /employees resource that returns a list of all employees. I want the employees embedded but just with some basic information (not the full employee). This is OK according to the above answer and the spec. But how would my link look like?
So what would _links look like? Lets simplify the example. Assume there is no paging:
GET /employees
{
"_links": {
"self": { "href": "/employees" },
"employees" { "href": "/employees/{id}", "templated": "true" }
},
"_embedded": {
"employees": [{
"id": "1",
"fullname": "bla bli",
"_links": { ... }
},
{
"id": "2",
"fullname": "djsjsdj",
"_links": { ... }
}]
}
}
Does the templated "emloyees" URL make sense or would this be a case where you would not use any entry in _links? And if the URL is OK: is it necessary that the template parameter (here "id" does match the attribute in the embedded employee objects?
My heuristic is to consider the analogs in HTML - if it's OK for a web page, then it will also be OK for HAL.
"employees" { "href": "/employees/{id}", "templated": "true" }
What's the HTML analog? It's a form with a GET action. Can we have a form with a get action on a web page that also has digests of the information that will be reached via the form? Of course. So it must be fine here.
is it necessary that the template parameter (here "id") does match the attribute in the embedded employee objects?
I don't think it's necessary (the machines don't really care), but it's going to make life easier for the humans, and that alone has value.
Imagine, if you will, reading the documentation of a schema, and discovering that the same semantic concept (an identifier for an employee) has two different names with unrelated spellings. I would guess that would (a) introduce avoidable errors in the documentation when authors get confused about which spelling context they are in and (b) that's the sort of inconsistency that would make me suspicious of the quality of the specification as a whole.
But it's not impossible to have tradeoffs, and other benefits that outweigh these liabilities.
I am sending a message to a third party and the payload looks like as below:
{
"keys": {
"salesCaseId": 1000449
},
"attributes": {
"assetDeliveryDetails": {
"registrationNumber": "PXS3388",
"vinNumber": "DW1265",
"deliveryDate": "16-04-2020"
}
}
}
"Keys" and "attributes" are common for all the messages between us.
Question: Because I have only one nested JSON inside attributes which is assetDeliveryDetails is it necessary to have this nested JSON property? or I can have the other 3 attributes one level higher as below :
{
"keys": {
"salesCaseId": 1000449
},
"attributes": {
"assetRegistrationNumber": "PXS3388",
"assetVinNumber": "DW1265",
"assetDeliveryDate": "16-04-2020"
}
}
Does it makes sense to group because I can logically group them together or is it an overhead because I need to create equivalent DTO classes as well?
Generally speaking you want any data structure (whether that's JSON, XML, an Object model, etc.) to be as simple as possible to fulfill your requirements. If the second model (i.e. without assetDeliveryDetails) fulfills your requirements then I recommend you go with that. Unnecessary complexity is not desirable as it will be more difficult to understand and take more resources to parse..
I am using the rest-api profile for a Grails app and have the following in one of my json views (_event.gson):
model {
Event event
}
json g.render(event, [excludes: ['product']]) {
product {
id event.product.id
name event.product.name
model event.product.model
}
}
In short, a Product belongs to an Event. By default, I would get the product key with an id inside it as json. I wanted to add more fields to that.
So I used excludes so I could define the fields that would appear under the embedded json document detailing the product. My goal is to have the following as json:
{
"id": 123,
...,
"product": {
"id": 545434,
"name": "Something Cool",
"model": "MZX 1234"
}
}
The last fields -- model -- is not appearing. It seems it is being confused with the model keyword that is used in the very first line of my _event.gson file. Is there any way around this? I tried adding quotes to "model" but it still does not work.
The issue has been reported as a bug: http://github.com/grails/grails-views/issues/45
I'm relatively new to REST but I've been doing my homework on how RESTful should be. Now I'm trying to create a RESTful api implementing a JSON+HAL serializer for my models which have relationships with other models.
Example models in python:
class Category(Model):
name = CharField()
parent = ManyToOneField(Category)
categories = OneToManyField(Category)
products = ManyToManyField(Product)
class Product(Model):
name = CharField()
price = DecimalField()
related = ManyToManyField(Product)
categories = ManyToManyField(Category)
lets suppose we have a category "catalog" with a sub-category "food" with products "burger" and "hot-dog" which are both related.
First question. Categories and products should be resources so they need an URI, should I implement an uri field in my model and store it in the DB or somehow calculate it at runtime, what about multiple identifiers(URIs)?
Second question. Discoverability, In Hal format what should "GET /" and diferent nodes return to make the api easily self discoverable.
{
"_links":{
"self":{
"href":"/"
},
"categories":[
{
"href":"/catalog"
}
]
}
}
Third question. Add as properties, embed or link. Example "GET /catalog/food":
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"parent":"/catalog",
"categories":[],
"products":[
"/products/burger",
"/products/hot-dog"
]
}
{
"_links":{
"self":{
"href":"/catalog/food"
},
"parent":{
"href":"/catalog"
},
"categories":[
],
"products":[
{
"href":"/products/burger"
},
{
"href":"/products/hot-dog"
}
]
},
"name":"food"
}
{
"_links":{
"self":{
"href":"/catalog/food"
}
},
"name":"food",
"_embedded":{
"parent":{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
...
},
"categories":[
],
"products":[
{
"_links":{
"self":{
"href":"/products/burger"
}
},
"name":"burger",
...
},
{
"_links":{
"self":{
"href":"/products/hot-dog"
}
},
"name":"hot-dog",
...
}
]
}
}
Fourth question. How deep should I go when returning structures. Example "GET /catalog
{
"_links":{
"self":{
"href":"/catalog"
}
},
"name":"catalog",
"parent":null,
"categories":[
{
"name":"food",
"parent":{...},
"categories":[],
"products":[
{
"name":"burger",
"price":"",
"categories":[...],
"related":[...]
},
{
"name":"hot-dog",
"price":"",
"categories":[...],
"related":[...]
}
]
}
],
"products": []
}
About 1st question: I wouldn't store the URIs in the DB. You can easily calculate them inside your controller in runtime, and it's of controller's responsibility to care about URIs. This way you keep your model and your API decoupled, and should you decide to change the API structure in the future, you won't need to update your whole database with the new URIs.
About multiple identifiers, I'm not sure what the question is, but again, in my opinion, it has nothing to do with the DB, it's the routing and the controllers who should care about how to deal with any URIs.
About 2nd question: First of all, as a side note: I would keep the word categories as part of the URI. For example, I 'd have http://domain.com/api/categories/catalog/food. This way, you make your API more descriptive and more "hackable", meaning that user should be able to remove the /catalog/food part and expect to receive a collection with all the available categories.
Now, about what GET should return to allow discoverability: I think it's already being made clear from your URI structure. When user hits GET /categories he expects to get a list with the categories (the name and URI for each, to keep it lightweight), and when he follows one of the URIs like GET /categories/catalog he should receive the resource catalog which is a category. Likewise, when he wants to GET /products/burger, he should receive a product resource with all the attributes you have in your model. You may want to check this example about the structure of your responses.
About 3rd question: Again, the same example can help you form the structure. I think your 2nd way of response is closer to that, but I would also add a name field, not only the href.
About 4th question: When the GET request expects a collection of resources (like GET /categories) I would suggest providing only the necessary for each resource, that is, the name and the URI for each and only when user follows the desired URI, he can receive the rest information.
In your example, catalog is a resource, so on GET /categories/catalog I would include of course the name of the resource (catalog) and its self link, and for parent, sub-categories and products that are related to it, I would just provide the name and the URI for each, to keep it light. But: This was a general thought about designing APIs. In your actual problem, you should decide depending on your specific business problem. I mean, if your API is about a restaurant menu with categories and dishes, you may want to include the price, or a small description even when responding not for the actual product but for a collection of products, because probably for your users, that's an important information. So generally, provide all the necessary info (you only know what are these for your problem) when responding about a list of resources, and provide all the details of the resource when responding about a specific resource.
I would store something in the DB and calculate the URI at Runtime. That way if you move boxes it's not static.
Create a 'bookmark' page. The page we created was just a list of links with their rels. I believe HAL defines that specifically. The bookmark page was the only page other pages needed to know about
Not sure about this
How deep you go is up to you. There is a big debate now at my place of work for fine grain vs course grain. I'm going to do fine grain with small resource to keep the api simple, but then use the Expand-ability concept. It's a combination ofthe idea of composite resources defined on pg 35 of Subbu’s REST book and the expand concept used by Netflix. http://developer.netflix.com/docs/REST_API_Conventions
I'm designing REST API for many resources I need to maintain at least 2 representations of the same format (e.g. JSON) which only differ in the amount of data they expose.
For example, I have books, publishers and authors which I'm exposing via REST. So I have 3 collection of resources:books, publishers and authors.
When requesting a book I want it to include minimum information about its authors and publishers - just names, ids and hyperlinks to their full representation.
GET /books/12345
200 OK
{
id: "12345",
title: "My Book",
authors: [
{id:"111", name:"J. Smith", _href:"/authors/111"},
{id:"222", name:"J.R.R. Tolkien", _href:"/authors/222"}
],
publisher: {id:"333", name:"HarperCollins", _href:"/publishers/333"},
}
When user requests, e.g. /publishers/333 it should get more info about it, not just name and id:
{
id: "333",
name: "HarperCollins",
address : { ... },
contact : { ... },
logo : { ... }
}
In my application I have 3 classes Book, Publisher and Author, where Book contain publisher and collection of authors. And I'm using Jackson and Spring MVC to expose all the entities as JSON via REST API.
#RequestMapping("/books/{id}")
#ResponseBody
public Book getBook(String id) {
return getBookById(id);
}
#RequestMapping("/authors/{id}")
#ResponseBody
public Book getAuthor(String id) {
return getAuthorById(id);
}
Now the questions:
How would I tell the view which fields of which object should be rendered in which case?
Should I instead split each my entity into two - one for short and another for full representation (e.g. AuthorReference and Author, PublisherReference and Publisher).
Should I prefer composition or generalization if I go with option #2?
Is there any best practice how to deal with different detalizations of the same entity representation in REST applications?
Have you considered using a more standard hypermedia format, such as HAL?
Doing so, it becomes pretty clear that you would have a difference between resources, and links to other resources. For example, it might end up as:
GET /books/123
200 OK
{
"id": "123"
"title": "My Book"
"_links": {
"author": [
{ "href": "/authors/111", "title": "J. Smith" },
{ "href": "/authors/321", "title": "Jules Verne" }
],
"publisher": { "href": "/publishers/123", "title": "Harper Collins" }
}
}
In terms of implementation, I would say you can either:
Create custom DTO classes for each resource representation, along the lines of:
public class BookDto {
#JsonProperty(value="_links")
public LinkCollection getLinks() { }
public String getTitle() { }
}
Use the same classes, but use filters to modify the objects before Jackson serializes, and use some combination of #JsonProperty/#JsonIgnore.
1/ According to Spring MVC documentation, to serialize your object over HTTP you have to define your own class implementing HttpMessageConverter<T> with T either Book, Publisher or Author.
http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/remoting.html#rest-message-conversion
2/ It is not necessary to split your objects in two. The logic to select what fields you pick up from an Author is contained in the AuthorToHttp implement HttpMessageConverter<Author>. Just need to develop a AuthorReferenceToHttp implement HttpMessageConverter<Author> to use when you want less data to be send over HTTP.
Another solution could be either AuthorReference extend Author or Author extend AuthorReference on one hand and AuthorToHttp implement HttpMessageConverter<Author> with AuthorReferenceToHttp implement HttpMessageConverter<AuthorReference> on the other hand to serialize your Author.
3/ Inheritance/Generalization is enough. Not necessarily a need for a pattern.
Composite is a wrong choice, you don't need a tree structure. According to circumstances you want either all data about an author - default behavior -, or the main data about an author - "proxied" behavior -. Proxy would be a better choice.
4/ No idea.