So I've been reading a lot on RESTfull design - specifically dealing with resources.
Taking the canonical example of Users, Posts, and Comments, with relationships as:
Users ---(hasMany)---> Post ---(hasMany)---> Comment
One may initially think to expose something like:
GET /users GET /posts GET /comments
POST /users POST /posts POST /comments
GET /users/id GET /posts/id GET /comments/id
PUT /users/id PUT /posts/id PUT /comments/id
DELETE /users/id DELETE /posts/id DELETE /comments/id
But then, say I want all Comments of a certain Post made by a particular User. I'd need to do something like:
GET /users/id
> someUser
> var postIds = someUser.posts()
GET /posts?id=<postIds[0]>&id=<postIds[1]>&...
> somePosts
> **application user inspects posts to see which one they care about**
> var postOfInterest = somePosts[x];
> var postId = postOfInterest.id;
GET /comments?id=postId
> someComments (finally)
Suppose though I only care about a Post or Comment in the context of it's owner. Suppose a different resource structuring which may (or may not?) be more natural:
GET /users
POST /users
GET /users/id
PUT /users/id
DELETE /users/id
GET /users/id/posts
POST /users/id/posts
GET /users/id/posts/id
PUT /users/id/posts/id
DELETE /users/id/posts/id
GET /users/id/posts/id/comments
POST /users/id/posts/id/comments
GET /users/id/posts/id/comments/id
GET /users/id/posts/id/comments/id
GET /users/id/posts/id/comments/id
Which to me, is probably a better representation of what the resources are. Then all I need is:
GET /users/id/posts
> somePosts
> **application user inspects posts to see which one they care about**
> var postOfInterest = somePosts[x];
> var postId = postOfInterest.id;
GET /users/id/posts/postId/comments
> someComments
This just seems more like navigating a file system than the previous method - but I don't know if its RESTfull at all (perhaps this is what REST was trying to get rid of) because in order to access a Comments resource, I need to know which User and which Post it belongs to. But the former requires 3 requests, while the latter requires just 2.
Thoughts?
Quite a bit of what is good REST is opinion but I would say your second approach is generally more "RESTful".
Basically you do want hierarchy in REST API and filesystem like navigation instead of query parameters. This is especially so if you follow HATEOS like API as someone can navigate your API.
In your second example it's important to have both GET /users/id and GET /users/id/posts so that when a request for the user's info is made it doesn't include all it's posts (or their IDs) too. And the second request will return their posts too. Often users have thousands of posts in a forum.
The disadvantage is that the api user always has to know the author of the post for which it wants to get comments. He'd essentially make a "give me that user and give me his/hers posts" request to your server which means that your server will make a query for that user and then select his posts. Instead it's much more convenient for both your user and your server to have separate requests - "give me that user", "give me that post" and "give me that comment". This means that you have to store separately users, posts and comments and for each post/comment store the id of it's author so that you can make selection of posts/comments by their author ("give me posts by this user", or simply "give me this post")
I would personally go with this variant of requests
GET user
GET post
GET comment
...
For every request I'd implement a where clause which will give the user of my api more options to make a specific selection. For example GET posts where userId='myID'. It can be implemented with url query parameters like http://myapi.mydomain.com/post?userId=user1 or inside the header. It will return a list of posts for that user. You can also have where clause for the post's ID - http://myapi.mydomain.com/post?id=123 which will return only this post. Note that for the first case - when you fetch a list of posts - you can only return some kind of short info for the posts (like id, author, summary...) and require an additional request to post?id=id for the full post content.
Having this implementation would give you at least two advantages:
the user of the api needs to know only one id to get some info - postID to get a post's content/comments, userId to get all posts/comments for that user
the selection is done on the server so less data is transfered over the network meaning faster responses (and potentially less costs for final users if they are on a mobile plan or something)
In my opinion this implementation giveŃ you loosely coupled objects (user, post, comment) and more flexible queries
Related
I have the following functionalities in my API:
Getting a user by their name
Getting a user by their ID
Getting a user, or if it doesn't exist create one
Getting multiple users by their ID
Currently I'm handling the two first functionalities with a GET request, and the third with a POST request. I could use a GET request for getting multiple users, but sending potentially hundreds of IDs through a query parameter seems like the wrong approach, as I would get a very long URL. I could also use a POST request to send the long list of IDs through its body, but I doubt a POST request is meant for this purpose.
What method would be appropriate to use for the last one?
I see 2 possible ways here:
Get all users and do your filtering post response.
Get a range of IDs, which resumes to only 2 parameters, the lower and the upper limits of the interval. (If this satisfy your needs)
Behaving the way you described and avoiding long URLs in the same time will not work together.
My data model is like this:
User: id, email, hashed_password
Item: id, name, color
UserItem: user_id, item_id, rating
and I want to write a RESTful API to get these resources. The authentication is provided via OAuth 2 with a JWT token (that contains the logged user id).
First approach
Endpoints
The first idea for a URL structure is (the one I chose when there was still no authentication):
/items/{item_id}
/users/{user_id}
/users/{user_id}/items/{item_id}
In this case a user with id 1 could use:
GET /users/1 to get their own information;
GET /users/1/items to get their own items (with rating);
GET /items to get all items that they could add to their collection.
Analysis
I think this solution is quite clear, but also unelegant.
Good:
You can easily get other users info (if they are available to them);
1-to-1 relations between endpoints and data models.
Bad:
Longer URLs;
There is redundancy (why GET /users/1/items when in the token you already have the information about id 1?).
Second approach
Endpoints
Given that you can extract the user id from the token, the structure could as well be more simple:
/items/{item_id}
/users/{user_id}
In this case a user with id 1 could use:
GET /users/me to get their own information;
GET /items?class=owned to get their own items (with rating);
GET /items?class=all to get all items that they could add to their collection.
Analysis
This solution is a bit messy but probably more elegant.
Good:
Shorter URLs;
Less redundancy (GET /items to get your own items).
Bad:
Only model UserItem is represented (even though in this case it is probably almost meaningless to get an Item without its rating, that could be set to null if the user has not yet added it);
Not straightforward to get other users' items (maybe something like GET /items?user=3?).
Conclusions
Honestly I don't know what is the best practice in this case. I feel like there is something off about both of these. Maybe there is an hybrid approach I'm not seeing?
How would you organize a model like this?
You could look into a format like HAL. HAL gives you a way to describe specific resources (items) and allows you to create multiple collections that point to those resources.
This means that individual items could be hosted at /items/xyz, but items can be both part of the /user/a/items and /items collections.
I put a lot of work into a hypermedia client: https://github.com/badgateway/ketting . This is not just an ad though, there's alternatives but that approach of API design might we well-suited for you.
But regardless of the client you're using, systems like this can avoid the issue of retrieving the same item through multiple endpoints. A single item has a canonical url, and if the system is designed well you only have to retrieve an item once.
A collection is just a list of links to the resources (items) that belong to that collection. They point to the item, but don't 'contain it', just like a regular hyperlink.
OK, lets assume we have two entities: Profile, consisting of id, name and ~10 irrelevant fields, and Post, consisting of text, title, and it's author (Profile). Also, there is resource /feed that returns feed containing posts from different profiles.
So I have two options:
Send full Profile entity in author
Send author's id (there is a way to request Profiles separately)
Which way is faster (in terms on using it on front end) and more convenient (RESTy, if you like).
Obviously just sending the id of Profile is faster because the response length is smaller.
The important question, however, do you need the full Profile object with each Post? If you want to, for example, print out the name of the author for each Post then sending the full object makes more sense. But if you want to just supply a link to the author with each Post (in the front end) then the id should be enough.
For other services that query yours for Posts just send the id and have them make a second call if they need. They can always cache the data on their end if needed.
Try to build your service so that each call/endpoint returns the bare minimum amount of data needed to make sense of the response. This might mean that a Post contains a lean Profile object where only the name is included but all the other, "irrelevant", fields are excluded. But when you query a Profile directly, you get the full object.
You can also have an optional query parameter where the caller can specify whether they want just the id or the full Profile, this is a strategy Atlassian JIRA uses to preserve bandwidth and improve speed.
Also check out the hal+json specification, it can give you good ideas about how to design a more usable and transparent REST service.
MOST IMPORTANT! Your endpoints should only return data that the outside world can actually use and make sense of. So that means if Profile has a field/fields which values are only used in your back-end (like, for example, the user's password) then you should never leak those out.
I'm tring to create a little registration app, that will be published as a REST API. It will allow users / systems to create an account on my system. They will pass in a username and a password.
According to the rules that i've read, the "/" in a URI must be used to indicate a hierarchical relationship.
So I'm wondering if this type of a URI follows the rules, and is intuitive enough:
POST http://myregistrations.com/users/user/{user_id},{password}
GET http://myregistrations.com/users/user/{user_id}
PUT http://myregistrations.com/users/user/{user_id},{password}
DELETE http://myregistrations.com/users/user/{user_id}
or should i remove the "users" part from the URI? In that case, if I want to get "all" users, i could just do the following:
GET http://myregistrations.com/user/
DELETE http://myregistrations.com/user/ ** i might not allow this but just for discussion purposes...
And the rest of the methods would look like this:
POST http://myregistrations.com/user/{user_id},{password}
GET http://myregistrations.com/user/{user_id}
PUT http://myregistrations.com/user/{user_id},{password}
DELETE http://myregistrations.com/user/{user_id}
I don't want to over complicate things... but I also want to make sure I follow conventions.
I am leaning towards the second example, where I don't have "users". Given that each part of the URI should map to an addressable resource, and that I won't be allowing batch updates to accounts, having a concept of "users" seems useless at this time. I might be missing something though.
Maybe I'm just splitting hairs. I've been reading other posts here too about defining REST APIs... but I haven't found anything yet.
Thanks.
You can have both concepts (user and users) with a single API. The URI /users/user seems convoluted to me.
Over-simplified example:
Post one user:
POST /user/{user_id,password}
Post multiple users:
POST /user/[{user_id,password},{user_id,password}]
Get one user:
GET /user/{user_id}
Response: {user_name} or [{user_name}]
Get multiple users:
GET /user/{search_user_name}
Response: [{user_name},{user_name},{user_name}]
Typically plural is how you reference a resource so users in this case and not user. This is what you're routes should look like to follow the REST pattern.
POST http://myregistrations.com/users/ --> The post payload should contain: {user_id},{password}
GET http://myregistrations.com/users/{user_id} --> The user_id is in the URL
PUT http://myregistrations.com/users/{user_id} --> The user_id is in the URL
DELETE http://myregistrations.com/users/{user_id} --> The user_id is in the url
I don't want to over complicate things...
I have bad news for you, you've already overcomplicated things. The REST clients know nothing about the URI structure, because they follow hyperlinks annotated with semantic metadata. The clients check this metadata instead of the URI structure.
I am designing a REST API for inserting a record to the "solutions" table. A "solution" has a solverID, problemID. I have two different designs in mind:
POST /solutions
and passing the solverID and problemID in JSON with the content of the solution. Or putting the solverID and problemID in the URI:
POST /users/:solver_id/problems/:problem_id/solutions
Which design is better?
It's a good practice to define your resources in a consistent hierarchy, so that they are easily understandable and predictable.
Let's say this is the URL to retrieve a question -
GET /users/{solverId}/problems/{problemId}
It clearly conveys that the problem belongs to the {solverId}.
The following URL would clearly show that the we are retrieving all solutions for problems solved by {solverId}
GET /users/{solverId}/problems/{problemId}/solutions
To create a new solution for the {problemId}, you would do a post on
POST /users/{solverId}/problems/{problemId}/solutions
To retrieve a particular solution you would do a get on
GET /users/{solverId}/problems/{problemId}/solutions/{solutionId}
When to use Ids in path vs query ?
If an ID is definitely required to identify a resource, use it in the path. In the above scenario, since all three Ids are required to uniquely identify a solution, all of them should be in the path.
Let's say you want to retrieve a solution that was given in a particular date range, you would use the following
GET /users/{solverId}/problems/{problemId}/solutions?startDate={}&endDate={}
Here startDate and endDate cannot uniquely identify a resource, they are just parameters that are being used to filter the results.
Go with the first one. I would keep your urls as clean and simple as you can. Here are some other examples off the top my head. Not sure on your entire structure.
POST /solutions
GET /solutions?solverid=123 //query solutions by user
GET /users/555/problems // problems for a given user
GET /users/555/solutions // solutions for a given user
GET /problems/987/solutions // solutions for a given problem
I came up with a scheme: including user ID in the route only when authentication is not needed for the route, otherwise, the user ID can be figured out from the authentication information, and the above route becomes:
POST /problems/:problem_id/solutions