"Right" REST URL to get all resources within all resources? - rest

So... I am aware that this question could be dangerously close to being opinion based . I'm hoping is not, and that the REST standard is clear about what I'm going to ask but if it's not, I'll close it.
I have a website (in Django, with the data being stored in Postgres) with Product(-s) and Category(-ies) Each category can contain several product(-s).
So, the question is: What would be the "right" endpoint (if there's any) to GET all the categories with all the products in each?
I believe it would be clear getting all the products of a specific category. For instance, if the category ID was 24, in order to get all its products, I would write:
http://myserver.com/api/categories/24/products
But how about ALL the categories with ALL the products in each?
Would it be http://myserver.com/api/categories/products?
Would it be http://myserver.com/api/categories/all/products?
Would it be better using some kind of parameter, such as http://myserver.com/api/categories?mode=all_products ?
The idea would be having a response like this (JSON format)
{
"25": [{
"id": 1,
"name": "Product 1 in category 25",
"price": 100
}, {
"id": 2,
"name": "Product 2 in category 25",
"price": 200
}],
"26": [{
"id": 3,
"name": "Product 1 in category 26",
"price": 300
}, {
"id": 4,
"name": "Product 2 in category 26",
"price": 400
}]
}
Thank you in advance.

As far as REST is concerned if you are uniquely representing the resource in the url so that it is cacheable (and abiding with HATEOAS but let's skip that part), it doesn't really matter how you structure your urls. With that said in my opinion, since you want to get all the products, your url should be something like
GET /products # to return all products with their associated categories
GET /category/24/products # to return all products belonging to a particular category
Note:- Although url structure is not exactly a part of REST, but designing url in terms of an entity/resource and an identifier does makes it easier to create RESTful APIs. Well structured urls also makes them easier to be consumed by clients.

Related

What is the best way to avoid duplication in my music Library database modeling using mongodb

As a newbie in mongodb, I tried to model a music library database. From what I have done so far, I suspect some level of duplications, especially with the artist entity. Suggestions on how to avoid such duplications or perfect the database model will be appreciated.
{
"track_id": "1",
"Duration": " 5.00",
"title": "Andersen",
"date released": ISODate("01-25-1896")
"Artist":
{
"Name": "Lee Jones",
"Gender": "Male"
},
"Album":
{
"Name": "star wars",
"date released": ISODate("01-25-1896")
"Artist":
{
"Name": "Lee Jones",
"Gender": "Male"
}
}
}
In the above codes I made use of the Embedded document pattern considering the following:
A track is made by an artist
A artist can make zero to many tracks
A track can be associated with zero album or a single album
An album can have one or many tracks
An album belongs to an artist
An artist can own zero to many albums
With MongoDB, how you model your data depends – entirely – on your
particular application’s data access patterns. You want to structure
your data to match the ways that your application queries and updates
it.
Of course you can reference (link) the Artist field with a Artist Document and avoid data duplication, but this destroy the may purpose of a document database ( ease of development and fast response), you should favor embedding unless there is a compelling reason not to.
More info: 6 Rules of Thumb for MongoDB Schema Design

MongoDB design speculation

I am digging the MongoDB related questions/answers but one thing is still not obvious.
Let's consider the following Product collection:
{
"manufacturer": "Man1",
"model": "Model1"
}
Let's say we have 1.000.000 products, and I would like to create
a dropdown of manufacturers (which would be max 50 options).
In this case every time I have to use the .distinct() function on that huge product collection.
Is this the right way to do it ?
I am a bit concerned about the performance.
Or should I create a separate collection for manufacturers and keep it synced ?
UPDATE
Thanks for all the answers, I still considering them.
And what if I do the following:
Manufacturer:
{
"name": "Man1",
"models": [
{
"name": "Model1",
"products": [Product1, Product2]
}
],
}
and Product
{
"manufcturer": "Man1",
"model": "Model1"
"manufacturer_id": Manufacturer1,
"model_id", Model1
}
First. If you've large number of records, you'd never want to load all the data in just one request to populate the list, dropdown or whatever it is. Rather, implementing something like load more options suits more. Just like pagination.
And you can manage to get like 20,40 records per request and do any optimization on those small chunk of data.
You can create a separate collection for manufacturers. You just have to keep it in sync, after every addition/update/deletion of product from products collection.
I think you can think of designing your Product collections like this:
{
"manufacturer": "Man1",
"model": "Model1"
"Product" :[Product1,Product2]
}
And having an index on "manufacturer" will optimize your query to get list of manufacturers

How do relational databases work with a REST API?

I'm trying to understand how a REST API would work when dealing with a relational, normalized database. For example, given the tables Customer, Order, and OrderType:
Customer
ID
Name
Address
Order
ID
Amount
TypeId
CustomerId
Order Type
ID
Desc
If I wanted to lookup all the order information for a given customer, I would do something like url/customers/:customerId/orders; and in the the API it would handle performing joins on tables in order to return a response like this?
{
"orderNumber": 123,
"type": "online",
"customer": "john doe",
"amount": "500"
},
{
"orderNumber": 124,
"type": "in-store",
"customer": "jane doe",
"amount": "100"
}
Is that correct? Or would I need to do separate API calls such as url/customers/:customerId, url/orders/:customerId, and url/orderType/:typeId then assemble the information in the front end?
If you assemble information on front end you lose most benefits of relational database and have to write lots of code instead of one line of sql. You design your API routes according to specific tasks you want to perform, which does not depend on application architecture and what stack of technologies you use.

What's the best practice to get user profile in RESTful API design

In my case, each user has a profile with lots of attributes, e.g. gender, age, name. What's the best practice to design the RESTful API to get those attributes? The followings are possible solutions:
Get all attributes in a single call
Get all attributes:
Request: GET http://api.domain.com/users/id/profile
Response: {"name" : "Jim", "gender" : "male", "age" : 12}
Get attribute one-by-one
Get attributes list:
Request: GET http://api.domain.com/users/id/profile
Response: { "attributes" : ["name", "gender", "age"] }
Get a specified attribute:
Request: GET http://api.domain.com/users/id/profile/name
Response: {"name" : "Jim"}
With the first solution, the client gets all attributes in a single call. However, the problem is that there's too many attributes, and we'll add more attributes to the profile. I'm wondering which one is better?
If you have lots and lots of attributes, another approach would be to group them.
In REST, everything needs to be a resource (for example, but not limited to, something identifiable by an URL).
So you could have
GET http://api.domain.com/users/id/profile
and you get
{ "categories" : ["names", "address", "interests", "jobhistory", "publications", "blogs", "skills"] }
and then you query further. That does imply multiple trips but you would not have to query the many attributes one by one, ending up with 50 queries out of 75 attributes, for example, but might need 3 queries to get the 50 attributes you want.
Definitely the first option seems much better primarily because of saving multiple calls - mind you clients as well, it will be much easier for them to fetch what they need in a single call instead of calling - more or less - the same resources multiple times.
It seems that what are you looking for is called resource expansion - you can read about it e.g. here.
In short it assumes that the response you send is configurable with query params. If no params are included some basic subset of attributes is returned. If params to be expanded are sent - the basic subset is returned along with other attributes listed in query param. You can also mix two approaches. Some of parameters might be expanded via query params other may be called as subresources - it depends arbitrarily on the size of a resource.
I would recommend to divide user profile attributes into logical categories and make these categories available to your clients through query parameters like ...
names (array of names (aliases)) : first, last, middle, prefix
addresses (array of addresses) : street, apt, city, state, country, county
jobs (array of jobs) : company, designation, start_date, end_date, city, state, country
Provide an API that returns the up-to-date list of categories available on user profile as documentation can get outdated.
GET http://api.domain.com/users/profiles/categories
Response:
{
"categories": ["names", "address", "interests", "jobs" ],
"links": [
{
"rel": "users.profiles.categories",
"href": "http://api.domain.com/users/profiles/categories"
}, {
"rel": "users.profiles.category.names",
"href": "http://api.domain.com/users/profiles?categories=names"
}, {
"rel": "users.profiles.category.addresses",
"href": "http://api.domain.com/users/profiles?categories=addresses"
}, {
"rel": "users.profiles.category.interests",
"href": "http://api.domain.com/users/profiles?categories=interests"
}, {
"rel": "users.profiles.category.jobs",
"href": "http://api.domain.com/users/profiles?categories=jobs"
}, {
"rel": "users.profiles.category.all",
"href": "http://api.domain.com/users/profiles?categories=all"
}
]
}
With the above HATEOAS and depending on the categories mentioned in query parameter, your service can query those entities from database and form the response and return back to your client.
GET http://api.domain.com/users/id/profile?categories=names,address,interests,jobs
NOTE: if comma(,) cannot be not directly used in URL then you can use %2C (url-encoded value of ,).
Additionally, your actual GET API can also return HATEOAS to sub categories if the user doesn't use the categories API to get all sub categories.
This is just one of the way where you can using additional informative end point provide available/supported categories (query parameters) and HATEOAS will help your client to navigate thru those available sub-categories.

REST API: How to fetch objects and popular objects, should it be same API or separate API?

We have several API endpoints like:
/api/cities/ => Fetches all cities in our database. Its an oversimplified example, actually we have around 1k cities.
{
"name": "Chicago",
"name": "Los Angeles",
"name": "New York",
"name": "Phoenix"
}
Currently these API returns city list in alphabetic order.
We have new requirement where we need to fetch popular cities at the top, this followed by list in alphabetic order.
JSON Output would look like:
{
"popular"
{
"name": "New York",
"name": "Los Angeles"
},
"all"
{
"name": "Chicago",
"name": "Los Angeles",
"name": "New York",
"name": "Phoenix"
}
}
How should current APIs be modified to fulfil this:
Should we create new API like /api/popularcities/ which would fetch list of popular cities? This way client would call /api/popularcities/ first and then /api/cities/ API.
Should we add query string parameter in existing API /api/cities/?fetch=popularall to fetch both popular and all cities.
Or any other thing, this doesn't look like filter attribute as this is actually adding result at the top and not filtering it out.
We do need to repeat popular cities in all city list as this would be binded directly to UI dropdown and requirement is to keep alphabetic ordering intact.
Suggestions?
popular cities are a way of sorting for the same entities.
It shouldn't be a new resource, but a way of querying the same resource sorted differently to get the needed entries.
I would use a query for that: ?sort=popular.
you can use both endpoint, a endpoint like as shortcut of api fetch all with order by popular.
/cities?sort=popular
/popular_cities