RESTful URI for Filtering Nested Collections - rest

I'm looking for a good way to form a URI for a resource that filters on a collection of values contained within records. For example, say you have a recipe database and you want to search for recipes that contain "cherry" (obviously most recipes would contain multiple ingredients).
If I just want to filter on single values, I could do something similar to the following:
/recipe/search/?name=Spaghetti
But what about filtering on multiple values? I was considering something like the following:
/recipe/search/?ingredients=contains=cherry
Any thoughts on this? Is there a "standard" for a filter of this kind?
Update: One problem I have with my idea is the way it gets handled on the backend (in my case Rails). When querying the server with this particular format, Rails generates a Ruby hash that could get ugly like the following:
{"ingredients"=>"contains=cherry",
"action"=>"search",
"controller"=>"recipe"}

Your URI
First of all, in contrast to other answers I'll start from a REST perspective and then find appropriate additions to it. I am not strong in Ruby so bear with no-code on the backend.
Recipies are the entities you wanna present
your users find them at /recipies
HTTP has QUERYS for filtering
wanna have sorted those recipies by date? use /recipies/?sortby=date&sort=asc
Only recipies with cherries goes similarly: /recipies/?ingredients=cherry
So that's the REST way of structuring your basic URL.
For multiple matches there is no official way to do it, but i'd follow user1758003. This is an intuitive construction of the url and easily to parse on the backend, so we have /recipies/?ingredients=cherry,chocolate
Don't forget /recipies/search is not restful because it mirrors recipies and does not represent an independent entity. However it is a great place to host a searchform for visitors to your site.
Now there are some additional questions packed into the first, let me address them one by one:
I have a website consuming this api, how should the search form look like?
Give your visitors a /recipie/search page or a quick filtering possibility on /recipies.
Just set the <form action="/recipies" type="GET">. The browser will add all parameters as an Query string after /recipies.
Advanced functionality
A request to /recipies should list all. If you add a query every parameter of the query must be respected. so /recipies/?ingredients=cheese MUST only return cheesy recipies.
For multiply query parameter values there is no fixed standard but I'd like a service to be intuitive.
I read GET /recipies/?ingredients=cherry,chocolate as Get me the recipies which have ingredients of cherry and chocolate. To get a list of recipies containing either cherry or chocolate I'd want to write the URI like /recipies/?ingredients=cherry|chocolate which makes it visually very different from a comma and has a predefined meaning (OR) in programming contexts.

I'm not familiar with the specifics of ruby hashes but I'm guessing the hash is created to uniquely identified the query both at the http and data access levels?
Regardless, you want to be careful about URL encoding if you wish to use json in a query parameter. Another thing to keep in mind is the term "search" could be considered repetitive. If your server is being accessed using a GET method and you have criteria then hopefully you're not modifying any state in the back end. Not your question but just thought I'd mention it.
So...
https://yourserver.com/approot/recipe/search?ingredients=cherry,cheese&type=cake
HTTP doesn't define commas as a query string separator so your framework should be able to parse 2 query string parameters:
ingredients: "cherry,cheese"
which you should be able to easily covert to an an array using split or whichever equivalent function ruby provides.
and
type: "cake" (extra query term added to illustrate a point and because cherry cheese cake is awesome and cake is not an ingredient)
If I understand your example correctly you would end up with:
{
"ingredients"=>"cherry,cheese",
"type"=>"cake",
"action"=>"search",
"controller"=>"recipe"
}
Is this what you where looking for?

Most of the REST webservice using JSON data only.So use JSON format which will return single string value only. From this JSON format you can send the array value also.
For array value means you to convert that array into the JSON format like this
from php:
$ingredients = array('contains'=>array('fruits'=>'cherry,apple','vitamins'=>'a,d,e,k'));
$ingredients_json = json_encode($ingredients);
echo $ingredients_json;
it will return json format like this:
{"contains":{"fruits":"cherry,apple","vitamins":"a,d,e,k"}}
and you can use this JSON string in the url
/recipe/search/?ingredients={"contains":{"fruits":"cherry,apple","vitamins":"a,d,e,k"}}
in the server side we have the option to decode this JSON format value to the array.
{"ingredients"=>"{\"contains\":{\"fruits\":\"cherry,apple\",\"vitamins\":\"a,d,e,k\"}}",
"action"=>"search",
"controller"=>"recipe"}

Related

How to construct REST API endpoints with both composite keys and arrays?

Although there are tons of similar questions regarding the REST API design, I am asking a very specific question that I could not found answers in other similar questions.
Suppose that I am trying to GET a list of devices in the database with Building_Type and Room_Type filters. I would like to pass an array of filters, and each filter contains two field as a composite key. I've found standard practice to pass parameter arrays, but I could not find a good way for composite keys in the array.
Example:
GET /api/v1/devices?building_type=Educational&room_type=Office
This GETs all rooms with Educational building type and Office room type. However, I am trying to get a list of rooms for multiple composite combinations of {building_type, room_type}.
I am thinking of something like the following:
GET /api/v1/devices?location[]={building_type=Educational,room_type=Office}&location[]={building_type=Commercial,room_type=Office}&location[]={building_type=Educational,room_type=Classroom}
However this doesn't look like standard practice. I am asking for a better way to design this endpoint. I also don't want POST because this query does not change the state
on the server.
Note:
Please note that the following is incorrect, because I need to filter by an array of composite attributes of {building_type, room_type}.
GET /api/v1/devices?building_type[]=Educational&building_type[]=Commercial&room_type[]=Office&room_type=Classroom
It depends on what your backend can handle, but I would try an array of objects, like:
GET /api/v1/devices?location[][building_type]=Educational&location[][room_type]=Office&location[][building_type]=Commercial&location[][room_type]=ClassRoom
Rails 6 parses this like I expect:
"location"=>[{"building_type"=>"Education", "room_type"=>"Office"}, {"building_type"=>"Commercial", "room_type"=>"ClassRoom"}]
But, as this article goes into, libraries don't handle complex object serialization/deserialization into query params consistently. If your backend doesn't like the above, numerically indexing the array should work (though it's more work to construct from your client code):
GET /api/v1/devices?location[0][building_type]=Educational&location[0][room_type]=Office&location[1][building_type]=Commercial&location[1][room_type]=ClassRoom
If you want something that won't be implementation-dependent, you could also consider URL-encoding a JSON string that represents your search query:
GET /api/v1/devices?query=%7B%22locations%22%3A%20%5B%7B%22building_type%22%3A%20%22Educational%22%2C%20%22room_type%22%3A%20%22Office%22%7D%2C%20%7B%20%22building_type%22%3A%20%22Commercial%22%2C%20%22room_type%22%3A%20%22Office%22%7D%5D%7D
Not pretty, but possibly less frustrating.

Pass multiple filters with multiple values in a query string (REST)

I originally posted multiple filters containing multiple values in JSON as part of my GET request but I believe this is bad practise, so I changed it to a POST but I don't like it as getting results from a query has nothing to do with a POST so I guess I'll have to use a query string
Most filter examples I have found are either using one filter or one value, but I am looking as to whether or not there is a best practise to pass multiple filters with multiple values for filtering as a single parameter in the query string.
For example, this is a basic one which looks for all cars that are red
GET /cars?color=red
But what if I wanted to look for all cars that are
red, blue or green and
have 2 seats or less and
their brand name starts with b and
can be bought in the US, UK or Germany
Would the following be ok?
http://myserver/api/cars?color=red|blue|green¬seats<=2¬brand[startswith]b¬country=USA|UK|Germany
I'm suggesting the use of the:
| character as a separator between each values for a given filter
¬ character as a separator between each filters
[startsWith] to handle the search type, but could contain [=, <=, >=, <>, [contains],[endswith], etc...
This would then be parsed in the server end and the relevant filters would be build accordingly based on the provided values
Hope this make sense but I'm really interested as to whether or not there is a standard/best practise used for such scenarios with REST in mind?
Thanks.
As in most design questions, the key is having a consistent design for all your APIs. You can follow certain well-known guidelines/standards to make your API easily discoverable.
For example, take a look at OData. The "Queries" section on this page is relevant to your question. Here's an example:
https://services.odata.org/v4/TripPinServiceRW/People?$top=2 & $select=FirstName, LastName & $filter=Trips/any(d:d/Budget gt 3000)
Another option is the OpenSearch standard. The relevant section is here. Here's an example:
https://opensearch.php?recordSchema=html&query=dc.creator any Mill* Grad*
Another interesting option is GraphQL, which makes it easier to map query parameters to data fetch parameters. It uses a filter payload instead of query parameters. See the spec here: GraphQL Spec.

Add subcategories in a filtered API Restful resource

I'll give an example as the title might sound a bit confusing.
How to build a resource path for something like that:
GET /courses/?language=english&active=true/units
I want to filter the courses (not using an id as usually) and then get the units of this result. How would you do that? I guess using question marks between the path is not allowed.
That would depend a little on your DB schema of what is a "course" and a "unit". The whole point on using the RESTful way is to always build requests and urls resource-specific.
But let's say that one course has X units on it. Here's what i would do to make a RESTful path to that request:
Due to the path problem of filtering courses AND using the /unit suffix, it can be done by adding another query parameter that specifies what fields the request is supposed to return. Something like this:
GET /courses?language=english&active=true&fields=units
That would filter the courses, and then return only the 'units' field on the response. As i said, depending on your DB and models, if the units are not stored inside the courses, it would be a bad practice to get them by requesting a /courses path. In that case, first request the courses that match the desired filter, and then make another request to the /units context sending i.e the courses ID's as query parameters.

Can I prefix with $ (dollar sign) my URL query string parameters safely?

I am starting to implement filtering through query strings in my API REST application where I can perform filtering on any field passed to a entity in my database. But there are especial parameters that I want to differentiate like; sort, page, direction, etc from the rest of fields that will be used to filter the collection.
I want to avoid using an implementation like:
/?filters=field1:value1,field2:value2&page=3&per_page=50
Because I will need to make a custom parser to the filters value.
My desired structure is something more plain, like this:
/?lastname=Halden&country=somewhere&$sort=lastname
So, all the properties that aren't prefixed with $ are used to make the filter and the other properties with the prefix are used to tweak the result.
My actual question is that if it is safe?. If there can exist a problem in the moment to parse the whole query string in some libraries.
It should be ok. As a matter of fact, ODATA, one of the widely used REST standards in enterprise software uses $ the same way you want to use. Another sensible option you could use is _ (e.g. _sort, _page, etc.)

REST parameters vs URI

I'm just learning REST and trying to figure out how to apply it in practice. I have a sampling of data that I want to query, but I'm not sure how the URLs are meant to be formed, i.e. where I put the query. For example, for querying the most recent 100 data records:
GET http://data.com/data/latest/100
GET http://data.com/data?amount=100
which of the previous two queries is the better, and why? And the same for the following:
GET http://data.com/data/latest-days/2
GET http://data.com/data?days=2
GET http://data.com/data?fromDate=01-01-2000
Thanks in advance.
Personally, I would use the query string format in this case. If your /data path is returning all of the data, and you would like to perform this type of query, I believe it makes the most sense. You could also pass query string parameters such as ?since=01-01-2000 to get entries after a specified date or pass column names such as ?category=clothing to retrieve all entries with category equaling clothing.
Additionally, you would want paths such as /data/{id} to be available to retrieve certain entries given their unique id.
It really depends on a lot of things. If you're using any sort of MVC framework, you'd use the URI segments to define your get request to your API which I personally prefer.
It's not a big deal either way, it's all based on preference and how predictable you want the URL to be to your user. In some cases, I'd say go with the REST parameters, but more often than not a URI based GET is quite clean if your setup supports it.