Lesson 13 / 25

Pagination, Filtering and Naming Conventions

Describe common API conventions consistently in OpenAPI.

Consistency makes APIs predictable

OpenAPI makes conventions visible, so decide them once and apply them everywhere, ideally captured in an API style guide enforced by linting. Naming: plural nouns for collections (/orders), kebab-case paths, consistent property casing (camelCase or snake_case, never mixed), and verbs only for genuine actions (/orders/{id}/cancellation as a resource, or :cancel if your guide allows). Pagination: prefer cursor-based pagination for large or changing collections, with limit and cursor parameters and a response envelope containing items and nextCursor; offset pagination is simpler but slower and inconsistent under concurrent inserts. Filtering and sorting: query parameters such as status, createdAfter and sort=-createdAt. Partial responses: optional fields parameters. Dates: ISO 8601 date-time in UTC. IDs: opaque strings, possibly prefixed by type (ord_...). Model these patterns as reusable components (a Page schema, standard parameters) so every list endpoint looks the same.

Consistent list endpoints

Every collection returns the same page shape with items and a cursor to the next page.

Three identical page cards stacked with an arrow from the bottom corner of each to the next.
Figure 5.1 — Cursor pagination with a standard page envelope.

A reusable page envelope for cursor pagination

Every list endpoint returns the same structure.

components:
  schemas:
    OrderPage:
      type: object
      required: [items, nextCursor]
      properties:
        items:
          type: array
          items: { $ref: '#/components/schemas/Order' }
        nextCursor:
          type: [string, 'null']
          description: Pass as ?cursor= to get the next page; null when there are no more items
  parameters:
    Sort:
      name: sort
      in: query
      description: Field to sort by; prefix with - for descending
      schema: { type: string, enum: [createdAt, -createdAt, total, -total], default: -createdAt }
    CreatedAfter:
      name: createdAfter
      in: query
      schema: { type: string, format: date-time }

paths:
  /orders:
    get:
      operationId: listOrders
      parameters:
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
        - $ref: '#/components/parameters/Sort'
        - $ref: '#/components/parameters/CreatedAfter'
      responses:
        '200':
          description: A page of orders
          content:
            application/json:
              schema: { $ref: '#/components/schemas/OrderPage' }

Write the style guide as lint rules

A style guide in a wiki is forgotten; the same rules expressed as Spectral or Redocly lint rules run on every pull request and keep dozens of APIs consistent.

Quick check: Why is cursor-based pagination often preferred for large collections?

  • It stays fast and consistent as data changes, unlike large offsets
  • It is easier to implement than every alternative
  • It returns all items at once
  • OpenAPI only supports cursors
Answer

It stays fast and consistent as data changes, unlike large offsets — Cursors avoid slow deep offsets and skipped or duplicated items when rows are inserted.