Category: Query Parameters
Description
Collection GET endpoints must support filtering query parameters beyond pagination to allow clients to retrieve specific subsets of data based on criteria.
What It Checks
This rule validates that GET operations on collections (endpoints returning arrays) include query parameters for filtering results. It checks:
- GET operations that return arrays or collections
- Collection endpoints identified by plural nouns (e.g.,
/users,/products) - The presence of query parameters beyond standard pagination parameters
- Excludes singleton resource endpoints (those with path parameters like
{id}) - Excludes verb endpoints (e.g.,
/users/search)
The rule automatically filters out common pagination parameters before checking for filters:
- Page number style:
page,page_number,size,per_page - Limit/offset style:
limit,page_size,offset,page_start_index - Cursor style:
cursor - Time-based style:
since,until
Why It Matters
Supporting filter parameters on collections is essential for building practical, usable APIs:
- Efficiency: Clients can request only the data they need, reducing payload size and processing time
- Performance: Filtering at the server level is more efficient than retrieving all data and filtering client-side
- Usability: Enables clients to build meaningful features like search, status filtering, and data exploration
- Scalability: Reduces network bandwidth and server load by returning smaller, targeted datasets
- User Experience: Allows building responsive interfaces with quick, filtered data retrieval
Examples
Good ✅
User Collection with Status Filter
paths:
/users:
get:
summary: List users with filtering
parameters:
- name: page
in: query
schema:
type: integer
- name: per_page
in: query
schema:
type: integer
- name: status
in: query
description: Filter by user status
schema:
type: string
enum: [active, inactive, pending]
- name: role
in: query
description: Filter by user role
schema:
type: string
responses:
'200':
description: Filtered list of users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Product Collection with Multiple Filters
paths:
/products:
get:
summary: List products
parameters:
- name: limit
in: query
schema:
type: integer
- name: offset
in: query
schema:
type: integer
- name: category
in: query
description: Filter by product category
schema:
type: string
- name: min_price
in: query
description: Minimum price filter
schema:
type: number
- name: max_price
in: query
description: Maximum price filter
schema:
type: number
- name: in_stock
in: query
description: Filter to only in-stock items
schema:
type: boolean
responses:
'200':
description: Filtered products
Order Collection with Date Range Filter
paths:
/orders:
get:
summary: List orders
parameters:
- name: cursor
in: query
schema:
type: string
- name: status
in: query
description: Filter by order status
schema:
type: string
enum: [pending, processing, shipped, delivered, cancelled]
- name: customer_id
in: query
description: Filter by customer ID
schema:
type: string
- name: created_after
in: query
description: Filter orders created after this date
schema:
type: string
format: date-time
- name: created_before
in: query
description: Filter orders created before this date
schema:
type: string
format: date-time
responses:
'200':
description: Filtered orders
Bad ❌
Collection with Only Pagination (No Filters)
paths:
/users:
get:
summary: List users
parameters:
- name: page
in: query
schema:
type: integer
- name: per_page
in: query
schema:
type: integer
# No filter parameters - always returns all users for the page
responses:
'200':
description: Paginated users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Products Collection Without Filters
paths:
/products:
get:
summary: List all products
parameters:
- name: limit
in: query
schema:
type: integer
- name: offset
in: query
schema:
type: integer
# Missing filter parameters like category, price range, etc.
responses:
'200':
description: All products
Orders with No Query Parameters at All
paths:
/orders:
get:
summary: Get all orders
# No parameters at all - neither pagination nor filters
responses:
'200':
description: Complete list of orders
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Order'
Acceptable Scenarios
The rule allows certain endpoints to skip filter requirements:
-
Singleton Resources: Endpoints that retrieve a single item
/users/{userId} # Single user retrieval -
Verb Endpoints: Action-oriented endpoints
/users/search # Search is a verb, different pattern -
Non-Collection Responses: GET endpoints that don't return arrays
/dashboard/summary # Returns single summary object
Configuration
This rule has no configuration options. It is either enabled or disabled. The list of acceptable pagination parameters is hardcoded based on industry-standard pagination styles.
Common Filter Patterns
Status Filtering
- name: status
in: query
schema:
type: string
enum: [active, inactive, pending]
Relationship Filtering
- name: user_id
in: query
description: Filter by associated user
schema:
type: string
- name: organization_id
in: query
description: Filter by organization
schema:
type: string
Range Filtering
- name: min_price
in: query
schema:
type: number
- name: max_price
in: query
schema:
type: number
- name: created_after
in: query
schema:
type: string
format: date-time
- name: created_before
in: query
schema:
type: string
format: date-time
Boolean Flags
- name: is_featured
in: query
schema:
type: boolean
- name: in_stock
in: query
schema:
type: boolean
Multi-Value Filtering
- name: categories
in: query
description: Filter by multiple categories
schema:
type: array
items:
type: string
style: form
explode: true
How to Fix
1. Identify Filterable Attributes
Analyze your resource model and identify which fields make sense for filtering:
// User resource
{
id: "123",
name: "John Doe",
email: "[email protected]",
status: "active", // Good filter candidate
role: "admin", // Good filter candidate
created_at: "2024-01-15", // Good for date range filtering
department_id: "dept-5" // Good for relationship filtering
}
2. Add Filter Parameters to Your Spec
Add query parameters for each filterable field:
# Before
/users:
get:
summary: List users
parameters:
- name: page
in: query
schema:
type: integer
# After
/users:
get:
summary: List users with filtering
parameters:
- name: page
in: query
schema:
type: integer
- name: status
in: query
description: Filter by user status
schema:
type: string
enum: [active, inactive, pending]
- name: role
in: query
description: Filter by user role
schema:
type: string
- name: department_id
in: query
description: Filter by department
schema:
type: string
- name: created_after
in: query
description: Filter users created after this date
schema:
type: string
format: date-time
3. Implement Server-Side Filtering
Ensure your backend actually implements the filtering logic:
// Example Express.js implementation
app.get('/users', async (req, res) => {
const { page = 1, per_page = 20, status, role, department_id, created_after } = req.query;
let query = db.select().from('users');
// Apply filters
if (status) {
query = query.where('status', status);
}
if (role) {
query = query.where('role', role);
}
if (department_id) {
query = query.where('department_id', department_id);
}
if (created_after) {
query = query.where('created_at', '>=', created_after);
}
// Apply pagination
const offset = (page - 1) * per_page;
query = query.limit(per_page).offset(offset);
const users = await query;
res.json({ data: users, page, per_page });
});
4. Document Filter Behavior
Clearly document how filters combine:
/products:
get:
summary: List products
description: |
Retrieve a filtered list of products. All filters are applied with AND logic.
For example, `?category=electronics&in_stock=true` returns only in-stock electronics.
parameters:
- name: category
in: query
description: Filter by product category
schema:
type: string
- name: in_stock
in: query
description: Filter to only in-stock items
schema:
type: boolean
5. Consider Common Use Cases
Add filters that support your API's primary use cases:
- E-commerce: price range, category, availability, brand
- User management: status, role, department, registration date
- Content: author, publication date, category, tags
- Orders: status, customer, date range, total amount range
Related Rules
- Rule 48: Pagination - Ensures collections support pagination (this rule excludes those params)
- Rule 23: Query Fields GET - Supports field selection for partial responses
- Rule 1: Plural Collections - Ensures collection endpoints use plural nouns
Best Practices
-
Combine Filters with AND Logic: When multiple filters are provided, apply them all (AND logic is most intuitive)
-
Support Common Patterns: Include filters for status, relationships, date ranges, and boolean flags
-
Use Enums for Fixed Values: When filter values are limited, use enums to document valid options
-
Document Default Behavior: Clearly state what happens when no filters are provided
-
Validate Filter Values: Ensure filter parameters are validated server-side to prevent injection attacks
-
Consider Performance: Add database indexes on frequently filtered fields
-
Return Empty Arrays: When filters match no results, return an empty array (not an error)
-
Case Sensitivity: Document whether string filters are case-sensitive
-
Partial Matching: Decide whether string filters require exact matches or support partial matching (document this clearly)