I'm designing a new API for my project, and I want to return objects that have nested children as json. For that purpose i've decided to use RABL.
I want the client side to be able to understand whether the object is valid, and if not which fields are missing in order to save it correctly.
The design I thought of should include some fields as optional, under an optional hash, and the rest are required. The required fields should appear right under the root of the json.
So the output I try to describe should look something like this:
{
"name": "John",
"last_name": "Doe",
"optional": {
"address": "Beverly Hills 90210",
"phones":[{"number":"123456","name":"work"}, {"number":"654321","name":"mobile"}]
}
}
The above output example describes the required fields name and last name, and the not required address and phones (which is associated in a belongs_to-has_many relationship to the object). name, last_name and address are User's DB fields.
Playing with RABL I didn't manage so far to create this kind of structure.
Any suggestions? I'm looking for a DRY way to implement this for all my models.
RABL is really good in creating JSON structures on the fly, so I don't see why you couldn't achieve your goal. Did you try testing if a field is set to null-able in the schema, and thus presenting it as optional? It seems a good approach for me. For the nested children, just do the same, but extend the template for the children.
For example, in your father/show.rabl display a custom node :optional with all the properties that can be null.
Then, create a child/show.rabl with the same logic. Finally, go back to father/show.rabl and add a child node, extending the child/show.rabl template. This way you could achieve unlimited levels of "optionals".
Hope it helped you.
In this case I'd use the free form option.
From https://github.com/nesquena/rabl
There can also be odd cases where the root-level of the response
doesn't map directly to any object.
In those cases, object can be assigned to 'false'
and nodes can be constructed free-form.
object false
node(:some_count) { |m| #user.posts.count }
child(#user) { attribute :name }
Related
This question is language independent. Let's not worry about frameworks or implementation, let's just say everything can be implemented and let's look at REST API in an abstract way. In other words: I'm building a framework right now and I didn't see any solution to this problem anywhere.
Question
How one can construct REST URL endpoint for intersection of two independent REST paths which return collections? Short example: How to intersect /users/1/comments and /companies/6/comments?
Constraint
All endpoints should return single data model entity or collection of entities.
Imho this is a very reasonable constraint and all examples of Hypermedia APIs look like this, even in draft-kelly-json-hal-07.
If you think this is an invalid constraint or you know a better way please let me know.
Example
So let's say we have an application which has three data types: products, categories and companies. Each company can add some products to their profile page. While adding the product they must attach a category to the product. For example we can access this kind of data like this:
GET /categories will return collection of all categories
GET /categories/9 will return category of id 9
GET /categories/9/products will return all products inside category of id 9
GET /companies/7/products will return all products added to profile page of company of id 7
I've omitted _links hypermedia part on purpose because it is straightforward, for example / gives _links to /categories and /companies etc. We just need to remember that by using hypermedia we are traversing relations graph.
How to write URL that will return: all products that are from company(7) and are of category(9)? In otherwords how to intersect /categories/9/products and /companies/7/products?
Assuming that all endpoints should represent data model resource or collection of them I believe this is a fundamental problem of REST Hypermedia API, because in traversing hypermedia api we are traversing relational graph going down one path so it is impossible to describe such intersection because it is a cross-section of two independent graph paths.
In other words I think we cannot represent two independent paths with only one path. Normally we traverse one path like A->B->C, but if we have X->Y and Z->Y and we want all Ys that come from X and Z then we have a problem.
So far my proposition is to use query strings: /categories/9/products?intersect=/companies/9 but can we do better?
Why do I want this?
Because I'm building a framework which will auto-generate REST Hypermedia API based on SQL database relations. You could think of it as a trans compiler of URLs to SELECT ... JOIN ... WHERE queries, but the client of the API only sees Hypermedia and the client would like to have a nice way of doing intersections, like in the example.
I don't think you should always look at REST as database representation, this case looks more of a kind of specific functionality to me. I think I'd go with something like this:
/intersection/comments?company=9&product=5
I've been digging after I wrote it and this is what I've found (http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api):
Sometimes you really have no way to map the action to a sensible RESTful structure. For example, a multi-resource search doesn't really make sense to be applied to a specific resource's endpoint. In this case, /search would make the most sense even though it isn't a resource. This is OK - just do what's right from the perspective of the API consumer and make sure it's documented clearly to avoid confusion.
What You want to do is to filter products in one of the categories ... so following Your example if we have:
GET /categories/9/products
Above will return all products in category 9, so to filter out products for company 7 I would use something like this
GET /categories/9/products?company=7
You should treat URI as link to fetch all data (just like simple select query in SQL) and query parameters as where, limit, desc etc.
Using this approach You can build complex and readable queries fe.
GET /categories/9/products?company=7&order=name,asc&offset=10&limit=20
All endpoints should return single data model entity or collection of
entities.
This is NOT a REST constraint. If you want to read about REST constraints, then read the Fielding dissertation.
Because I'm building a framework which will auto-generate REST
Hypermedia API based on SQL database relations.
This is a wrong approach and has nothing to do with REST.
By REST you describe possible resource state transitions (or operation call templates) by sending hyperlinks in the response. These hyperlinks consist of a HTTP methods and URIs (and other data which is not relevant now) if you build the uniform interface using the HTTP and URI standards, and we usually do so. The URIs are not (necessarily) database entity and collection identifiers and if you apply such a constraint you will end up with a CRUD API, not with a REST API.
If you cannot describe an operation with the combination of HTTP methods and already existing resources, then you need a new resource.
In your case you want to aggregate the GET /users/1/comments and GET /companies/6/comments responses, so you need to define a link with GET and a third resource:
GET /comments/?users=1&companies=6
GET /intersection/users:1/companies:6/comments
GET /intersection/users/1/companies/6/comments
etc...
RESTful architecture is about returning resources that contain hypermedia controls that offer state transitions. What i see here is a multistep process of state transitions. Let's assume you have a root resource and somehow navigate over to /categories/9/products using the available hypermedia controls. I'd bet the results would look something like this in hal:
{
_links : {
self : { href : "/categories/9/products"}
},
_embedded : {
item : [
{json of prod 1},
{json of prod 2}
]
}
}
If you want your client to be able to intersect this with another collection you need to provide to them the mechanism to perform this. You have to give them a hypermedia control. HAL only has links, templated links, and embedded as control types. let's go with links..change the response to:
{
_links : {
self : { href : "/categories/9/products"},
x:intersect-with : [
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 1",
title : "Company 6 products"
},
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 2",
title : "Company 5 products"
},
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 3",
title : "Company 7 products"
}
]
},
_embedded : {
item : [
{json of prod 1},
{json of prod 2}
]
}
}
Now the client just picks the right hypermedia control (aka link) based on the title field of the link.
That's the simplest solution. But you'll probably say there's 1000's of companies i don't want 1000's of links...well ok if that;s REALLY the case...you just offer a state transition in the middle of the two we have:
{
_links : {
self : { href : "/categories/9/products"},
x:intersect-options : { href : "URL to a Paged collection of all intersect options"},
x:intersect-with : [
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 1",
title : "Company 6 products"
},
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 2",
title : "Company 5 products"
},
{
href : "URL IS ABSOLUTELY IRRELEVANT!!! but unique 3",
title : "Company 7 products"
}
]
},
_embedded : {
item : [
{json of prod 1},
{json of prod 2}
]
}
}
See what i did there? an extra control for an extra state transition. JUST LIKE YOU WOULD DO IF YOU HAD A WEBPAGE. You'd probably put it in a pop up, well that's what the client of your app can do too with the result of that control.
It's really that simple...just think how you'd do it in HTML and do the same.
The big benefit here is that the client NEVER EVER needed to know a company or category id or ever plug that in to some template. The id's are implementation details, the client never knows they exist, they just executed Hypermedia controls..and that is RESTful.
I am designing an API.
There's the user profile, accessible at
http://example.org/api/v1/users (resp. http://example.org/api/v1/users/:id)
Now, the user's profile will be dynamic.
So we will allow an API function to add a new profile attribute.
Is the following a valid REST API URL for this?
POST http://example.org/api/v1/users/attributes
Indeed, to retrieve a specific user, the user's id would be appended to the .../users/ URL.
Now if I use the "attributes" element after /users/, would that somehow break the user id pattern for the URL?
I'd like to keep the base URL to be api/v1/users though, because logically I am modifying the users profile still...
EDIT: The attributes would be added valid for all profiles, it's independent of a user. Say the profile has "name", "surname", "email", and I want to add "address" to all profiles (Of course I know that users with a missing "address" field would not get the new attribute)
What is a good practice to address such an issue?
I think the id should be kept in the URL because you are adding the attributes to a specific user, right?
It is an acceptable solution to use the /api/v1/users/attributes as long as the :id cannot be the text: "attributes". However I recommend to create your own media type, microformat, or microdata for the attributes, because it is rather a type than a resource.
I think you should check these links:
http://alps.io/spec/index.html
http://www.markus-lanthaler.com/hydra/spec/latest/core/
http://schema.org/
http://microformats.org/wiki/microformats2
http://amundsen.com/media-types/maze/
If the user can set what attributes she can have, only then should you use a resource for attributes. But then each user should have one. But I don't think using resources will be necessary, microdata and microformats both contain more than enough person description attributes...
Some update after 5 months:
Now if I use the "attributes" element after /users/, would that
somehow break the user id pattern for the URL?
From the perspective of the client that "id pattern" does not exist. The client follows links by checking the semantics annotated to them. So REST clients are completely decoupled from the URI structure of the actual REST API (aka. uniform interface constraint). If your pattern breaks, then it is completely a server side, link generation and routing issue, which is not a client side concern.
Say the profile has "name", "surname", "email", and I want to add
"address" to all profiles. What is a good practice to address such an
issue?
Address is an optional field in this case and probably a sub-resource, because it can have further fields, like city, postal code, street, etc... You can add address separately, for example with PUT /users/123/address {city: "", street: "", ...} or you can add those fields to your user form, and add a partial update to the user, like PATCH /users/123 {address: {city: "", street: "", ...}} if only the address changes.
In case you want to update every resource in the entire collection I would send a PATCH request to /users.
While it is a valid URI, I would suggest avoiding POST http://example.org/api/v1/users/attributes. In my opinion, it violates the principle of least surprise when a collection endpoint has a child node which is not a member of the collection. If you want to track user attributes as shared by all users, then that's a separate collection, perhaps /user-attributes.
POST /user-attributes
{
"name": "Email Address",
"type": "String",
...
}
GET /user-attributes would return all the possible attributes, and GET /user-attributes/{id} would return all the metadata around an attribute.
If there's no metadata, then #inf3rno's suggestion to just PUT the attribute up and let the server deal with it is definitely worth considering.
This all presupposes you need to manage attributes through the API. If not, I agree with #inf3rno that media types are the way to go. Of course, in that case you may want a media type for the user-attributes resource ..
Mandatory User Filters
I am working on a tool to allow customers to apply Mandatory User Filters. When attributes are loaded like "Year" or "Age", each can have hundreds of elements with the subsequent ids. In the POST request to create a filter (documented here: https://developer.gooddata.com/article/lets-get-started-with-mandatory-user-filters), looks like this:
{
"userFilter": {
"content": {
"expression": "[/gdc/md/{project-id}/obj/{object-id}]=[/gdc/md/{project-id}/obj/{object-id}/elements?id={element-id}]"
},
"meta": {
"category": "userFilter",
"title": "My User Filter Name"
}
}
}
In the "expression" property, it notes how one ID could be set. What I want is to have multiple ids associated with the object-id set with the post. For example, if I user wanted to add a filter to all of the elements in "Year" (there are 150) in the demo project, it seems odd to make 150 post requests.
Is there a better way?
UPDATE
Tomas thank you for your help.
I am not having trouble assigning multiple userfilters to a user. I can easily apply a singular filter to a user with the method outlined in the documentation. However, this overwrites the userfilter field. What is the syntax for this?
Here is my demo POST data:
{ "userFilters":
{ "items": [
{ "user": "/gdc/account/profile/decd0b2e3077cf9c47f8cfbc32f6460e",
"userFilters":["/gdc/md/a1nc4jfa14wey1bnfs1vh9dljaf8ejuq/obj/808728","/gdc/md/a1nc4jfa14wey1bnfs1vh9dljaf8ejuq/obj/808729","/gdc/md/a1nc4jfa14wey1bnfs1vh9dljaf8ejuq/obj/808728"]
}
]
}
}
This receives a BAD REQUEST.
I'm not sure what you mean by "have multiple ids associated with the object-id" exactly, but I'll try to tell you all I know about it. :-)
If you indeed made multiple POST requests, created multiple userFilters and set them all for one user, the user wouldn't see anything at all. That's because the system combines separate userFilters using logical AND, and a Year cannot be 2013 and 2014 at the same time. So for the rest of my answer, I'll assume that you want OR instead.
There are several ways to do this. As you may have guessed by now, you can use AND/OR explicitly, using an expression like this:
[/…/obj/{object-id}]=[/…/obj/{object-id}/elements?id={element-id}] OR [/…/obj/{object-id}]=[/…/obj/{object-id}/elements?id={element-id}]
This can often be further simplified to:
[/…/obj/{object-id}] IN ( [/…/obj/{object-id}/elements?id={element-id}], [/…/obj/{object-id}/elements?id={element-id}], … )
If the attribute is a date (year, month, …) attribute, you could, in theory, also specify ranges using BETWEEN instead of listing all elements:
[/…/obj/{object-id}] BETWEEN [/…/obj/{object-id}/elements?id={element-id}] AND [/…/obj/{object-id}/elements?id={element-id}]
It seems, though, that this only works in metrics MAQL and is not allowed in the implementation of user filters. I have no idea why.
Also, for your own attribute like Age, you can't do that since user-defined numeric attributes aren't supported. You could, in theory, add a fact that holds the numeric value, and construct a BETWEEN filter based on that fact. It seems that this is not allowed in the implementation of user filters either. :-(
Hope this helps.
Say I have a team object, that has a name property, a city property and a players property, where the players property is a an array of possibly many players. This is represented in an SQL database with a teams table and a players table, where each player has a name and a team_id.
Building a RESTful api based on this simple data-structure, I'm in doubt if there is a clear rule regarding, if the return object should/could include a list of players, when hitting /teams/:id ?
I have a view, that needs to show a team, and its players with their names, so:
1: Should /teams/:id join the two tables behind the scene and return the full team object, with a players property, that is an array of names and id's?
2: Should /teams/:id join the two tables behind the scene and return the team object, with a players property, that is an array of just id's that will then have to be queried one-by-one to /players/:id ?
3: Should two calls be made, one to /teams/:id and one to /teams/:id/players ?
4: Should a query string be used like this /teams/:id?fields=name,city,players ?
If either 2 or 3 is the way to go, how would one approach the situation, where a team could also have multiple cities, resulting in another cities table in the DB to keep it normalized? Should a new endpoint then be created at /teams/:id/cities.
When creating RESTful API's, is it the normalized datastructure in the DB that dictates the endpoints in the API?
Usually with a RESTful API, it is best that the use-cases dictate the endpoints of the API, not necessarily the data structure.
If you sometimes need just the teams, sometimes need just the players of a team, and sometimes need both together, I would have 3 distinct calls, probably something like /teams/:id, /players/:teamid and player-teams/:teamid (or something similar).
The reason you want to do it this way is because it minimizes the number of HTTP requests that need to be made for any given page. Of all of the typical performance issues, an inflated number of HTTP requests is usually one of the most common performance hits, and usually one of the easiest to avoid.
That being said, you also don't want to go so crazy that you create an over-inflated API. Think through the typical use cases and make calls for those. Don't just implement every possible combination you can think of just for the sake of it. Remember You Aren't Gonna Need It.
I'd suggest something like:
GET /teams
{
"id" : 12,
"name" : "MyTeam"
"players" :
{
"self" : "http://my.server/players?teamName=MyTeam"
},
"city" :
{
"self" : "http://my.server/cities/MyCity"
}
}
GET /cities
GET /cities/{cityId}
GET /players
GET /players/{playerId}
You can then use URIs to call out to get whatever other related resources you need. If you want the flexibility to embed values, you can use ?expand, such as:
GET /teams?expand=players
{
"id" : 12,
"name" : "MyTeam"
"players" :
{
"self" : "http://my.server/players?teamName=MyTeam",
[
{
"name" : "Mary",
"number" : "12"
},
{
"name" : "Sally",
"number" : "15"
}
]
},
"city" :
{
"self" : "http://my.server/cities/MyCity"
}
}
I'm designing a REST API and am looking for the recommended best practice for updating object graphs. My question is best explained in an example, so let's say that I have a GET resource as follows:
URI: /people/123
This URI returns an object graph like this:
{
"name":"Johnny",
"country":{"id":100,"name":"Canada"},
"likes":[
{"id":5,"name":"Fruit"},
{"id":100,"name":"Sports"}
]
}
When allowing the API consumer to update this resource, how would you expect the object to be updated via PUT or PATCH? Updating the "name" property is pretty straightforward, but I'm not certain about "country" or "likes", as the consumer can only only change the relationship to other objects and not create new ones.
Here is one way to request the update:
{
"name":"Bob",
"countryId":200
"likeIds":[3,10,22]
}
This update will change the resource to the following:
{
"name":"Bob",
"country":{"id":200,"name":"United States of America"},
"likes":[
{"id":3,"name":"Cars"},
{"id":10,"name":"Planes"},
{"id":22,"name":"Real Estate"}
]
}
This design explicitly and clearly asks the consumer to only update the "IDs" of the "Person", but I'm concerned that the object graph for a PUT/PATCH looks different than the GET, making the API hard to learn and remember. So another option is to request the PUT/PATCH as follows:
{
"name":"Bob",
"country":{"id":100},
"likes":[
{"id":3},
{"id":10},
{"id":22}
]
}
This will yield the same change as the previous update and does not alter the object graph. However, it doesn't make it clear to the API consumer that only the "IDs" can be updated.
In this scenario, which approach is recommended?
In my opinion you should stay with the same structure for both, GET and PUT requests. Why? Because it's quite common to map JSON/XML data into objects, and most (if not all) software that do the actual mapping work best if JSON schema is always the same.
So your webservice should accept a following JSON code:
{
"name":"Joe",
"country":{"id":200,"name":"United States of America"},
"likes":[
{"id":5,"name":"Fruit"}
]
}
However it doesn't have to take into account the country name and may focus only on the country id.