I’m trying to develop a online shopping web service with RESTful. However, I’m so new about this that I don’t know how to design it.
Here is my model:
User: includes name, email, phone number …etc
Product:includes its name, its tags and what category it belongs to. (I designed two types of Category: MainCategory and SubCategory, where SubCategory belongs to MainCategory).
Now I have the following needs about Product:
List all products under MainCategory M
List all products under MainCategory M and SubCategory S
List all products belongs to a user A
CRUD with the product with product_id
Here is my question about the situation above:
List all products under MainCategory M
should I use /products/M or /MainCategory/M to list all the products?
List all products under MainCategory M and SubCategory S
should I use /products/M/S or other ways?
List all products belongs to a user A
should I use /user/A/products or /products/?user=A or other ways?
CRUD with the product with pid(product_id)
is this correct? /product/pid
Besides, what to do if I need to sort or add some restriction?
For example:
List all products under MainCategory M, order by time
is this /products/M?order=time_asc ?
Thanks for any help!
First of all, you've got your principal resources:
/users/{id}
/products/{id}
Relationships between these two should be by links, as it makes total sense to think about either without reference to the other (users aren't owned by products, or vice versa). Use normal CRUD techniques for dealing with these resources, and consider sub-resources for complex attributes.
Then you've got the categorization system. This is also nicely modeled as a resource:
/category/{name}
/category/{name}/{subname}
A key difference here is that you don't need to support any operation other than GET on categories; the information there (a list of the members of the category) is really synthesized from the definitive information on the products. Where you wish to allow people to restrict the set of information returned by the listing (e.g., a limited range, a different ordering) that is done through parameters on the GET. The list of category members should really be a list of links to the relevant product resources.
If you support user categorization, put that as a separate resource and take care to add appropriate security restrictions; only authorized people should be able to see that sort of thing.
Related
From Microsoft's API design guidance(https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design#more-information):
In more complex systems, it can be tempting to provide URIs that enable a client to navigate through several levels of relationships, such as /customers/1/orders/99/products. However, this level of complexity can be difficult to maintain and is inflexible if the relationships between resources change in the future. Instead, try to keep URIs relatively simple. Once an application has a reference to a resource, it should be possible to use this reference to find items related to that resource. The preceding query can be replaced with the URI /customers/1/orders to find all the orders for customer 1, and then /orders/99/products to find the products in this order.
Avoid requiring resource URIs more complex than collection/item/collection.
From Microsoft's example, let's say I want to find all the products of customer 1. Then I would need to first query /customers/1/orders to find all the orders then query individual orders by /orders/{id}/products which falls into N+1 problem. Also, If I want to create a new order, should I POST to /customers/1/order or /orders with customer_id?
//2 endpoints
/customers/1/orders
/orders/{id}/products //for n orders
Or I could build all APIs with 1 depth and search for all the products by /products/?customer_id=1
//3 endpoints
/customers
/orders
/products
To sum up,
which would be better approach? nested vs 1depth but more endpoint
If nested is better, with microsoft's example, if I want to create a new order for customer 1, should i POST to /customers/1/orders or /orders with customer_id in body or support both?
Both approaches are okay from a REST API design perspective. You should design it according to your use case and to increase the developer experience:
If it's more intuitive to create an order in a customer context, choose a nested approach.
If it's more intuitive to create an order and send the customer id as an attribute of the order object, choose the other approach.
You can even serve both approaches, as the API is just an interface to communicate with your service (just like a GUI, but for m2m purposes).
I have a website providing a ranking (based on reviews) of other websites that require a membership to access them (ex. Online tool requiring a monthly membership).
I have 2 question related to this:
Should I use the Product or WebSite entity? At the moment I opted for Product since it has the price attribute and so on.
How can I describe my ranking of website? I'm looking for a kind of ordered list of products (or websites if I have to change that).
With the offers property, you can directly offer the WebSite (or WebApplication, which might be more appropriate in your case). No need for Product.
For the ranking, you have several options:
With the review property you can specify a Review, which can have the reviewRating property to specify a Rating. This allows you to provide specific scores (e.g., in the form of "1.27", "1.28", "2.04" …).
If the rank is the average rating from all reviews, you can use the aggregateRating property to specify an AggregateRating.
With an ItemList you could specify for each entry a ListItem with the position property. This allows you to (implicitly) provide a ranking (i.e., in the form of "1", "2", "3", …).
You could also use both ways (Rating/AggregateRating and ItemList).
Make sure to apply the review/aggregateRating on the correct item: on the Offer if you are ranking the offers, on the WebApplication if you are ranking the apps.
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).
I am currently trying to decide on the best approach to solve a problem I am having with designing my REST API.
The simplified scenario is my web application has two resources for example departments and employees. Both are security controlled within the business layer.
A user can exist who has access to employee but not to department, however when this user edits an employee they need to be able to select that employee's department from a drop down list (similarly they might have a list of employees that they want to filter by department).
Ordinarily that user would not have access to the department object so wouldn't be able to call /department/ for example but in the case of editing an employee they need the list of departments.
What would be the recommended way of dealing with this, would I return a list of departments on each GET of /employee/ or would I create another resource which was a combination of employee and department objects (department being the full list of departments)?
I can't currently change the security on the objects as this is deeply ingrained in the application logic.
Has anybody got any ideas?
Regards,
Gary
Create a new resource called something like 'DepartmentList'
Note: I think plural names are better.
You have to think of what would make the life of your users (devs) easier.
A combined resource would 'pollute' your api. Your api would expose /employees, /departments and /employeeDepartments. I don't think the latter deserves to be that high in the hierarchy.
It'd be also be a little more complex for your users to use:
"To edit an employee you need to set a department, BUT that department is not always available at /department, so you better get it from employeeDepartments ... "
Think of your employee object: GET /employees/123
employee:{
name: John,
...
department: {
id: ID
--a subset of data--
}
}
The subset of data should be enough to operate for Users with no rights, and Users with right access may operate on /departments/ID.
Now, how to get the list of available options?
I use to provide a 'special' action /new where I provide a 'form' which users can use as a template to post and create a new resource. This is not an adopted Rest 'standard' but is HATEOAS friendly - it really helps to the discoverability of your api.
So, GET /employees/new could print
employee:{
name: "",
...
department: [{ id: 1, --subset of data-- },{ id: 2, --subset of data-- }.. ]
}
There is some convention to be taken on the format (e.g: user needs to know that it only has to pick one department). But that's a hole new discussion.
Lets assume I have an object that I expose as a REST resource in my application. This object has many fields and contains many other objects including associated collections. Something like this, but think MUCH bigger:
Customer
List<Order> orders
List<Address> shippingAddresses;
// other fields for name, etc.
Order
List<Product> products
// fields for total, tax, shipping, etc.
Product
// fields for name, UPC, description, etc.
I expose the customer in my api as /customer/{id}
Some of my clients will want all of the details for every product in each order. If I follow HATEOAS I could supply a link to get the product details. That would lead to n+1 calls to the service to populate the products within the orders for the customer. On the other hand, if I always populate it then many clients receive a bunch of information they don't need and I do a ton of database lookups that aren't needful.
How do I allow for a customer representation of my resource based on the needs of the client?
I see a few options.
Use Jackson's JsonView annotation to specify in advance what is used. The caller asks for a view appropriate to them. i.e. /customer/{id}?view=withProducts. This would require me to specify all available views at compile time and would not be all that flexible.
Allow the caller to ask for certain fields to be populated in the request, i.e. /customer/{id}?fields=orders,firstName,lastName. This would require me to have some handler that could parse the fields parameter and probably use reflection to populate stuff. Sounds super messy to me. The what do you do about sub-resources. Could I do fields=orders.products.upc and join into the collection that way? Sounds like I'm trying to write hibernate on top of REST or something.
Follow HATEOAS and require the client to make a million HTTP calls in order to populate what they need. This would work great for those that don't want to populate the item most of the time, but gets expensive for someone that is attempting to show a summary of order details or something like that.
Have separate resources for each view...
Other?
I would do something like this:
/customers/{id}/orders/?include=entities
Which is a kind of a more specific variation of your option 1.
You would also have the following options:
Specific order from a specific customer without list of products:
/customers/{id}/orders/{id}
Just the orders of a customer without products:
/customers/{id}/orders/
I tend to avoid singular resources, because most of the time or eventually someone always wants a list of things.
Option 2 (client specifies fields) is a filtering approach, and acts more like a query interface than a GETable resource. Your filter could be more expressive if you accept a partial template in a POST request that your service will populate. But that's complicated.
I'm willing to bet all you need is 2 simple representations of any complex entity. That should handle 99.9% of the cases in your domain. Given that, make a few more URIs, one for each "view" of things.
To handle the 0.1% case (for example, when you need the Products collection fully populated), provide query interfaces for the nested entities that allow you to filter. You can even provide hypermedia links to retrieve these collections as part of the simplified representations above.