Suppose I have a container which is a resource containing a set of resources, for example:
http://www.example.com/persons
returns all persons
Now if I have want that it doesn't return me all persons but only a subset meeting a certain criteria like http://www.example.com/persons?age=18
is such a URL adhears to the RESTFul convention ?
Related
I'm developing a web-api that manages a two level hierarchy objects:
Group -> SubGroup.
The group are added only by their names and it is a unique identifier for the group
The sub groups are added only by their names and the group name + sub group name is a unique identifier for the sub group.
The subgroup can "live" only in the context of its parent (the group).
Both the group and subgroup have unique ids in the system (besides the names).
The user should have an option to get a certain subgroup details and i'm uncertain if i should give him an endpoint that lets him access it directly.
I researched some threads by didn't get a good answer (1,2,3)
I have two options:
Option 1:
create an endpoint that lets the user to access subgroup only by specifying its name and its parent group name:
/groups/subgroups?groupName="x"&subGroupName="y"
Option 2:
create a "direct" access endpoint that lets the user access the subgroup directly without specifying the parent group name by using its internal id (In the subgroup creation return this id)
for example:
/subgroups?id="52regfd235fdsf325f" (the id of subgroup "y")
What is the best practice for this situation? is adding a "direct" access endpoint to a nested resource is fine or it should be avoided? what will be the case for a subgroup removal endpoint for example? should it be identified by the subgroup id or by its name?
In the general case, when we have H1->H2->H3->...Hn hierarchies. For trying to access the last resource in the chain, what will be a good rule of thumb here?
Is it RESTful to have HTTP GET /employees/1,2,3,4,5? Or should this be modeled as HTTP GET /employees?id=1,2,3,4,5
This question is more about URI design than REST.
A URI is intended to identify a particular resource in the server. While /employees identify a collection of employees, a URI like /employees/{id} should identify an employee with the given id in that collection. The slash expresses hierarchy.
If you want to get multiple resources from that collection (in fact, perform a query in that collection), it makes more sense to use a query parameter. Both approaches are valid:
GET /employees?id=1,2,3,4,5
GET /employees?id=1&id=2&id=3&id=4&id=5
I had a simiar requirement to print all the data starting an index and ending with an index, I used query params as below. You can try this..
You can use..
../../employees?start=1&end=5
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getInjectResources(#QueryParam("start") String start,
#QueryParam("end") String end){
String queryParams = "start: "+start+" end: "+end;
// Your logic here..
}
I must design an API to manage a Document entity: the originality of this entity is it can have two different ids:
id1 (number, i.e. 1234)
id2 (number, i.e. 89)
For each document, one and only one id is available (id1 or id2, not both)
Usually I solve this issue by using query parameters to perform some kind of "search" feature:
GET /documents?id1=1234
GET /documents?id2=89
But it works only if there is no sub-entity...
Let's say I want to get the authors of the documents :
GET /documents/1234/authors
Impossible because I can't know what type of id I get: is it id1 or id2 ?
GET /documents/authors?id1=1234
Not really REST I think because id1 then refers to the "Author" entity, not "Document" anymore...
GET /id1-documents/1234/authors
GET /id2-documents/1234/authors
Then you create two URIs that return the same entity (/author) not really REST compliant.
GET /documents/id1=1234/authors
GET /documents/id2=89/authors
It looks like a composite key created only for the API, it has no "backend" meaning. For me it sounds strange to create a "composite" key on the fly.
GET /document-authors?id1=1234
GET /document-authors?id2=89
In this case you completely lose the notion of tree... You end up with an API that contains only root entities.
Do you see another alternative ?
Which one looks the best ?
Thank you very much.
It seems to me that you're conflating two different resources here - documents and authors. A document has a relationship with an author, but they should be separate resources because the authors have existence from any individual document. With that in mind you need to ask whether your clients are searching for authors or documents. If it's authors, then they should be querying an authors API rather than a documents API.
e.g.For all the authors of documents with id1 89 or id1 1234 or id2 4444 you might query like this...
GET /authors?docId1=89&docId1=1234&docId2=4444
That should return a list of author representations. If people care about the documents themselves, the author representations could contain links to the documents.
Alternatively, if you're looking for documents then you should be querying that directly...
GET /documents?id1=89&id1=1234&id2=4444
What you're modelling as a sub-resource isn't really a subresource. It's a relationship between 2 independent resources and should be modelled as a set of links. Each document returned from the documents api should contain a set of authors links (if people really care about the authors) and vice versa from the authors to the documents.
Here's an opinionated solution from SlashDB, which allows for record filtering and traversing to related resources at the same time.
The example is similar to yours - two entities Artist and Album.
Let's identify the Artist first.
Artist by ID:
https://demo.slashdb.com/db/Chinook/Artist/ArtistId/2
Artist by Name:
https://demo.slashdb.com/db/Chinook/Artist/Name/Accept
An Artist may have issued Albums. The two entities are related. We allow extending the URL with the name of the related entity, like so:
https://demo.slashdb.com/db/Chinook/Artist/Name/Accept/Album
You can keep "going", say to get to the Tracks from those albums
https://demo.slashdb.com/db/Chinook/Artist/Name/Accept/Album/Track
And even continue filtering too i.e. only tracks, which are shorter than 300000 milliseconds:
https://demo.slashdb.com/db/Chinook/Artist/Name/Accept/Album/Track/Milliseconds/..300000
I'm dealing with tree-structred resources. Every item resource has children. Each child type may be content resource or subject resource - which is determind by the type_id. (There could be more children types in the future).
What URI should be used for creating a new child for an item?
POST /api/items/<item_id>/children
(deliver the type_id via the JSON)
OR:
POST /api/items/<item_id>/children/contents
POST /api/items/<item_id>/children/subjects
OR: redirection according to the type_id to:
POST /api/contents
POST /api/subjects
And then using the GUID of the new resource for creating the hierarchy connection.
Thanks!
If your children have an attribute named type which can be subjects or contents, you can treat that as any other attribute, e.g. a gender that can be male or female.
Ideally, you would create a new child with
POST /api/items/<item_id>/children
{
"some_value": 50
"type": "subject",
}
or
POST /api/items/<item_id>/children
{
"some_value": 134
"type": "content",
}
No need to make a confusing endpoint for a simple attribute. If you'd do this for the type attribute, you might as well do it for all the other attributes as well, leading to a great many more endpoints pointing to basically the same resources, that is not something you want.
Later you can fetch them by type, for example getting all subjects would be
GET /api/items/<item_id>/children?type=subject
The typical REST endpoints service looks like this for an entity.
GET /products (list)
GET /products/:id (detail of a specific product)
POST /products (insert)
PUT /products/:id (update)
DELETE /products/:id (delete)
but if I've a requirement to return some custom result, for example
SELECT MAX(lastModified) FROM product
How would you form a REST request (method + URL) based on above or similar custom results?
If you are only interested in the "lastModifed" product from your list of products (i.e. the product with MAX(lastModified)) then:
GET /products/lastModified