How to support filters to your restful APIs with Spring Data MongoDB
Published: 2019-11-20 18:00:01

---
title: How to support filters to your restful APIs with Spring Data MongoDB
published: true
description: An easy way to add filtering, pagination, sorting, searching on your REST APIs with Spring Data MongoDB
tags: 
---


## Introduction

Writing truly Restful APIs is not simple. There are a lot of features that need to be covered to call an API restful. One aspect of those features is providing parameters to filter and manipulate the data of a resource. Two months ago I was practicing my TDD skills, and I decided to build a simple blog engine using Spring Boot + Spring Data MongoDB (technologies that I never used before). So I got to a point that I needed to support filtering, selecting, sorting and pagination. I realized there was nothing "simple" that I could use, so I build a custom solution and published it on the [Maven Central Repository](https://search.maven.org/artifact/com.github.apavlidi/restfulQueries/0.0.1/jar).

## Concept

We will build an endpoint that supports filtering, selecting, sorting and pagination using Spring Boot + Spring Data MongoDB. We will build it in a way that implementing it to other controllers would be straight forward.
To support these filters we have to accept a parameter to our controller, so we have to define a <b>RequestParam</b>.

The paramater must be a type of Map which will represent our filters. The valid keys of the map should be <i>sort</i>, <i>select</i>, <i>q</i>, <i>pageSize</i>, <i>page</i> representing our different filters that can be applied to our resource.
For example to limit the results of your query you have to add the parameter pageSize with value a a number greater than or equal to 0 to the URL.

> GET /blogs?pageSize=5


The pageSize parameter will be a key to our <b>RequestParam</b> map variable with a value the number defined to the URL.

There are two steps for implementing this.

1. Collect the URL parameters and pass them to the repository.
2. Translate the URL parameters in a way MongoDB understands.

## Implementation


Let's begin by defining our Rest Controller.

{% gist https://gist.github.com/apavlidi/a51ce86936cc9c347cab012c8a8ea8d3 %}


After having our filters as a parameter we have to validate their existence and collect them to pass them to our service and eventually to our repository. This function should be in a separate class from our Rest Controller to be reusable to other controllers as well.

{% gist https://gist.github.com/apavlidi/db83075f2d21f21ca9faa9f699a6df04 %}

The <b>collectRestApiParams</b> function will accept the filters from a controller and check if there are valid keys defined as a parameter to the URL and if there are it will add them to a new map. It's responsible for collecting all parameters, so for each one, we have a separate function.
The collect functions are pretty simple, they just check if there is a valid key in the filter's map and adds it to our new map called <b>restApiQueries</b>.

For example the <b>collectPageFilter</b> checks if there is a key "page" and just adds it to <b>restApiQueries</b>.

{% gist https://gist.github.com/apavlidi/f8dc923290f1f058cde3b505c37d3830 %}


The <b>collectRestApiParams</b> will be called from inside our Rest Controller and the returned value will be a map of valid keys to be passed to the service.

{% gist https://gist.github.com/apavlidi/ce34016f878347470f1db8db1161177f %}

The service will delegate these filters to the repository and with that, we finished the first step: ~~Collect the URL parameters and pass them to the repository~~ :heavy_check_mark:


The next step is to translate these filters in a way that MongoDB understands. To do that we will use a <b>Query</b> object and apply MongoDB filters upon it before the database call. Our repository function looks like this:

{% gist https://gist.github.com/apavlidi/3f1254395bef41bf8f2fdfeb126917d2 %}

The last missing part is the functions that are applying these filters to the <b>query</b> object. So let's define the functions that translate the parameters to MongoDB filters. The function <b>applyRestApiQueries</b> is responsible for applying all parameters to filters of the <b>query</b> object and should be to a separate class for reusability purposes.

{% gist https://gist.github.com/apavlidi/b60f54112c6c4509681170ad18211093 %}
 
 > This <b>query</b> object is received from the repository function before the database call.

Now let's write the functions that apply each parameter separately to the <b>query</b> object.<br>


The <b>applyPageQueryParam</b> takes the value of the key "page" and applies the "skip" filter to our query object by calling `query.skip(skip)`.

{% gist https://gist.github.com/apavlidi/97f4785d1375e9743d9d6e1a43fd716e %}

>  Because we are passing the query object by reference, any changes made to this object will be affected to the repository's query object as well.


The <b>applyPageSizeQueryParam</b> takes the value of the key "pageSize" and applies the "limit" filter to our query object by calling `query.limit(limit)`.

{% gist https://gist.github.com/apavlidi/23c127aff5989ccae479066577f383cb %}

The <b>applySortQueryParam</b> takes the value of the "sort" key which is a field name of a resource that represents the sorting field. If the field has a minus "-" symbol in front of it, it means the direction is descending otherwise it's ascending. After storing its value, we apply the "sort" filter to our query object by calling `query.with(new Sort(sortDir, sortBy))`.


{% gist https://gist.github.com/apavlidi/475bb53e0f9792bbb0510b0bf570aa56 %}


The <b>applySelectQueryParam</b> takes the value of the "select" which is a string of field names of a resource separated by a comma. Then split the fields with the comma delimiter and we append each field by using `query.fields().include(selectedField)`.


{% gist https://gist.github.com/apavlidi/b96993f465e0fc9ffb24a272713a6962 %}


The <b>applySearchQueryParam</b> is the trickiest one. The value of the "q" key in the map is a JSON object, so we have to parse the JSON and pass it to the query by calling ` query.addCriteria(Criteria.where(field).is(value))`.

{% gist https://gist.github.com/apavlidi/d1beb37bfb73a6b35f376d1497b0a4d6 %}

The second step is done: ~~Translate the URL parameters in a way MongoDB understands~~ :heavy_check_mark:


## Testing time

Here is our testing resource:

{% gist https://gist.github.com/apavlidi/3c29056660d37a3ed9718049c491bd20 %}

Let's try to sort our blogs by title with descending order.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/zb7gmg2secclmmih39ve.png)

We can select specific fields from the blogs, such as id, title.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/zqthb3ifv3o2hu4ngdc9.png)

We can also limit the results and retrieve the second page.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/79uaalp2l3cema9o76tt.png)

> Note that the page starts from index 0.

Now let's search for a blog with the text "This is content 1".

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/sl1zc3ptdpfhdk3rgawe.png)


## Conclusion
The implementation is pretty simple, we just added two extra steps to our flow to support the filters, the collection of the parameters which will take place on our Rest Controller, and the filter applier which will take place before the database call. 

Here is a sequence diagram of our process
![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/g1k5l6ywf7vudy3r6v96.png)


As I said before I published this code to the Maven Central Repository so you can support filters to your application by just adding the [dependency](https://search.maven.org/artifact/com.github.apavlidi/restfulQueries/0.0.1/jar).
Read the [documentation](https://github.com/apavlidi/RestfulQueries/wiki/API-Documentation) for more details.

## Source code
Available on [GitHub](https://github.com/apavlidi/RestfulQueries.git).