Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 21 additions & 17 deletions aep/general/0160/aep.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ APIs **may** support filtering on collection endpoints. If supported:
Examples:

```http request
### Get all books created after 2025-01-01
GET /books?created_after=2025-01-01T00:00:00Z
### Get all books published after 2025-01-01
GET /books?publishedTimeAfter=2025-01-01T00:00:00Z

### Get all published books
GET /books?state=published
Expand All @@ -42,7 +42,8 @@ GET /books?state=published

Different APIs have different resource models and user needs, so the set of filterable fields and supported operators
will vary. These guidelines ensure consistency in _how_ filters are named and composed, while allowing each API to
decide _which_ filters to offer based on real use cases.
decide _which_ filters to offer based on real use cases. Just because an operator is listed here doesn't mean it needs
to be implemented. Instead, this is saying "if you need to implement this parameter, name it this".

Filtering parameter names **must** be stable (do not rename lightly), specific, and self-explanatory.

Expand All @@ -51,18 +52,19 @@ APIs **must** use the direct field name for exact matches (e.g. `state=active`,
APIs **should** use the following prefix/suffix modifiers for common comparisons. Use consistent prefixes/suffixes and
only introduce them when there is more than one plausible comparison. APIs **should** use a subset as appropriate.

| Operator | Meaning | Example |
|-----------|---------------------------------------------------------|-----------------------------|
| | Equals (direct field name) | `status=active` |
| `_ne` | Not equals | `status_ne=cancelled` |
| `_lt`, | Less than | `price_lt=100` |
| `_lte` | Less than or equal | `price_lte=100` |
| `_gt`, | Greater than | `price_gt=0` |
| `_gte` | Greater than or equal | `price_gte=50` |
| `_before` | Timestamp-specific `<` | `created_before=2025-01-01` |
| `_after` | Timestamp-specific `>` | `created_after=2025-01-01` |
| `is_` | Boolean checks | `is_active=true` |
| `has_` | [Nullability and existence](#nullability-and-existence) | `has_phone_number=true` |
| Filtering operation | Pattern | Example |
|---------------------------------------------------------|-------------------------------------------------------------------|-----------------------------------------------------------------------------|
| Equals | `{field}` | `status=ACTIVE` |
| Not equals | `{field}NotEqual` | `statusNotEqual=CANCELLED` |
| Lower bound<br/>(exclusive) `>` | `{field}GreaterThan` | `priceGreaterThan=100` |
| Upper bound<br/>(exclusive) `<` | `{field}LessThan` | `priceLessThan=100` |
| Lower bound<br/>(inclusive) `≥` | `{field}GreaterThanOrEqual`<br/>`min{Field}`<br/>`minimum{Field}` | `priceGreaterThanOrEqual=100`<br/>`minCapacity=50`<br/>`minimumCapacity=50` |
| Upper bound<br/>(inclusive) `≤` | `{field}LessThanOrEqual`<br/>`max{Field}`<br/>`maximum{Field}` | `priceLessThanOrEqual=100`<br/>`maxCapacity=500`<br/>`maximumCapacity=500` |
| Timestamp lower<br/>bound (exclusive) `>` | `{field}After` | `publishedTimeAfter=2025-01-01` |
| Timestamp upper<br/>bound (exclusive) `<` | `{field}Before` | `publishedTimeBefore=2025-01-01` |
| Timestamp lower<br/>bound (inclusive) `≥` | `earliest{Field}` | `earliestPublishedTime=2025-01-01` |
| Timestamp upper<br/>bound (inclusive) `≤` | `latest{Field}` | `latestPublishedTime=2025-01-01` |
| [Nullability and existence](#nullability-and-existence) | `has{Field}` | `hasPhoneNumber=true` |

### Combining filters (`AND`)

Expand Down Expand Up @@ -106,12 +108,12 @@ If a real use case requires it, APIs **should** implement a specialized [search]

Query parameters do not naturally express "field is missing" vs. "field is present but empty". APIs **should**
avoid exposing "missing vs present" semantics unless necessary. If existence filtering is required, APIs **may** add
explicit parameters such as `has_{field}=true|false`
explicit parameters such as `has{field}=true|false`

Example:

``` http request
GET /users?has_phone_number=true
GET /users?hasPhoneNumber=true
```

APIs **must** document exactly what "has" means for each field (non-null, non-empty string, non-empty array, etc.). The
Expand Down Expand Up @@ -173,6 +175,8 @@ guidelines, see

## Changelog

* **2026-02-18**: Change format from underscore (`_`) to `camelCase`. Add to naming conventions table. Use full words
instead of abbreviations.
* **2026-01-27**: Removed search guidance, it will be a separate AEP. Align guidance with [ADR-001].
* **2025-12-11**: Initial creation, adapted from [Google AIP-160][] and aep.dev [AEP-160][].

Expand Down
2 changes: 1 addition & 1 deletion aep/general/0160/aep.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ id: 160
state: approved
slug: filtering
created: 2025-12-11
updated: 2026-01-27
updated: 2026-02-18
placement:
category: design-patterns