[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
filterfor 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.1SHOULD support nested filters
httpGET /stores?filter=eq(user(name),John) HTTP/1.1MAY support logical operations
httpGET /stores?filter=gt(registeredAt,2024-07-13T00:00:00) HTTP/1.1Supported 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
filterquery parameterhttpGET /users?filter.name=John HTTP/1.1SHOULD support nested filters
httpGET /users?filter.address.region=eu HTTP/1.1SHOULD support value OR using
,httpGET /users?filter.role=MANAGER,SUPERVISORMAY support logical operations
httpGET /users?filter.createdAt:gt=2024-07-13T00:00:00 HTTP/1.1Supported 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}
:empty={true|false}: filter resources where the field is empty (true) or not empty (false)
- filter.{property}
MAY support comma-separated values for OR operations
httpGET /users?filter.name=John,JasonMAY 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-definedThe 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]=xRight-Hand Side brackets
httpfilter[key1]=[gt]xLeft-Hand Side colon
httpfilter.key:gte=yRight-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-timeLHS 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-timeReference
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
2025.07.22Addfilter.{property}:emptyoperation for filtering empty fields in LHS filtering.2024.09.20Add tips for common cases.