Context
Hi, in my job we are trying to modify an API endpoint (/resources
) to make special queries to it. A resource
has a location (long/lat) and we want to make queries based on that location. We want to support mainly two different filters:
- Filter by lat/long (already done):
GET /resources?lat=0&long=0
returns a list ofresource
with that location. - Filter by a
polygon
(currently designing): a polygon is a very long sequence of points and we want to return thoseresource
s that are included in that polygon.
Both filters return an array of resource
s but with different representations.
Problems
The new filter has three main problems:
- Due to the length of the polygon (represented by and array or vertices) we cannot use a query parameter.
- This filter is quite time consuming in our backend, so we need to implement it in an
asynchronous
way. - Both endpoints return different representations of each resource (both return an array of
resource
but in a different format).
Solution first problem
We are going to use the body
of the request instead of a query parameter. So we call the endpoint like:
GET /resources
Body:
{
"vertices": [ ... very long list of lat/long values ... ]
}
We think this solution is the only available.
Solution second problem
We are going to use jobs. By that I mean the following:
GET /resources -> returns a job_id -> once it's finished the frontend gets notified -> frontend calls a new endpoint GET /resources?job_id=<job_id>
We see some drawbacks in this design:
- To differentiate the type of filter, we need to check the parameters: if
lat
,long
andzoom
are included, then it's the first filter. If the body is not empty and contains a list ofvertices
(the polygon), then is the second one. We think this is a bad approach and it's not "very restful".
Solution thrid problem
As stated in the third problem, we are returning different representations but we do not specify that anywhere. To fix that, we have thought of using the Content Type
header and create our own types, like application/json;<our_type>
, but we are not sure about this solution either.
How do you think we should solve these problems? Can you give us some guidelines or resources to try to improve the solution?
2 Answers 2
GET /resources
Body:
{
"vertices": [ ... very long list of lat/long values ... ]
}
Bad idea. See RFC-7231
A payload within a GET request message has no defined semantics
What this really means: if somebody is using standard, general-purpose components to interact with your API, and something expensive happens, it's your fault.
POST serves many useful purposes in HTTP, including the general purpose of "this action isn’t worth standardizing." -- Roy Fielding, 2009
POST /resources -> returns a job_id -> once it's finished the frontend gets notified -> frontend calls a new endpoint GET /resources?job_id=<job_id>
That's the right basic idea (notice that I changed the method of the first request). Think about how it would look with web pages; we would submit a form (POST), and get back a document that would tell us that our form submission was accepted, the job is in process, here's a link to use to check if the job is done, here's the link where the results will appear.
The main difference here is that, in REST, the server would usually provide the URI to use, rather than just giving you information to build your own URI.
To fix that, we have thought of using the Content Type header and create our own types, like application/json;<our_type>, but we are not sure about this solution either.
That's a GREAT answer. See RFC 6838.
My interpretation is that you probably want to use vendor/vanity tree subtypes, rather than parameters
application/vnd.company-name.v1+json
application/vnd.company-name.v2+json
You'll find lots of spelling examples in the IANA media-type registry.
-
Thanks a lot for your answer! I still have some doubts about the second problem, I have recently found this post asyncrestapi.docs.apiary.io/#introduction/allowed-http-requests where it solves the problem in a quite elegant way but I think it does not fit with the solution to the third problem. What do you think?Antonio Gamiz Delgado– Antonio Gamiz Delgado2021年03月23日 19:57:06 +00:00Commented Mar 23, 2021 at 19:57
I think @VoiceOfUnreason has provided great suggestions for your problems and solutions. But, let me tell you that you can use WebSocket for the search part which is more efficient, and I should tell you that it's not hard to use it.