[ADP-311] Filtering
TIP
You MAY use a simple ?k=v&a=b approach if logical filtering is not required. However, if your use case necessitates logical operations, it is RECOMMENDED to consider implementing this approach instead of devising a new mechanism. Alternatively, if you have a better idea, you may propose it to the API Design Principle owner for review, as there is no universally accepted 'filtering' standard.
Overview
Filtering is a common feature in RESTful APIs, allowing clients to retrieve only the data that meets specific criteria. This guideline outlines a standardized approach for implementing filtering in RESTful APIs, focusing on nested attributes and other common methods.
Guidance
- SHOULD document filtering methods if your API supports filtering.
- SHOULD use the common query parameter
filter
for resource filtering.
We propose two different filtering approaches: RHS (Right-Hand Side) and LHS (Left-Hand Side).
RHS is suitable for simple queries, while LHS is beneficial for complex logical operations.
RHS Filtering
SHOULD support complex logic combinations with RHS query parameters
httpGET /stores?filter=eq(name,John) HTTP/1.1
SHOULD support nested filters
httpGET /stores?filter=eq(user(name),John) HTTP/1.1
MAY support logical operations
httpGET /stores?filter=gt(registeredAt,2024-07-13T00:00:00) HTTP/1.1
Supported operations:
eq
({property},{value}): equal to the given valuene
({property},{value}): not equal to the given valuegt
({property},{value}): greater than the given valuege
({property},{value}): greater than or equal to the given valuelt
({property},{value}): less than the given valuele
({property},{value}): less than or equal to the given valuein
({property},{value1},{value2},...): contains at least one of the listed valueslike
({property},{value}): contains values similar to the listed expressionsexists
({property}): the given path existsand
({query1},{query2},...): logical ANDor
({query1},{query2},...): logical ORnot
({query}): logical NOT
LHS Filtering
SHOULD support simple LHS filtering using the common
filter
query parameterhttpGET /users?filter.name=John HTTP/1.1
SHOULD support nested filters
httpGET /users?filter.address.region=eu HTTP/1.1
SHOULD support value OR using
,
httpGET /users?filter.role=MANAGER,SUPERVISOR
MAY support logical operations
httpGET /users?filter.createdAt:gt=2024-07-13T00:00:00 HTTP/1.1
Supported operations:
- filter.{property}
:eq
={value}: equal to (can be omitted) - filter.{property}
:ne
={value}: not equal to - filter.{property}
:gt
={value}: greater than - filter.{property}
:ge
={value}: greater than or equal to - filter.{property}
:lt
={value}: less than - filter.{property}
:le
={value}: less than or equal to
- filter.{property}
MAY support comma-separated values for OR operations
httpGET /users?filter.name=John,Jason
MAY add extra namespaces to meet different requirements, surrounding the property part. If implemented, MUST document it thoroughly.
httpGET /licenses?filter=eq(features/featurelist,"nightwatch")
Design Considerations
Why not use property-only design?
A possible design for result filtering is using only the field name, such as:
GET /emaps?region=us&category=self-defined
The main issue with this approach is potential conflicts with common query parameters. As you implement more custom query parameters, it's more likely to collide.
LHS/RHS
LHS (Left-Hand Side) means placing the logic on the left-hand side of the =
sign. RHS is for the right-hand side.
Left-Hand Side brackets
httpfilter[key1][gt]=x
Right-Hand Side brackets
httpfilter[key1]=[gt]x
Left-Hand Side colon
httpfilter.key:gte=y
Right-Hand Side colon
httpfilter.key=gte:x
Example with OpenAPI
RHS Filtering Example
openapi: 3.1.0
info:
title: Stores API
version: 1.0.0
paths:
/stores:
get:
summary: Get stores list
parameters:
- in: query
name: filter
schema:
type: string
example: eq(name,John)
description: RHS filtering parameter
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Store'
components:
schemas:
Store:
type: object
properties:
id:
type: integer
name:
type: string
registeredAt:
type: string
format: date-time
LHS Filtering Example
openapi: 3.1.0
info:
title: Users API
version: 1.0.0
paths:
/users:
get:
summary: Get users list
parameters:
- in: query
name: filter.name
schema:
type: string
example: John
description: Filter by name
- in: query
name: filter.role
schema:
type: string
example: MANAGER,SUPERVISOR
description: Filter by role (supports multiple values)
- in: query
name: filter.createdAt:gt
schema:
type: string
format: date-time
example: '2024-07-13T00:00:00'
description: Filter by creation time (greater than)
responses:
'200':
description: Successful response
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
properties:
id:
type: integer
name:
type: string
role:
type: string
createdAt:
type: string
format: date-time
Reference
Design Reference
- https://eclipse.dev/ditto/2.1/http-api-doc.html
- https://www.moesif.com/blog/technical/api-design/REST-API-Design-Filtering-Sorting-and-Pagination/
Changelog
2024.09.20
Add tips for common cases.