Given an object structure similar to this, how does one structure the API? Does the header, BookWishlists, have its own endpoint and the details, the WishlistEntries, are fetched seperately?
Also, how should the API be structure for the various types of WishlistEntries? Do we have one endpoint that accepts the "type" of entry to add?(POST /[EntryType]/[BaseBookId] as an example) Is it better to have a distinct endpoint for each type of entry?(POST /BookOnAmazon/[BookOnAmazon:Id])
A link to an api that does something like this would be appreciated as we've been unable to find one.
We're doing this in ASP.net Web API with a Phonegap/Javascript frontend if it's relevant.
Each "resource" is An endpoint, and I think your URL structure for the wishlists looks fine.
I don't think mapping database tables directly to resources is always the right thing to do. You would be better off putting yourself in the mindset of the consumer of your API. What will they need to ask for?
It looks to me like you only have 2 resources
wishlist
book
With book being something that is actually part of a wishlist. You may or may not want to return all the book details with a wishlist entry, or perhaps just and ID which the consumer can then request. The latter would require more development effort/requests by the consumer, but will probably be more efficient.
You can follow a thumbrule of /resources/{particularResourceId}/subresources/{oneSubresourceId}
So if you are not going to directly access books, I would think of only one main resource
/wishlists/ For getting a list of wishlists
/wishlists/{listId} for getting one particular wishlist
/wishlists/{listId}/entries/ for all wishlist entries
/wishlists/{listId}/entries/amazon/books for all books in Amazon wishlist entries.
You will logically get the URL formed in your mind.
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".
my REST API format:
http://example.com/api/v1.0/products - get all products
http://example.com/api/v1.0/products/3 - get product with id=3
Also, the products can be orginized into a product groups.
What is a proper way to get all product groups according to REST best practices:
http://example.com/api/v1.0/products/groups
or
http://example.com/api/v1.0/productgroups
...
another option ?
I can't agree with Rishabh Soni because http://example.com/api/v1.0/products/groups may lead to ambiguity.
I would put my money on http://example.com/api/v1.0/productgroups or even better http://example.com/api/v1.0/product_groups (better readability).
I've had similar discussion here: Updating RESTful resources against aggregate roots only
Question: About the thing of /products/features or /product-features,
is there any consensus on this? Do you know any good source to ensure
that it's not just a matter of taste?
Answer: I think this is misleading. I would expect to get all features
in all products rather than get all possible features. But, to be
honest, it’s hard to find any source talking directly about this
problem, but there is a bunch of articles where people don’t try to
create nested resources like /products/features, but do this
separately.
So, we can't be sure http://example.com/api/v1.0/products/groups will return all possible groups or just all groups that are connected with all existing products (what about a group that has not been connected with the product yet?).
To avoid this ambiguity, you can add some annotation in documentation. But you can just prepare http://example.com/api/v1.0/product_groups and all is clear.
If you are developing Rest API for your clients than you should not rely on id's. Instead build a meaningful abbreviation and map them to actual id on server side.
If that is not possible, instead of using
http://example.com/api/v1.0/products/3 you can use http://example.com/api/v1.0/products?product_id=3 and then you can provide "product_id" description in the documentation. basically telling the client ways to use product_id.
In short a url must be meaningful and follow a pattern.The variable part must be send by in the url query(part after ? or POST payload)
With this, method to querying the server is also important. If client is trying to get something to the server he should use "GET" http request, similar POST http request if it is uploading new info and "PUT" request if it is updating or creating a new resource.
So by this analogy http://example.com/api/v1.0/products/groups is more appropriate as it is following a pattern(groups in product) while productgroups is more like a keyword with no pattern.
A directory like pattern is more easier to understand. Like in file systems(C:\Program Files\WinRAR), every part gets us to more generalized target.
You can also customize this for specific group- http://example.com/api/v1.0/products/groups?id=3
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).
In designing a RESTful API, the following call gives us basic information on user 123 (first name, last name, etc):
/api/users/123
We have a lot of information on users so we make additional calls to get subresources on a user like their cart:
/api/users/123/cart
For an admin page we would like to see all the cart information for all the users. A big table listing each user and some details about their cart. Obviously we don't want to make a separate API call for each user (tons of requests). How would this be done using RESTful API patterns?
/api/carts/users came to mind but then you'd in theory have 2 ways to get a specific user's cart by going /api/carts/users/123.
This is generally solved by adding a deref capability to your REST server. Assuming the response from your user looks like:
{
...
cartId: "12345",
...
}
you could add a simple dereference by passing in the query string "&deref=cart" (or however you setup your syntax.)
This still leaves the problem of making a request per user. I'd posit there are two ways to generally do this. The first would be with a multiget type of resource (see [1] for an example). The problem with this approach is you must know all of the IDs and handle paging yourself. The second (which I believe is better) is to implement an index endpoint to your user resource. Indexing allows you to query a resource (generally via a query string such as firstName=X or whatever else you want to sort on.) Then you should implement basic paging so you're not passing around massive amounts of data. There are tons of examples of paging, but the simplest would be to specify a number (count=20) a start token (since=X) and a sort order (sort=-createdAt).
These implementations allow you to then ask for all users and their carts by iterating on the index endpoint. You might find this helpful as a starting point for paging [2].
[1] - How to construct a REST API that takes an array of id's for the resources
[2] - Pagination in a REST web application
For some reason I was under the assumption that having 2 URIs to the same resource was a bad thing. In my situation /api/users/123/cart and /api/carts/users/123 would return the same data. Through more research I've learned from countless sources that it's acceptable to have multiple URIs to the same resource if it makes sense to the end user.
In my case I probably wont expose /api/carts/users/123, but I'm planning on using /api/carts/users with some query parameters to return a subset of carts in the system. Similarly, I'm going to have /api/carts/orgs to search org carts.
A really good site I found with examples and clear explanations was the REST API Tutorial. I hope this helps others with planning their API URIs.
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.