41

Following REST principles, I would want to create a GET method for my API that make a search using some criteria and return the results to the client. The problem is that the criteria can have up to 14 parameters, one of them is a list of complex objects, so...

  • I don't even know if it possible to encode/decode these complex objects to/from url parameters.

  • I didn't calculate how long the url could get but I'm sure it will be large enough and maybe reach the url lenght limit?

Also, the search should show the results in "real time", I mean, every time the user changes something from the search form he should be able to see the new results without pressing any "search" button.

Could you clarify me these points and what would be your advise to create a restful search method with a lot of parameters?

  • 2
    As an aside (I note that neither of the two answers at the time of writing mention the real time part), real time searches typically should be just sending the updated request over and over. Eg, while you're typing, you'd be sending requests for stuff like search?q=t, search?q=te, search?q=test, and so on. Consider limiting how often the query is sent to avoid hurting your server. You could also alternatively return a great deal of information and on the client side do filtering. That works well if the user enters broad categories that can narrow things down greatly. – Kat Jul 25 '17 at 21:38
  • 2
    No matter the solution, be sure to exchange data in a technology neutral format. So don't use an internal representation or it will be harder to write clients for your API. – Kwebble Jul 30 '17 at 20:31
  • Depending on your needs, this library could make the job : github.com/jirutka/rsql-parser. It has a JPA extension if you use JPA, or you can write your own Visitor to map to your search engine's API. And you can add your own operator. – Walfrat Aug 30 '17 at 7:27
52

Before you read my answer, I would like to say that I agreed with @Neil. We have to pick our battles. We usually want to do our best, but sometimes there's too little room for discussion and we have to make decisions against our will.

Anyways, in Neil's answer, I miss one more thing. Documentation. Just to ensure that developers know that POST requests to /search are safe.

That said.

1. Give a chance to GET

Consider the GET option first. Check out this question URL's max length. Evaluate whether your longest query string is longer than 2000 characters. If it doesn't, and you don't expect it to be, go with GET. It might seem ugly, but at least you can bookmark the URL and, of course, it has all the advantages derived from the method' semantics (idempotence, safe and caching)

1.1 Try encoding the query string

For example, in base 64. Even javascript supports base 64 encodings.

This's how it works:

  1. Build the JSON with all the filters and normalise it.
  2. Parse it to string
  3. Encode it
  4. Send the encoded JSON as request param (/search?q=SGVsbG8gV29ybGQh....).
  5. On the server-side, decode the param q.
  6. Deserialize the JSON string

Previously, make the longest possible JSON string, encode it and take the length. Evaluate if the encoded string fits in the URL. I have implemented the following snippet on Fiddle.js for you to test. (I hope it still works)1

Base 64 encodes are deterministic and reversible, so there's no chance for collisions.

With encoded queries, we could also save searches in the DB, bookmark the URL, share links, etc. And, of course, we don't have to escape/unescape the string.

1.2 Try with aliases

Reading this blog about how to design REST APIs, I remembered one more alternative. Aliases for common queries.

I find these to be interesting for the next reasons

  • Shorten the query string length. It makes the API cleaner and user-friendly

    GET /tickets/?status=closed&closedAt=xxx vs GET /tickets/recently-closed/

  • Combinable with more aliases or more request parameters.

    GET /tickets/?status=closed&closedAt=xxx&within=30min vs GET /tickets/recently-closed/?within=30min

  • We can combine aliases with encoded query strings

    GET /tickets/?status=closed&closedAt=xxx&within=30min vs GET /tickets/recently-closed/?q=SGVsbG8g...


1: I have used JSON, but we could use other formats as soon as we can deserialize it on the server-side.

  • 2
    This is both practical and correct. It is also worth noting that most programming languages make it trivial to transform a hash into a query string so starting off with a GET action is very easy to do. – Aluan Haddad Jul 19 '17 at 17:33
  • 1
    I love Spring stackoverflow.com/questions/16942193/… I can't believe it worked at first try :D. About the url lenght, is less than 1k, although we still need to iterate the specs. – anat0lius Jul 20 '17 at 9:52
  • Then, go with GET. For simplicity. With Spring MVC, you can achieve the very same mapping with GET. Look for Spring's WebArgumentResolver ;-) – Laiv Jul 20 '17 at 20:19
  • Base64 inflates the payload size by about 4/3. While urlencoding can make it 3/1 for special characters, queries with mostly safe characters are going to keep the same size. Is there any other reason to use base64? – villasv Feb 18 at 18:51
  • Not really. I just dislike (un)escape URLs. The overweigth of the payload is the tradeoff here. It still have to fit within GET's max size per request. That's why I built the snippet. For the user to try. When I wrote the answer, I priorised the web semantics over the implementation details. The whole point of the answer is "keep trying with GET". Find your way or use any of these I share with you. – Laiv Feb 18 at 21:05
13

If all you have is a hammer, everything looks like a nail. It seems the problem here is that you're trying to turn a search page into a RESTful one, and this hardly seems to be a common pattern for RESTful design to solve.

Simply go with a POST request with parameters as provided by the user in order to get the information you require from the backend. I assume you need not do anything other than perform a search, so there's no chance you'll need to insert through this page. Just add a /search to the end of your URL so you don't risk to run into conflicts with your /users page which would be RESTful.

  • 2
    @LiLou_: For that requirement, there are only two realistic possibilities: 1. Read all data to your front-end and do the filtering there. This might be prohibitive in the required amount of memory. 2. Do a new request to the server for each change in search criteria. It doesn't matter if this is a POST or a GET request, but the involved network latency might be disruptive for the user's sense of "real time" updates. – Bart van Ingen Schenau Jul 19 '17 at 12:55
  • 2
    I would agree to disagree, POST means something else semantically. I would suggest to go with the GET and pass all the filter data in the query parameter now if there are too many parameters then it's a fault at the application level. – CodeYogi Jul 19 '17 at 14:14
  • 2
    @CodeYogi sometimes the customer doesn't give you space for discusion. I have implemented Excel-like view pages with 40-50 columns each of them with its own filter. And sortable of course. Anyways, is still possible with GET but it might not seem too fashion – Laiv Jul 19 '17 at 14:22
  • 1
    @Laiv in that case there needs to be serious discussion because POST is meant for change in server state. The use cases like this are not exceptional hence should be taken care of without hacks. – CodeYogi Jul 19 '17 at 14:25
  • 2
    In theses cases, documentation is a must. I have had a serious discussion with customers regarding the usability of their applications. Later I was proven to be right because the end-user agreed with me. However, sometimes you have to pick your battles. – Laiv Jul 19 '17 at 14:28
0

It fully depend on what is your API model: As none or as verb.

If the API is a none then you may want to get list of objects as follow:

GET: /api/v1/objects

In this case you have to send data as request parameters. So you have to describe your parameters as a flat list of key-values:

GET: /api/v1/objects

key1 : val1
key2.key1 : val 21
key2.key2 : val 22
....

Some platforms supports custom parameter resolver (e.g. Spring MVC), an you can convert params into an object.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.