The Filtering and sorting pattern helps clients to retrieve resources based upon specific criteria and sort order.

14.1. Filtering and Sorting Conventions (query parameters, filter syntax)

Filtering and sorting are applied to the GET operation of a resource collection as query arguments:

  • Simple equality:
    • GET /customers?status=ACTIVE
  • Multiple values:
    • GET /customers?status=ACTIVE&status=ACTIVE,PENDING
  • Range filters:
    • fromDate, toDate, minTotalAmount, maxTotalAmount
  • Sorting:
    • sort=createdAt or sort=-createdAt
    • Structured sort object in body for POST-based search (e.g., /search-orders).

14.2. When to Use This Pattern

Use this pattern when the number of filters is limited and the filtering can be expressed with query arguments. Switch to a POST with a structured request body when more complex queries are needed.

Filtering and sorting need consistent patterns when applied, as not to confuse developers and machine-based consumers.

14.3. When NOT to Use This Pattern

  • If the number of filters exceeds a predetermined number, e.g., 20 fields, or require more expressive queries, use the Query / Search Model pattern instead.

  • Avoid using this pattern when filters contain PII/NPI data, as those values may be (and likely are) logged by intermediary servers, such as CDNs, API gateways, and in client and server log files. Use the Query / Search Model pattern to move sensitive data into the body, which is commonly not logged in web access logs.

14.4. Filtering OpenAPI Example

paths:
  /customers:
    get:
      operationId: listCustomers
      summary: List customers with equality and multi-value filters
      description: |
        Retrieves customers using **query parameter filters**.
        
        **Filter Logic**:
        - **AND**: Multiple different parameters combine with AND logic
          - `?status=ACTIVE&region=US` → status=ACTIVE AND region=US
        - **OR**: Comma-separated values within a parameter use OR logic
          - `?status=ACTIVE,PENDING` → status=ACTIVE OR status=PENDING
        
        **Examples**:
        - `GET /customers?status=ACTIVE` — Active customers only
        - `GET /customers?status=ACTIVE,PENDING` — Active OR Pending customers
        - `GET /customers?status=ACTIVE&region=US` — Active customers in US
        - `GET /customers?status=ACTIVE,PENDING&region=US,CA` — (Active OR Pending) AND (US OR CA)
      tags: [Customers]
      parameters:
        # --- Simple Equality Filter ---
        - name: status
          in: query
          description: |
            Filter by customer status.
            - Single value: `status=ACTIVE`
            - Multiple values (OR logic): `status=ACTIVE,PENDING`
          required: false
          schema:
            type: string
            example: ACTIVE,PENDING
          examples:
            single:
              summary: Single status filter
              value: ACTIVE
            multiple:
              summary: Multiple statuses (OR)
              value: ACTIVE,PENDING

        # --- Simple Equality Filter ---
        - name: region
          in: query
          description: |
            Filter by geographic region code.
            - Single value: `region=US`
            - Multiple values (OR logic): `region=US,CA,MX`
          required: false
          schema:
            type: string
            example: US
          examples:
            single:
              summary: Single region
              value: US
            multiple:
              summary: Multiple regions (OR)
              value: US,CA,MX

        # --- Simple Equality Filter ---
        - name: customerType
          in: query
          description: |
            Filter by customer type.
            - `INDIVIDUAL`: Personal/consumer accounts
            - `BUSINESS`: Business/corporate accounts
          required: false
          schema:
            type: string
            enum: [INDIVIDUAL, BUSINESS]
            example: BUSINESS

        # --- Simple Equality Filter ---
        - name: accountManagerId
          in: query
          description: |
            Filter by assigned account manager (UUID).
            Returns customers managed by specified employee.
          required: false
          schema:
            type: string
            format: uuid
            example: 7c9e6679-7425-40de-944b-e07fc1f90ae7

        # --- Pagination (using offset for this example) ---
        - name: offset
          in: query
          description: Zero-based starting index.
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: limit
          in: query
          description: Maximum items to return.
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20

      responses:
        '200':
          description: Filtered customer list.
          headers:
            X-Total-Count:
              description: Total customers matching filter criteria.
              schema:
                type: integer
                example: 342
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Customer'
              example:
                - customerId: "550e8400-e29b-41d4-a716-446655440001"
                  name: "Acme Corporation"
                  email: "contact@acme.com"
                  status: "ACTIVE"
                  region: "US"
                  customerType: "BUSINESS"
                  createdDate: "2024-01-10T09:00:00Z"
                - customerId: "550e8400-e29b-41d4-a716-446655440002"
                  name: "TechStart Inc"
                  email: "info@techstart.com"
                  status: "PENDING"
                  region: "US"
                  customerType: "BUSINESS"
                  createdDate: "2024-02-15T11:30:00Z"
        '400':
          description: Invalid filter parameter.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
              example:
                type: "https://api.example.com/problems/invalid-parameter"
                title: "Invalid Filter Parameter"
                status: 400
                detail: "Invalid status value 'UNKNOWN'. Allowed values: ACTIVE, PENDING, SUSPENDED, CLOSED."
                instance: "/customer-management/v1/customers"

14.5. Sorting OpenAPI Example

paths:
  /customers:
    get:
      operationId: listCustomers
      summary: List customers with sorting options
      description: |
        Retrieves customers with **sorting** via query parameters.
        
        **Sorting Convention**:
        - `sort`: Field name to sort by
        - `sortOrder`: Direction (`asc` for ascending, `desc` for descending)
        
        **Examples**:
        - `?sort=createdDate&sortOrder=desc` — Newest customers first
        - `?sort=name&sortOrder=asc` — Alphabetical by name
        - `?sort=totalSpend&sortOrder=desc` — Highest spenders first
        
        **Default behavior**:
        - If `sort` is omitted: results sorted by `createdDate`
        - If `sortOrder` is omitted: defaults to `desc` (descending)
        
        **Combining with filters**:
        - `?status=ACTIVE&sort=totalSpend&sortOrder=desc` 
          → Active customers sorted by highest spend
      tags: [Customers]
      parameters:
        # --- Sorting Parameters ---
        - name: sort
          in: query
          description: |
            Field name to sort results by.
            Must be a sortable field (not all fields support sorting).
          required: false
          schema:
            type: string
            enum:
              - createdDate
              - updatedDate
              - name
              - email
              - totalSpend
              - orderCount
              - status
            default: createdDate
            example: createdDate

        - name: sortOrder
          in: query
          description: |
            Sort direction.
            - `asc`: Ascending (A→Z, oldest→newest, lowest→highest)
            - `desc`: Descending (Z→A, newest→oldest, highest→lowest)
          required: false
          schema:
            type: string
            enum: [asc, desc]
            default: desc
            example: desc

        # --- Filters (for combining with sort) ---
        - name: status
          in: query
          description: Filter by status (comma-separated for OR).
          required: false
          schema:
            type: string
            example: ACTIVE

        # --- Pagination ---
        - name: offset
          in: query
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20

      responses:
        '200':
          description: Sorted customer list.
          headers:
            X-Total-Count:
              description: Total customers matching filter criteria.
              schema:
                type: integer
                example: 250
            X-Sort:
              description: The sort field applied.
              schema:
                type: string
                example: totalSpend
            X-Sort-Order:
              description: The sort direction applied.
              schema:
                type: string
                enum: [asc, desc]
                example: desc
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Customer'
              examples:
                sortedBySpend:
                  summary: Sorted by total spend (descending)
                  value:
                    - customerId: "550e8400-e29b-41d4-a716-446655440001"
                      name: "MegaCorp Industries"
                      email: "procurement@megacorp.com"
                      status: "ACTIVE"
                      totalSpend: 125000.00
                      createdDate: "2023-06-15T10:00:00Z"
                    - customerId: "550e8400-e29b-41d4-a716-446655440002"
                      name: "Global Solutions"
                      email: "billing@globalsolutions.com"
                      status: "ACTIVE"
                      totalSpend: 89500.00
                      createdDate: "2023-09-22T14:30:00Z"
                    - customerId: "550e8400-e29b-41d4-a716-446655440003"
                      name: "StartupXYZ"
                      email: "founder@startupxyz.io"
                      status: "ACTIVE"
                      totalSpend: 45200.00
                      createdDate: "2024-01-08T09:15:00Z"
                sortedByName:
                  summary: Sorted by name (ascending)
                  value:
                    - customerId: "550e8400-e29b-41d4-a716-446655440004"
                      name: "Alpha Tech"
                      email: "info@alphatech.com"
                      status: "ACTIVE"
                      totalSpend: 32000.00
                      createdDate: "2024-02-12T11:00:00Z"
                    - customerId: "550e8400-e29b-41d4-a716-446655440005"
                      name: "Beta Industries"
                      email: "contact@betaind.com"
                      status: "ACTIVE"
                      totalSpend: 18500.00
                      createdDate: "2024-03-05T08:45:00Z"
                    - customerId: "550e8400-e29b-41d4-a716-446655440006"
                      name: "Gamma Corp"
                      email: "sales@gammacorp.com"
                      status: "ACTIVE"
                      totalSpend: 67800.00
                      createdDate: "2023-11-20T16:20:00Z"
        '400':
          description: Invalid sort parameter.
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetails'
              example:
                type: "https://api.example.com/problems/invalid-parameter"
                title: "Invalid Sort Parameter"
                status: 400
                detail: "The field 'lastLoginDate' is not sortable. Sortable fields: createdDate, updatedDate, name, email, totalSpend, orderCount, status."
                instance: "/customer-management/v1/customers"