My question is about when it's OK to merge separate models into one single REST resource and whether this leads to tricky and difficult to work with design, down the line.
Let's say I have a movie streaming service and users can only watch movie genres they have permissions for. Let's say these are represented with these hypothetical models:
users (id)
movie_genres (id, genre_name)
users_to_genres_permissions (id, genre_id, user_id)
exposed through REST routes /users /movie_genres and /users_to_genres_permissions
Now, as a user client of this API (think a website or a mobile app), in order to find out what genres I'm allowed to get hold of, I would fetch the genres permissions and then all the movie genres. Two network calls.
However, an argument could be made that having to make multiple round-trips to the API is inefficient, and you additionally have to deal with a bunch of joins on the client. This example is simple enough with its 3 relations, but in the real world you could have much longer chains.
Thus one could consider collapsing two models into one, and for example return permissions already joined to movie genres:
movie_genres (id, genre_name, authorized_for_current_user)
However the question is, this thought process can be taken pretty far. You could save the client a lot of joins and round-trips by doing all joining on the server. However, at what point do you stop? At what point is what you returning no longer a REST resources but a generic blob of data that's been concatenated together?
Is there a rule of thumb for deciding where to draw the line?
REST stands for Representational State Transfer. From wiki:
Requests and responses are built around the transfer of
representations of resources. A resource can be essentially any
coherent and meaningful concept that may be addressed. A
representation of a resource is typically a document that captures
the current or intended state of a resource.
As such, RESTful web-services provide access to resources, which means, that any API call should concentrate on one resource - and that should be your "rule of thumb".
The example that you posted is very basic, but if you'll add more entities, such as: movie-producers, actors, media-companies etc, then each request should handle only one entity. That said, your backend would need to handle requests that will require it running JOINs, for example, movies recommendations for user X. But don't let it confuse you - the request should be very simple and the response should include a "list" of objects of type movie (only one entity!).
Related
Say I have a relational database with 100+ tables. Each table models some sort of entity (person, address, vehicle, dog, etc etc). Say I also have a restful API and a bunch of people who want to POST data into this database. Many times this data comes in as an XML package or POST data from a web form or something of that nature. Sometimes we need to post to all the tables of the database, sometimes most, sometimes some, sometimes one.
Now requiring our clients to post clumps of multi resource data into a 100+ table persistence via the restful way of
POST /person
POST /email
POST /vehicle
POST /insurance
is insane! So we could have a resource instead that is
POST /auto-record
{ post body of key values for all the tables needed to make an 'auto-record' }
and it would be connected to some sort of business logic that knows to make inserts into the many tables of the database needed. Okay great. But now that I'm thinking about it, does this design abide by the open/closed principle? If we ever needed to update/add/remove to what an 'auto-record' is then we screw up our clients.
How can restful api's deal with resource groupings? Or does it simply not? Are there alternatives?
You can implement more versions of your RESTful API resource /auto-record. For now modify your resource URI to /v1/auto-record. When there will be a feature change request, you will simply provide your customers with a new resource /v2/auto-record. Old functionality will be preserved at /v1/auto-record and new users will have their needed functionality at v2/auto-record.
I'm trying to wrap my head around how to design a RESTful API for creating object graphs. For example, think of an eCommerce API, where resources have the following relationships:
Order (the main object)
Has-many Addresses
Has-many Order Line items (what does the order consist of)
Has-many Payments
Has-many Contact Info
The Order resource usually makes sense along with it's associations. In isolation, it's just a dumb container with no business significance. However, each of the associated objects has a life of it's own and may need to be manipulated independently, eg. editing the shipping address of an order, changing the contact info against an order, removing a line-item from an order after it has been placed, etc.
There are two options for designing the API:
The Order API endpoint intelligently creates itself AND its associated resources by processing "nested resource" in the content sent to POST /orders
The Order resource only creates itself and the client has to make follow-up POST requests to newly created endpoints, like POST /orders/123/addresses, PUT /orders/123/line-items/987, etc.
While the second option is simpler to implement at the server-side, it makes the client do extra work for 80% of the use-cases.
The first option has the following open questions:
How does one communicate the URL for the newly created resource? The Location header can communicate only one URL, however the server would've potentially created multiple resources.
How does one deal with errors? What if one of the associons has an error? Do we reject the entire object graph? How is that error communicated to the client?
What's the RESTful + pragmatic way of dealing with this?
How I handle this is the first way. You should not assume that a client will make all the requests it needs to. Create all the entities on the one request.
Depending on your use case you may also want to enforce an 'all-or-nothing' approach in creating the entities; ie, if something falls, everything rolls back. You can do this by using a transaction on your database (which you also can't do if everything is done through separate requests). Determining if this is the behavior you want is very specific to your situation. For instance, if you are creating an order statement you may which to employ this (you dont want to create an order that's missing items), however if you are uploading photos it may be fine.
For returning the links to the client, I always return a JSON object. You could easily populate this object with links to each of the resources created. This way the client can determine how to behave after a successful post.
Both options can be implemented RESTful. You ask:
How does one communicate the URL for the newly created resource? The Location header can communicate only one URL, however the server would've potentially created multiple resources.
This would be done the same way you communicate linkss to other Resources in the GET case. Use link elements or what ever your method is to embed the URL of a Resource into a Representation.
I am curious as to how a CRUD REST API would implement the idea of a tweets resource. Of course, an application such as Twitter has the notion of tweet objects, but these are needed by the application in various ways ("collections").
Twitter would need an endpoint for user timeline (tweets published by a certain user) and also for the home timeline (the timeline of tweets from people a user is following). I imagine, in a CRUD API, user timeline would be located at a URI such as: tweets?filter={username:"Bob"}
However, I'm not quite sure how a CRUD API design would implement the home timeline collection of tweets. Furthermore, collections such as favourites for a user — are these treated as separate resources altogether, or should they somehow be attached to the tweets resource?
Furthermore, Twitter have not used the CRUD design for their API. Maybe there is a good reason for this?
The good thing about resource design is that it doesn't really matter, as long as it makes (some) sense. Obviously some nuances are in place, but let's get to the point. Business models don't (have to) map 1:1 to resources, this is probably why you don't find such relation in the Twitter API.
Some assumptions: Timelines are pre-defined and their behaviour isn't influenceable, other by creating new tweets. Favorites are (references to) tweets. Favorites are influenceable.
A favorite collection resource, could be something like:
/user/bob/favorites
Your "CRUD" operations could be something like:
[POST] /user/bob/favorite { "tweet_id": "343fe4a" } -- Add a new favorite
[GET] /user/bob/favorite -- All favorites, for the user Bob
[DELETE] /user/bob/favorite/343fe4a -- Delete tweet 343fe4a as being favorite
Normally it's best to avoid multiple variables in a single resource, as this introduces a certain complexity that isn't needed. In this example, however, a favorite doesn't have it's own identifier. It instead re-uses the identifier from a tweet and it's also tightly-coupled with a user.
If a favorite does have it's own identifier, I would go about creating a resource like: /favorite/ef213e13f this could return meta-data or act as an alias (redirect) to a tweet for a HTTP GET method or a resource to "un-favorite" something (DELETE method).
This statement probably makes more sense if we don't talk about tweets, but instead about a blog with articles and comments:
/blog/article/42 -- representing an article
/blog/article/42/comments -- representing a collection to all comments for this article
/blog/comment/44571 -- representing a single comment
Depending on what you want, a couple of examples for timelines could be resources like:
/user/bob/timeline/home
/user/bob/timeline?type=home
/timeline/home?user=bob
As I mentioned earlier, it's best to avoid using multiple variables in a resource. I would probably pick option 3. The reasons being, besides the complexity of having too many variables, is that such a resource probably isn't worth caching (client-side) and no CUD actions may be done on it. Since it's most likely an aggregate resource for different entities.
A couple of closing words:
Design resources first and only then come up with a matching URL
Don't design resources 1:1 to (business-)models
Don't over think the situation from the start. Implement something and tinker with it to see possible problems in the future. Once you're happy, put it in production.
Suggestions for further reading:
HAL - http://stateless.co/hal_specification.html
Hypermedia - http://en.wikipedia.org/wiki/Hypermedia
RMM - http://martinfowler.com/articles/richardsonMaturityModel.html
Roy Fielding's blog - http://roy.gbiv.com/untangled/tag/rest
I'm building my first REST API (at least trying) for a personal project.
In this project there are resources called players which hold can be in a team. According to REST API design rulebook a resource should be made either to be a document or a store and one should keeps these roles as segregated as possible.
Yet I would like to append some metadata to the team resource, eg the date the team was founded. Is it okay then for GET /teams/atlanta to return this metadata (making it a document) alongside the list of players in the team (making it a store).
Is this a good idea? If so why? If not why not and how to solve this better?
I know there are no rules to developing a REST API, but there are good practices and I would like to adhere to those. Please also not that this is really my first REST API so pardon my ignorance if there is any.
I would recommend having GET /teams/atlanta return just the information about the team, such as the founding date that you mention, and then having GET /teams/atlanta/players return the list of players for that team. These distinctions become more important when you are presenting an API that uses HTTP methods other than GET.
For example, if you wanted to add a player to a team - this would be a lot easier if you could just POST a player object to /teams/atlanta/players than if you had to PUT the whole team object to /teams/atlanta every time you wanted to add one individual player.
If your API only allows retrieval of data, and if it is for a specific client application, there is an argument for combining all the team data into one object to save the client having to make additional requests for the data, but bear in mind that it is less flexible.
Your application may want to display a list of teams by calling GET /teams but you probably wouldn't want all of the player information included in each object in the list as this is quite a lot of data, but if GET /teams/atlanta returns player information then it would be inconsistent not to include it in the list version too.
I would personally favour splitting up the resources as I've suggested, and live with the fact the client may need to make an extra request or two.
Currently I am developing an API and within that API I want the signed in users to be able to like/unlike or favorite/unfavorite two resources.
My "Like" model (it's a Ruby on Rails 3 application) is polymorphic and belongs to two different resources:
/api/v1/resource-a/:id/likes
and
/api/v1/resource-a/:resource_a_id/resource-b/:id/likes
The thing is: I am in doubt what way to choose to make my resources as RESTful as possible. I already tried the next two ways to implement like/unlike structure in my URL's:
Case A: (like/unlike being the member of the "resource")
PUT /api/v1/resource/:id/like maps to Api::V1::ResourceController#like
PUT /api/v1/resource/:id/unlike maps to Api::V1::ResourceController#unlike
and case B: ("likes" is a resource on it's own)
POST /api/v1/resource/:id/likes maps to Api::V1::LikesController#create
DELETE /api/v1/resource/:id/likes maps to Api::V1::LikesController#destroy
In both cases I already have a user session, so I don't have to mention the id of the corresponding "like"-record when deleting/"unliking".
I would like to know how you guys have implemented such cases!
Update April 15th, 2011: With "session" I mean HTTP Basic Authentication header being sent with each request and providing encrypted username:password combination.
I think the fact that you're maintaining application state on the server (user session that contains the user id) is one of the problems here. It's making this a lot more difficult than it needs to be and it's breaking a REST's statelessness constraint.
In Case A, you've given URIs to operations, which again is not RESTful. URIs identify resources and state transitions should be performed using a uniform interface that is common to all resources. I think Case B is a lot better in this respect.
So, with these two things in mind, I'd propose something like:
PUT /api/v1/resource/:id/likes/:userid
DELETE /api/v1/resource/:id/likes/:userid
We also have the added benefit that a user can only register one 'Like' (they can repeat that 'Like' as many times as they like, and since the PUT is idempotent it has the same result no matter how many times it's performed). DELETE is also idempotent, so if an 'Unlike' operation is repeated many times for some reason then the system remains in a consistent state. Of course you can implement POST in this way, but if we use PUT and DELETE we can see that the rules associated with these verbs seem to fit our use-case really well.
I can also imagine another useful request:
GET /api/v1/resource/:id/likes/:userid
That would return details of a 'Like', such as the date it was made or the ordinal (i.e. 'This was the 50th like!').
case B is better, and here have a good sample from GitHub API.
Star a repo
PUT /user/starred/:owner/:repo
Unstar a repo
DELETE /user/starred/:owner/:repo
You are in effect defining a "like" resource, a fact that a user resource likes some other resource in your system. So in REST, you'll need to pick a resource name scheme that uniquely identifies this fact. I'd suggest (using songs as the example):
/like/user/{user-id}/song/{song-id}
Then PUT establishes a liking, and DELETE removes it. GET of course finds out if someone likes a particular song. And you could define GET /like/user/{user-id} to see a list of the songs a particular user likes, and GET /like/song/{song-id} to see a list of the users who like a particular song.
If you assume the user name is established by the existing session, as #joelittlejohn points out, and is not part of the like resource name, then you're violating REST's statelessness constraint and you lose some very important advantages. For instance, a user can only get their own likes, not their friends' likes. Also, it breaks HTTP caching, because one user's likes are indistinguishable from another's.