65

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 length 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 advice to create a restful search method with a lot of parameters?

update: now that I have more experience, I've realized that having to do a search function with A LOT of parameters for a web app is actually something that designates bad design and bad requirements, and @Neil answer has the point. So my advice is do what you can within the scope. Nevertheless, the @Laiv answer is still good for less extreme cases, so take it into account too.

4
  • 4
    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, 2017 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, 2017 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, 2017 at 7:27
  • Check Elasticsearch API documentation. You might get inspiration from their attemps. However, I'm not sure how much "RESTful" their approach is.
    – Yuri
    Jun 18, 2020 at 6:38

3 Answers 3

76

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 GET a chance

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 it has all the advantages derived from the method' semantics (idempotence, safe and caching). And bookmarking.

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 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 too, share links, etc. And, of course, we don't have to escape/unescape the string (something I dislike).

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 these on the server-side.

6
  • 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. Jul 19, 2017 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, 2017 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, 2017 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, 2019 at 18:51
  • 1
    JSON and base-64 encoding will avoid stupid little errors where client and server, or something in between, don’t quite agree how things should be escaped/unescaped. By far the least painful. Plus you can add some encryption/decryption in between with very little code changes.
    – gnasher729
    Jan 15 at 7:48
21

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.

9
  • 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. Jul 19, 2017 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, 2017 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, 2017 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, 2017 at 14:25
  • 3
    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, 2017 at 14:28
-1

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.