REST API Best practice: How to accept list of parameter values as input [closed]

We are launching a new REST API and I wanted some community input on best practices around how we should have input parameters formatted:

Right now, our API is very JSON-centric (only returns JSON). The debate of whether we want/need to return XML is a separate issue.

As our API output is JSON centric, we have been going down a path where our inputs are a bit JSON centric and I've been thinking that may be convenient for some but weird in general.

For example, to get a few product details where multiple products can be pulled at once we currently have:

http://our.api.com/Product?id=["101404","7267261"]

Should we simplify this as:

http://our.api.com/Product?id=101404,7267261

Or is having JSON input handy? More of a pain?

We may want to accept both styles but does that flexibility actually cause more confusion and head aches (maintainability, documentation, etc.)?

A more complex case is when we want to offer more complex inputs. For example, if we want to allow multiple filters on search:

http://our.api.com/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}

We don't necessarily want to put the filter types (e.g. productType and color) as request names like this:

http://our.api.com/Search?term=pumas&productType=["Clothing","Bags"]&color=["Black","Red"]

Because we wanted to group all filter input together.

In the end, does this really matter? It may be likely that there are so many JSON utils out there that the input type just doesn't matter that much.

I know our JavaScript clients making AJAX calls to the API may appreciate the JSON inputs to make their life easier.


Solution 1:

A Step Back

First and foremost, REST describes a URI as a universally unique ID. Far too many people get caught up on the structure of URIs and which URIs are more "restful" than others. This argument is as ludicrous as saying naming someone "Bob" is better than naming him "Joe" – both names get the job of "identifying a person" done. A URI is nothing more than a universally unique name.

So in REST's eyes arguing about whether ?id=["101404","7267261"] is more restful than ?id=101404,7267261 or \Product\101404,7267261 is somewhat futile.

Now, having said that, many times how URIs are constructed can usually serve as a good indicator for other issues in a RESTful service. There are a couple of red flags in your URIs and question in general.

Suggestions

  1. Multiple URIs for the same resource and Content-Location

    We may want to accept both styles but does that flexibility actually cause more confusion and head aches (maintainability, documentation, etc.)?

    URIs identify resources. Each resource should have one canonical URI. This does not mean that you can't have two URIs point to the same resource but there are well defined ways to go about doing it. If you do decide to use both the JSON and list based formats (or any other format) you need to decide which of these formats is the main canonical URI. All responses to other URIs that point to the same "resource" should include the Content-Location header.

    Sticking with the name analogy, having multiple URIs is like having nicknames for people. It is perfectly acceptable and often times quite handy, however if I'm using a nickname I still probably want to know their full name – the "official" way to refer to that person. This way when someone mentions someone by their full name, "Nichloas Telsa", I know they are talking about the same person I refer to as "Nick".

  2. "Search" in your URI

    A more complex case is when we want to offer more complex inputs. For example, if we want to allow multiple filters on search...

    A general rule of thumb of mine is, if your URI contains a verb, it may be an indication that something is off. URI's identify a resource, however they should not indicate what we're doing to that resource. That's the job of HTTP or in restful terms, our "uniform interface".

    To beat the name analogy dead, using a verb in a URI is like changing someone's name when you want to interact with them. If I'm interacting with Bob, Bob's name doesn't become "BobHi" when I want to say Hi to him. Similarly, when we want to "search" Products, our URI structure shouldn't change from "/Product/..." to "/Search/...".

Answering Your Initial Question

  1. Regarding ["101404","7267261"] vs 101404,7267261: My suggestion here is to avoid the JSON syntax for simplicity's sake (i.e. don't require your users do URL encoding when you don't really have to). It will make your API a tad more usable. Better yet, as others have recommended, go with the standard application/x-www-form-urlencoded format as it will probably be most familiar to your end users (e.g. ?id[]=101404&id[]=7267261). It may not be "pretty", but Pretty URIs does not necessary mean Usable URIs. However, to reiterate my initial point though, ultimately when speaking about REST, it doesn't matter. Don't dwell too heavily on it.

  2. Your complex search URI example can be solved in very much the same way as your product example. I would recommend going the application/x-www-form-urlencoded format again as it is already a standard that many are familiar with. Also, I would recommend merging the two.

Your URI...

/Search?term=pumas&filters={"productType":["Clothing","Bags"],"color":["Black","Red"]}    

Your URI after being URI encoded...

/Search?term=pumas&filters=%7B%22productType%22%3A%5B%22Clothing%22%2C%22Bags%22%5D%2C%22color%22%3A%5B%22Black%22%2C%22Red%22%5D%7D

Can be transformed to...

/Product?term=pumas&productType[]=Clothing&productType[]=Bags&color[]=Black&color[]=Red

Aside from avoiding the requirement of URL encoding and making things look a bit more standard, it now homogenizes the API a bit. The user knows that if they want to retrieve a Product or List of Products (both are considered a single "resource" in RESTful terms), they are interested in /Product/... URIs.

Solution 2:

The standard way to pass a list of values as URL parameters is to repeat them:

http://our.api.com/Product?id=101404&id=7267261

Most server code will interpret this as a list of values, although many have single value simplifications so you may have to go looking.

Delimited values are also okay.

If you are needing to send JSON to the server, I don't like seeing it in in the URL (which is a different format). In particular, URLs have a size limitation (in practice if not in theory).

The way I have seen some do a complicated query RESTfully is in two steps:

  1. POST your query requirements, receiving back an ID (essentially creating a search criteria resource)
  2. GET the search, referencing the above ID
  3. optionally DELETE the query requirements if needed, but note that they requirements are available for reuse.

Solution 3:

First:

I think you can do it 2 ways

http://our.api.com/Product/<id> : if you just want one record

http://our.api.com/Product : if you want all records

http://our.api.com/Product/<id1>,<id2> :as James suggested can be an option since what comes after the Product tag is a parameter

Or the one I like most is:

You can use the the Hypermedia as the engine of application state (HATEOAS) property of a RestFul WS and do a call http://our.api.com/Product that should return the equivalent urls of http://our.api.com/Product/<id> and call them after this.

Second

When you have to do queries on the url calls. I would suggest using HATEOAS again.

1) Do a get call to http://our.api.com/term/pumas/productType/clothing/color/black

2) Do a get call to http://our.api.com/term/pumas/productType/clothing,bags/color/black,red

3) (Using HATEOAS) Do a get call to `http://our.api.com/term/pumas/productType/ -> receive the urls all clothing possible urls -> call the ones you want (clothing and bags) -> receive the possible color urls -> call the ones you want