Query strings in RESTFul web services

abril 5, 2008

Atenção, este blog foi migrado para: http://brunopereira.org

I said before that I don’t like query strings in RESTFul URIs. You cannot cache the results in the web server and in many cases they lead to a poor design of your URIs.

However, there are cases where query strings are useful and make sense. In the project I’m working right now, we have an asynchronous queue which several server instances consume periodically. This queue has events that must be processed, and each event belongs to a given user. Each user may have several events in the queue.

We have this queue of events that must be processed and we register them in another list when they are successfully processed. When an event is processed, he goes to the “processed” list if everything goes right or he stays in the queue if something gone wrong. In the queue we register how many times we tried to process each event.

In my team we have an application that helps us manage our infrastructure. This application does many things that we previously needed to ask to the operations team or DB team. Something very desirable is to be able to check how the queue is going without needing to check server logs or the database. So we’re adding some features in the management application to help us check the queue.

Ok, now let’s talk about the RESTful services. We’ve implemented some services that access the queue. One the of services allows us to check how many events are still in the queue with at least N tries. For example, how many events are pending with at least 1 try?

What’s the Resource here? In my opinion the resource is “Pending event”. Considering this, I would access this resource doing GET /event/pending. Ok, but doing GET /event/pending would probably give us the whole list of pending events. I want pending events with at least 1 try. How can we do that? We could do a GET /event/pending/1, and it would work. However, this URI is bad, very bad. If I didn’t know what this service was about, I’d probably say this URI returns the first pending event according to some order. Probably it would be the oldest or newest one.

So how do we form this URI? We could also have a GET /event/pending/tries/1. This would indeed be much better than the first URI, but I don’t like it either. My resource is “Pending event”. Pending events with at least 1 try are still just pending events for me. What I want is just to filter the pending events. So, I ended up using GET /event/pending?t=1. “t” in this query string stands for “tries”. I could also have used GET /event/pending?tries=1. As a matter of fact, I think supporting both is nice. It would be similar to command line options in Unix applications.

Now, let’s say I want to know the pending events of a given user. Looking at the previous URI, You might say GET /event/pending?u=123 or GET /event/pending?user=123. But in this case, I wouldn’t use query strings. The best URI in my opinion is /event/pending/user, and that would lead to GET /event/pending/user/123. What’s the difference here? I consider the “User” as a meaningful thing. It’s another resource. However, “number of tries” for me is just a filter. I don’t need to know anything about a specific try, so it’s just a filter in this case. Another similiar filter would be “Pending events created before today”. We could have another query string parameter to filter events by creation date. Something like GET /event/pending?c=03/04/2008 or GET /event/pending?creation=03/04/2008.

So, I changed my mind a little bit about query strings. They are indeed useful in some cases, such as this example. They are good to filter results. Filter by something that it’s not a resource. To decide our URIs we must always think of what’s really a resource in our application, and what’s just a filter. Such as in the world wide web, URIs identify Resources. After deciding what’s a resource and what’s just a filter, we can design our URIs much better. Meaningful URIs are among the most important things in RESTFul web services.