Resource path query string for filter criteria

Background

I have 2 resources: courses and professors .

A course has the following attributes:

  • id
  • theme
  • semester_id
  • year
  • section
  • Professor_id

A professor has the following attributes:

  • id
  • faculty
  • SUPER_USER
  • first_name
  • last_name

So you can say that a course has one professor, and a professor can have many courses.

If I want to get all courses or all professors, I can: GET /api/courses or GET /api/professors respectively.


difficulty

My difficulty comes when I want to get all the courses that some professor is teaching.

I could use one of the following:

  • GET /api/professors/:prof_id/courses
  • GET /api/courses?professor_id=:prof_id

I am not sure what to use.


Current solution

I am currently using an extended form of the latter. My reasoning is that it is more scalable if I want to add filtering / sorting criteria.

I actually encode / paste JSON strings in request parameters. So an example (decoded) could be:

 GET /api/courses?where={professor_id: "teacher45", year: 2016}&order={attr: "topic", sort: "asc"} 

In the above request, all courses that were (or are currently) taught by a professor with the provided professor_id in 2016, sorted by topic title in ascending ASCII order, will be received.

I have never seen anyone do it this way, although, I wonder, I'm doing something stupid.


Closing Issues

Is there a standard practice of using a query string against a resource path for filter criteria? What has the larger API done in the past? Is this acceptable or is it recommended to use both paradigms at the same time (make both endpoints available)? If I really have to use the second paradigm, is there a better organization method that I could use besides JSON coding? Has anyone seen another public API using JSON in their query strings?


Edited to be less opinion. (See Comments)

+5
source share
2 answers

As explained in the previous comment, REST does not really care about the actual form of the link, which identifies a unique resource, unless the RESTful constraints or the Hypertext Transfer Protocol (HTTP) are violated.

Regarding the use of query parameters or paths (or even matrices), then it is entirely up to you. There is a fixed rule when you need to use only individual settings.

I like to use query parameters, especially when this value is optional and not required, as many frameworks, such as JAX-RS, allow you to define default values. It is often indicated that query parameters avoid caching responses, which, however, are a more urban legend and then true , although some implementations may still omit responses cached for URIs containing query strings.

If the parameter defines something like a specific property of the aroma (i.e. the color of the car), I prefer to put them in the matrix parameter. They can also appear in the middle of a URI, i.e. /api/professors;hair=grey/courses can return all courses taught by professors whose hair color is gray.

The path parameters are required arguments that the application requires to fulfill the request in my understanding, otherwise the corresponding method handler will not be called on the service side in the first place. Typically, these are some resource identifiers, such as table row identifiers, UUIDs assigned to a specific object.

For a description of relationships, I usually start with 1 part of a 1: n relationship. If I come across the m: n ratio, as in your case with professors - courses, I usually start with a creature that can exist without the other more easily. The professor is still a professor, although he does not give any lectures (in a certain sense). Since the course will not be the course, if the professor is not available, I would put the professors before the courses, although, with regard to REST courses, nevertheless, these are excellent top-level resources.

So I would change your request

 GET /api/courses?where={professor_id: "teacher45", year: 2016}&order={attr: "topic", sort: "asc"} 

to something like:

 GET /api/professors/teacher45/courses;year=2016?sort=asc&onField=topic 

I changed the semantics of your fields a little, since the property of the year is probably better suited for courses and not for professors, since the professor is already reduced to one resource through the professors id. Courses, however, should be limited only to those held in 2016. Since sorting is optional and may have a default value, this is a great candidate for me to include in the query parameters section. The sort field refers to the sort itself and therefore also relates to query parameters. I put the year in the matrix parameter, because this is a certain property of the course itself, for example, the color of the car or the year the car was made.

But, as explained earlier, this is rather stubborn and may not match yours or other people.

+3
source

I could use one of the following:

  • GET / api / professors /: prof_id / courses
  • GET / api / courses? Professor_id =: prof_id

Could you. Here are a few things to consider:

Machines (in particular, REST clients) should treat URIs as an opaque thing; the closest thing to them that they have ever come to understand their value is permission.

But people, looking at the HTTP traffic log, do not handle the URIs opaquely - we are actually trying to figure out the context of what is happening. Staying on the path of a poor bastard trying to spot an error is a good feature for URI design.

This is also a useful property for your URI design. A URI designed from a few simple consistent principles will be much easier to work with than arbitrary.

There is a great overview of the path segment and request for Programmers

https://softwareengineering.stackexchange.com/questions/270898/designing-a-rest-api-by-uri-vs-query-string/285724#285724

Of course, if you have two different URIs, then both "follow the rules", then the rules do not really help in choosing.

Support for multiple identifiers is a valid option. It is perfectly reasonable that there can be more than one way to get a specific idea. For example, these resources

 /questions/38470258/answers/first /questions/38470258/answers/accepted /questions/38470258/answers/top 

can return representations of the same "answer".

On the other hand, the choice adds complexity. It may or may not be a good idea to offer your customers more than one way to do something. "Don't make me think!"

On the other / other side, api with a bunch of "general" principles that carry a bunch of arbitrary exceptions is not as easy to use as one with consistent principles and some duplication (citation needed).

The concept of a "canonical" URI, which is important in SEO, has a counterpart in the API world. Mark Seemann has an article on his own links that covers the basics.

You can also consider what methods the resource supports, and whether the design offers these assumptions. For example, POST to modify a collection is a common idiom. Therefore, if your URI looks like a collection

 POST /api/professors/:prof_id/courses 

Then, customers are more likely to create a relationship between the resource and the supported methods.

 POST /api/courses?professor_id=:prof_id 

There is nothing "wrong" in this, but it is not so arbitrary.

GET / api / courses? where = {Professor_id: "teacher45", year: 2016} & order = {attr: "topic", sort: "asc"}

I have never seen anyone do it this way, although, I wonder, I'm doing something stupid.

I don't either, but syntactically this looks a bit like GraphQL . I see no reason why you could not submit the request in this way. This would make sense to me as one description of the request, rather than breaking it down into several parts. And of course, this should be encoded in a URL, etc.

But I would not want to go crazy with this right, if you really do not need to provide your customers with such flexibility. Simpler constructions exist (see Roman answer)

+2
source

All Articles