Context
If we start looking at any REST style architecture/guidelines, it always talk about resource and GET
,PUT
,POST
,DELETE
operations around it. However, search for a resource is always debatable. The most common notation is to use the GET
with /<resource>?<search params>
approach. This approach is easy to implement, satisfies the desinging architects 👔 and reasonably flexible.
In this article I will try to mention some of the issues and possible approaches to how this can be implemented in a different and extensible way.
Issues
Let's start by listing some of the major issues with this approach.
Leaking Personal Information
GDPR 😉, I don't need to say anything. Most of the corporate systems have access logging enabled and they log the requested URL (with query params). Now if the URL start containing sensitive or personal information, the solution is not good enough.
Limitation of filter
With query params, there is a limit to the number of filters that can be added. Now if your data set has lots of numeric values and requirement is to support multiple search options, you would end up with fields like amount_less_than
, amount_greater_than
, amount_equals
for all the fields. What if the requirement is to suddenly add "less than and equal to" option also 😁.
Approaches
One of the thing that came to the mind is to provide a search on POST
request. This is all good, but how would the body look like 😕.
No Brain approach
The No Brain approach is the easiest one. All the query filters, can be made part of the Search Request Object. Example
{
"amount_greater_than": 10,
"amount_less_than": 20,
"active": true
}
You already know my opinion on this one, so let's move to the next one.
Generic Filter
The simplest alternative, just accept a bunch of generic filters. Something like
{
"filters": [
{
"field": "AMOUNT",
"operation": "LT",
"value": "20"
},
{
"field": "ACTIVE",
"operation": "EQ",
"value": "true"
}
]
}
✔️ This is quite extensive and can be used for new filters without making any change to the consumers.
❌ One drawback is that the field type information is lost. Everything is String.
❌ Another drawback is that the unsupported filter could also be requested. So informing what all are supported for each field type is tricky.
❌ Another one is that multiple similar filters can be used, which makes API responses confusing. e.g. LT 10 (amount less than 10) and LT 20 could be requested. No harm, but confusing.
Filters per field
The slight variation of the above approach but addresses the major drawback is to have filters per field type. This way the type information is preserved. A sample API request could be like
{
"active": {
"operation": "EQ",
"value": true
},
"amount": [
{
"operation": "LT",
"value": 20
}
]
}
✔️ Field type information is preserved and communicated properly.
✔️ More flexibility in choosing the operations supported per field type. Also, the API will communicate to the consumer, if the field supports multiple filters (like amount) or only one (like active).
❌ Still there is a possibility that the caller might use the same filter for a field twice.
DSL type filter per field
Going a step further, what if we define the operations per field as possible fields in json. A sample request for such a filter could be modelled as follows
{
"active": {
"eq": true
},
"amount": {
"eq": 0,
"ge": 0,
"gt": 0,
"le": 0,
"lt": 0
}
}
✔️ Clean/well documented API with listing all the capabilities possible.
✔️ Extensible, if ne
(not equal) filter is required for a field, it can just be added.
✔️ Consistency and easy to follow request.
Java example
In Java, this can be modelled with the help of Generics as follows
@Data
class FilterEQ<T> { //Class for EQ filter
private T eq;
}
@Data
class Filter<T> extends FilterEQ<T> { //Filter extending EQ
private T lt;
private T le;
private T gt;
private T ge;
}
@Data
public class Filters { //Request Object
private Filter<Long> amount;
private FilterEQ<Boolean> active;
}
Conclusion
Providing Search API in REST design is not easy and for really extensible use cases, GraphQL is the best approach. However, it is not always possible to get the best desk in the office 😊. We have to choose the best seat from the available ones.
Here, I have given some ideas on how to design and improve a Search API. According to me, the DSL type Filter per field is very close to how one would develop a search API in GraphQL. But maybe there is a better approach. Do let me know your thoughts on the same. Please like and share with friends if you see some merits in the process and arguments I have listed.
Top comments (0)