There are products and reviews of products.
So, I can get product list like this...
GET /products
also I can fetch one product or reviews or the product like this...
GET /products/{productID}
GET /products/{productID}/reviews
Those are clear. but the problem happens when I want to get all reviews I wrote.
I could create uri like this..
1. GET /products/reviews?author=myId
or
2. GET /reviews?author=myID
However, the problem of first one comes from conflict between {productID} and reviews.
the problem of next one comes from relation products and reviews because reviews should be under the products according to the hierarchy.
How can I get all reviews I wrote with RESTful API?
usually the route is like this
GET /products/:product_id/reviews?author=myID
so the parameters are
- product_id
- author
with that, you will get all the reviews for a certain product and author.
Though, if you want all of your reviews, then this is correct:
GET /reviews?author=myID
To get all reviews written by a given author for all products, a RESTful way to do this would be
GET /authors/{authorId}/reviews
If you want to get all reviews written by a given author for a single product, then you could use
(a) GET /authors/{authorId}/products/{productId}/reviews
I like this approach over specifying productId or authorId in the query string. Authors contain products, and products contain reviews, so this is a nice RESTful URL. But if you want to use query string, then you could use either of the following:
(b) GET /products/{productId}/reviews?authorId={authorId}
(c) GET /authors/{authorId}/reviews?productId={productId}
But I personally prefer option (a) over options (b) or (c). I think it describes the object hierarchy in a cleaner way.
I would use the second one if I were you, since flat URIs are usually shorter than hierarchical ones and shorter URIs are easier to route: it is harder to mistype the URI template accidentally.
So using
GET /reviews?author={myID}
GET /reviews?product={productID}
is ok too. (If you have different review types, maybe it is better to use product-reviews instead of just reviews.)
Just to avoid misunderstandings REST does not have URI structure constraints, those are implementation details, so you can use both your proposed URIs. You should document them and apply the HATEOAS constraint.
Related
I'm thinking about a REST API design. There are several tables in my database. For example Customer and Order.
Of course - each Order has its Customer (and every customer can have many Orders).
I've decided to provide such an interface
/api/v1/Customers/ -- get list of Customers, add new Customer
/api/v1/Customers/:id: -- get Customer with id=:id:
/api/v1/Orders/ -- get list of Orders, add new Order
/api/v1/Orders/:id: -- get Order with id=:id:
It works flawlessly. But my frontend has to display a list of orders with customer names. With this interface, I will have to make a single call to /api/v1/Orders/ and then another call to /api/v1/Customer/:id: for each record from the previous call. Or perform two calls to /api/v1/Orders/ and /api/v1/Customers/ and combine them on the frontend side.
It looks like overkill, this kind of operation should be done at the database level. But how can/should I provide an appropriate interface?
/api/v1/OrdersWithCustomers
/api/v1/OrdersWithCustomers/:id:
Seems weir. Is it a right way to go
There's no rule that says you cannot "extend" the data being returned from a REST API call. So instead of returning "just" the Order entity (as stored in the backend), you could of course return an OrderResponseDTO which includes all (revelant) fields of the Order entity - plus some from the Customer entity that might are relevant in your use case.
The data model for your REST API does not have to be an exact 1:1 match to your underlying database schema - it does give you the freedom to leave out some fields, or add some additional information that the consumers of your API will find helpful.
Great question, and any API design will tend to hit pragmatic reality at some point like this.
One option is to include a larger object graph for each resource (ie include the customer linked to each order) but use filter query parameters to allow users to specify what properties they require or don't require.
Personally I think that request parameters on a restful GET are fine for either search semantics when retrieving a list of resources, or filtering what is presented for each resource as in this case
Another option for your use case might be to look into a GraphQL approach.
How would you do it on the web?
You've got a web site, and that website serves documents about Customers, and documents about Orders. But your clients aren't happy, because its too much boring, mistake-prone work to aggregate information in the two kinds of documents.
Can we please have a document, they ask, with the boring work already done?
And so you generate a bunch of these new reports, and stick them on your web server, and create links to make it easier to navigate between related documents. TA-DA.
A "REST-API" is a facade that makes your information look and act like a web site. The fact that you are generating your representations from a database is an implementation details, deliberately hidden behind the "uniform interface".
I have to put a (read-only) REST service atop of an existing product database. The easy part is having a top level product resource, like:
/api/products/
Now, actually callers of this service will rather need to get their relevant products based on the ID of a store and of a specific process (like "retail"). Behind the scenes, the combination of those two values results in a configured subset of products. This must be transparent for the caller, it should not need to know about these "product portfolios".
So I thought about designing the URI like this, where 1234 is the StoreID and retail is the process:
/api/stores/1234/retail/products
The first question that comes up here is if I should return full products here or URIs to their individual resources on /api/products/ ... the pro would be clearly that the caller does not need to retrieve each individual product from /api/products, the con would be that this would cause a caching headache on the /api/stores/1234/retail/products URI.
To complicate things, those products of course also have prices. Also here, a product does not have one price, but multiple ones that is also dependent of the StoreID and the Process, besides other factors. In reality, prices are direct children of products, so:
/api/products/ABCD/prices
would be the obvious choice, but again, as StoreID and Process are relevant to pre-filter the prices, an URI like:
/api/stores/1234/retail/products/ABCD/prices
would be more appropriate.
At the same time, there are other subresources of products that will not make sense to have under this URI, like product details. Those would clearly only make sense directly under /api/products/ABCD/details as they are not dependant on the store or process.
But this looks somehow messy to me. But at the same time, solving this by only having queryparam filters to solve it directly on the product resource, is not much nicer and does not enforce the caller to provide both, StoreId and process:
/api/products?store=1234&process=retail
/api/products/ABCD/prices?store=1234&process=retail
Even more, process or storeid does not have anything to do with the product, so querying for it directly on product seems odd. For prices, it would make sense, though.
So my question is: is there a good way to solve this that i don't see? And: would you recommend returning full products when they are a subresource - and what do you think about handling (HTTP) caching when doing that?
The first question that comes up here is if I should return full
products here or URIs to their individual resources on /api/products/
[...] the con would be that this
would cause a caching headache on the /api/stores/1234/retail/products
URI.
I would definitely return the full products - imagine the amount the client would have to do if it would simply want to display a list of product names. Ideally this endpoint would be paginated (query string can include something like &pageSize=10&pageNumber=2, for example).
Also there are various caching solutions around this - for example you can cache all the products in a data structure service like Redis.
To complicate things, those products of course also have prices [...]
and details subresource.
Looking at the Richardson Maturity Model level 3, this would be where links come into play, and you could have something like this under a product resource:
<link rel = "/linkrels/products/ABCD/prices"
uri = "/products/ABCD/prices?store=1234&process=retail"/>
and another similar link for the product details resource.
#Roman is right, REST is meant to be discoverable, clients should simply follow links (that can have long/ugly uris), instead of having to memorize them (like in SOAP, for example).
Your backend has two Models:
One Company to Many Employees.
You want to accomplish the following:
Get all Companies
Get a Company by ID
Get all Employees for a Company
Get all Employees
Get a Employee by ID
What is the best practice for handling the REST URLs when your models have 1:M relationships? This is what I have thought of so far:
/companies/
/companies/<company_id>/
/companies/<company_id>/employees/
/employees/
/employees/id/<employee_id>/
Now let's pretend One Company has Many Models. What is the best name to use for "Adding an employee to a Company" ? I can think of several alternatives:
Using GET:
/companies/<company_id>/add-employee/<employee_id>/
/employees/<employee_id/add-company/<company_id>/
Using POST:
/companies/add-employee/
/employees/add-company/
The URIs look fine to me, except maybe the last one, that does not need an additional "id" in the path. Also, I prefer singular forms of words, but that is just me perhaps:
/company/
/company/<company_id>/
/company/<company_id>/employee/
/employee/
/employee/<employee_id>/
The URIs do not matter that much actually, and can be changed at any point later in time when done properly. That is, all the URIs are linked to, instead of hardcoded into the client.
As far as adding an employee, I would perhaps use the same URIs defined above, and the PUT method:
PUT /employee/123
With some representation of an employee. I would prefer the PUT because it is idempotent. This means, if the operation seems to fail (timeout, network error occurs, whatever) the operation can be repeated without checking whether the previous one "really" failed on the server or not. The PUT requires some additional work on the server side, and some additional work to properly link to (such as forms), but offers a more robust design.
As an alternative you can use
POST /employee
With the employee representation as body. This does not offer any guarantees, but it is easier to implement.
Do not use GET to add an employee (or anything for that matter). This would go against the HTTP Specification for the GET method, which states that it should be a pure information retrieval method.
I am trying to understand which should be the correct REST approach to name some of a e-commerce style endpoint.
If I am not mistaken, getting a list of products and the details of one of each will end up with two GET endpoints as
A) GET /products
B) GET /products/id
(I deliberately skipped pagination issues)
If I am looking at the list of shops which sell a specific product I can specify and endpoint as such
C) GET /products/id/shops
I struggle to understand though what happens if I need to specify more than one product for shop reserach.
Can the above endpoint be expanded to take multiple parameters or this is somehow discouraged?
In other words, should I be looking into something like
1) GET /products/id1,id2,id3/shops
2) GET /products/id1/shops [id2,id3]
or rather a completely new
3) GET /shops [id1,id2,id3]
?
Notes
Unanswered question in SO seems to underline that this is a sort of an untold story in RESTful services... :)
My current source of reference
As referred in many SO answers, as for example here, the URI does not make the service RESTful.
I agree with it, so to extend the concept a little my point above is that the implementation of the a service like 1) above may be (and in my case is, for server implementation details) different from easily combining the result given by 3 endpoints in the form of C).
In a more general sense, the implementation of such a combination could be kept internal.
Thus, yes the URI does not make the service RESTful but it would be nice to extend the cleaniness and expresiveness of the C) form for multiple ids.
Edit
In response to the correct note by Lutz in answer, shops may be trated as resources on their own.
What if, I came out with this not so clever example, the subresource does not really exists on its own, as for example the free places for 2 movies in a cinema where
GET /movies/12,14/places
where
GET /places?movies=12,14
is obviously feasible but not that RESTful imho.
I agree with the answer given by Lutz Horn. Furthermore, I'm not sure why you would think that using the query string in a GET request is "not that RESTful".
To quote from O'Reilly's RESTful Web Services, pg 233, under the header URI Design (emphasis mine):
Use punctuation characters to separate multiple pieces of data at the same level of hierarchy. Use commas when the order of the items matters, as it does in latitude and longitude: /Earth/37.0,-95.2. Use semicolons when the order doesn't matter: /color-blends/red;blue.
Use query variables only to suggest arguments being plugged into an algorithm, or when the other two techniques fail. If two URIs differ only in their query variables, it implies that they're the different sets of inputs into the same underlying algorithm.
This should give you sufficient direction in determining how to construct your routes. While you could easily construct it thusly (using semicolons, because it doesn't seem that order matters here):
GET /products/id1;id2;id3/shops
You could just as easily write it this way:
GET /shops?productIds=id1,id2,id3
And this might be a more reasonable approach given that what you indicate you are doing is searching for all shops that carry a particular item, and search is an algorithm for which product ids are input parameters.
To your example of movies (if I am understanding it correctly), if you are looking for places (our resource) where the movie (or movies) is showing AND seating is available:
GET /places?movie=id1,id2,id3&availability=true
It looks like you just have additional search parameters. If I am misunderstanding your "subresource may not exist" comment, please clarify this for us so that we can address it properly.
I would make shops a separate resource.
GET /shops lists all shops
GET /shops/123 gets the details of shop 123
GET /shops?sellsProduct=id1,id2,id3 lists all shop that sell the products
In URLs like /products/id/shops the shops are a subresource of a product. But since a shop can exist independently of any product, this makes not much sense.
GET https://api.website.com/v1/project/employee;company-id={company-id},
title={title-id}?non-smoker={true|false}&<name1>=<value1>&<name2>=<value2>&<name3>=<value3>
where:
company-id is mandatory,
title is optional
name/value can be any filter criteria.
Is there a better way to define the interface?
This API is not supposed to create an employee object. It is for getting an array of employee objects that belongs to a particular company and has a particular title and the other filter criteria.
I don't know if there is a better way, because it depends often on the technology you use and its idioms.
However, here is two different URI designs that I like (and why)
#1 GET https://api.website.com/v1/project/employee/{company-id}?title={title-id}&non-smoker={true|false}&<name1>=<value1>&<name2>=<value2>&<name3>=<value3>
#2 GET https://api.website.com/v1/project/company/{company-id}/employee?title={title-id}&non-smoker={true|false}&<name1>=<value1>&<name2>=<value2>&<name3>=<value3>
As you can see in both example I extracted company-id from the query string. I prefer to add mandatory parameters in the path info to distinguish them. Then, in the second URI, the employee ressource is nested in the company. That way you can easily guess that you can retrieve all employee from a specific company, which is not obvious in the first example.
This api is supposed to GET employee objects that satisfy the given criteria of belonging to a particular company, having particular job title and some other filter criteria.
Personally I would just design your URI as http://acme.com/employee/?company=X&title=Y&non-smoker=Z&T=U. I wouldn't write "in stone" that the company is mandatory: your API will be easier to change.
However, you should consider that few "big" requests are far faster than plenty of small ones. Moreover, URI representations can be effectively cached. Therefore it is often better to have URIs based on IDs (since there are more chances that they will be asked again).
So you could get the complete employee list of a company (plus other data about the company itself) with http://acme.com/company/X and then filter it client-side.
Are you creating a new employee object? If so then a POST (create) is more appropriate. A good clue is all the data you're pushing in the URL. All that should be in the body of the POST object.