We recently had a situation where our query filter keys had the same name as the our API order by keyword. Does anyone have any good idea how this can be resolved?
Lets say you have a ReST api where you have the url
"/items" where a GET request returns the following data;
[
{
"id":"2bd8cba7-767b-429f-9fa2-59d98f6fd07f",
"createdAt":"2015-02-21T05:07:23+00:00",
"modifiedAt":"2015-02-21T05:07:23+00:00",
"orderBy":"foo",
"createdByUserId": "47dab17c-10a0-4de6-9a3c-a1164c427342"
},
{
"id":"f4ddc597-7829-40d2-adb3-24d22ae7e80e",
"createdAt":"2015-02-21T05:07:23+00:00",
"modifiedAt":"2015-02-21T05:07:23+00:00",
"orderBy":"orderBy",
"createdByUserId": "b13a1020-fb78-4e62-b0a5-5240eb6af146"
},
{
"id":"65e329a2-8828-415c-870d-e454770ad34e",
"createdAt":"2015-02-21T05:07:23+00:00",
"modifiedAt":"2015-02-21T05:07:23+00:00",
"orderBy":"orderBy desc",
"createdByUserId": "0df3a37e-9926-4402-a039-29e561f17c9e"
}
]
Normally, if we want to return items that are newer then 2015-01-01 and we want to order the result by when it was created we use a url like "/items?createdAt=>2015-01-21T05:07:23+00:00&orderBy=createdAt" but in this case we have a filter key that is the same as the api keyword "orderBy".
Recently we ended up in a situation where we wanted items where the "orderBy" field was "orderBy" and we wanted the result ordered using the "createdAt" field. "/items?orderBy=orderBy&orderBy=createdAt", which one is the order by parameter and which one is the filter parameter?
Our API have the keywords "orderBy", "fields", "include", "limit" and "page" so a complete GET request can look like
"/items?orderBy=orderBy&orderBy=createdAt&field=id&field=orderBy&field=createdAt&include=createdByUser"
Right now it looks like we have a few options:
- Rename the fields in our data model. We can't do this since we have to return data using this exact structure.
- Change the API keywords if there is a name collision. This would make a our API inconsistent.
- Change the keywords to "apiKeywordX" like "apiKeywordOrderBy" which would decrease the chance of name collisions. Unfortunately we have a endpoint where the user can create custom models where each field can be searchable (thank you Postgresql for the JSON datatype) so there is still a risk of collisions.
- Move any api keywords to the request headers. In this case the url would look like
"/items?orderBy=orderBy" and have headers like
"OrderBy: createdAt",
"Include: createdByUser",
"Fields: id",
"Fields: orderBy",
"Fields: createdAt".
We ran a short trial using this but we ran into problems with a few corporate proxies that sometimes stripped unusual headers.
- Use a Json string as the query parameter. In this case the url would look like
"/items?parameter={"filters": {"orderBy": "orderBy"}, "orderBy": [{"createdAt": "desc"}], "fields": ["id", "orderBy", "createdAt"], "include": ["createdByUser"}".
After URL encoding it looks like
"/items?parameter=%7B%22filters%22%3A%20%7B%22orderBy%22%3A%20%22orderBy%22%7D%2C%20%22orderBy%22%3A%20%5B%7B%22createdAt%22%3A%20%22desc%22%7D%5D%2C%20%22fields%22%3A%20%5B%22id%22%2C%20%22orderBy%22%2C%20%22createdAt%22%5D%2C%20%22include%22%3A%20%5B%22createdByUser%22%7D".
This way the request is still cachable but it forces the consumer to have a json serializer. Our API returns data in XML, json, yaml or bson so we can't guarantee that the consumer have a json library.
Right now the Json query parameter looks like the best solution but I don't really like it since the url is completely unreadable. On the other hand no normal users are supposed to see those urls.
We can't be the first one that have a legacy database with a REST api that have this problem. Is there any common way to handle things like this?