Skip to content

fix: correct query param serialization for arrays, dates and null - #329

Merged
oliverlaz merged 1 commit into
mainfrom
fix/query-param-serializer
Aug 18, 2026
Merged

fix: correct query param serialization for arrays, dates and null#329
oliverlaz merged 1 commit into
mainfrom
fix/query-param-serializer

Conversation

@oliverlaz

Copy link
Copy Markdown
Member

queryParamsStringify silently corrupted GET query strings in three ways. All of them failed silently — the request returned 200 with wrong data rather than erroring, so callers got no signal that the parameter was never sent.

The bugs

1. Arrays of objects serialized to [object Object]. The array branch used param.join(','), which stringifies each element via String(). The object branch immediately below it already used JSON.stringify.

{ sort: [{ field: 'created_at', direction: 1 }] }
// before → sort=%5Bobject%20Object%5D
// after  → sort=%5B%7B%22field%22%3A%22created_at%22...

2. Date params lost their key. The instanceof Date branch pushed param.toISOString() with no `${k}=` prefix, dropping the key and injecting a bare, unencoded value into the query string.

{ limit: 10, start_time: new Date('2026-08-15T10:00:00Z') }
// before → limit=10&2026-08-15T10:00:00.000Z    (key gone, server never sees it)
// after  → limit=10&start_time=2026-08-15T10%3A00%3A00.000Z

3. null serialized as the literal string "null". typeof null === 'object', so it took the object branch and JSON.stringify(null) produced id_gt=null on the wire. undefined was already dropped correctly.

Affected endpoints

Audited every *Api.ts under src/gen for GET query params typed as an array of non-scalars or as Date:

Bug Endpoint Param
1 ChatApi.getReplies sort?: SortParamRequest[]
1 VideoApi.queryCallSessionParticipantStats sort?: SortParamRequest[]
2 VideoApi.getCallParticipantSessionMetrics since?: Date, until?: Date
2 VideoApi.getCallStatsMap start_time?: Date, end_time?: Date

getReplies returned unsorted replies, so an ordering assertion failed with no indication that the sort was never sent. For the Video endpoints, any caller passing a time range silently got an unfiltered one.

Not affected: queryChannels, queryThreads, queryReminders and getRetentionPolicyRuns are POST, so their sorts travel in the JSON body. queryUsers, queryMembers and searchRoles are GET but nest sort inside a payload object, which already took the correct JSON.stringify branch.

The fix

Array handling moves to src/utils/query-params.ts, which resolves the wire format in a single pass:

  • Scalar arrays keep the comma-separated form (ids=a,b). Switching those to JSON unconditionally — as stream-chat's axiosParamsSerializer does — would be a breaking wire-format change for every string-array param, so the format is chosen per array. If the backend accepts a JSON array everywhere, this could be simplified; that's an API-owner call.
  • Anything non-scalar is JSON encoded.
  • null/undefined entries within an array are dropped, so a single empty value can't flip a scalar array from a,b to a JSON array.

Verification

Unit tests cover all four wire formats plus the empty-value cases. Beyond that, the encodings were checked against the live API rather than assumed:

  • The server parses and validates the JSON sort array — sort=notjson and a bare (non-array) JSON object are both rejected with not a valid JSON for field 'sort', and a bogus field name is rejected with Sorting is only supported on 'created_at' field.
  • getReplies with limit=2 returns the two oldest replies for direction: 1 and the two newest for direction: -1, confirming the sort is honoured end-to-end. Note the sort selects the page; in-page order is always chronological.
  • getCallStatsMap with ISO start_time/end_time fails on the missing call rather than on the time format, confirming the dates parse.

The full suite shows no new failures; the remaining failures are pre-existing on main (live-API integration tests and a webhook-signature test).

`queryParamsStringify` silently corrupted GET query strings in three ways.
All of them failed silently — the request returned 200 with wrong data
rather than erroring, so callers got no signal the param was never sent.

- Arrays of objects used `join(',')`, stringifying each element via
  `String()` and producing `sort=[object Object]`. They are now JSON
  encoded, which is the format the API expects. Scalar arrays keep the
  comma-separated form (`ids=a,b`), since switching those to JSON would
  be a breaking wire-format change.
- `Date` values were pushed without the `${k}=` prefix, dropping the key
  and injecting a bare value into the query string
  (`?limit=10&2026-08-15T10:00:00.000Z`). They now keep their key and are
  URL encoded.
- `null` took the `typeof param === 'object'` branch and serialized as the
  literal string `id_gt=null`. It is now skipped, matching `undefined`.

`null` and `undefined` entries within an array are dropped as well, so
that a single empty value can't flip a scalar array from `a,b` to a JSON
array. The array handling lives in `utils/query-params.ts` and resolves
the format in a single pass.

Affected endpoints: ChatApi.getReplies and
VideoApi.queryCallSessionParticipantStats (top-level `sort`), plus
VideoApi.getCallParticipantSessionMetrics and VideoApi.getCallStatsMap
(`Date` bounds).

Verified against the live API: the server validates the JSON `sort` array
and honours it for page selection, and accepts the ISO date bounds.
@oliverlaz
oliverlaz requested a review from szuperaz as a code owner August 17, 2026 07:58
@oliverlaz
oliverlaz merged commit 7f228fc into main Aug 18, 2026
14 of 23 checks passed
@oliverlaz
oliverlaz deleted the fix/query-param-serializer branch August 18, 2026 07:35
oliverlaz pushed a commit that referenced this pull request Aug 18, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.7.64](v0.7.63...v0.7.64)
(2026-08-18)


### Features

* update to open api version 233.25.1
([#326](#326))
([618728a](618728a))
* update to open api version 235.16.3
([#328](#328))
([d4f0c22](d4f0c22))


### Bug Fixes

* correct query param serialization for arrays, dates and null
([#329](#329))
([7f228fc](7f228fc))
* order "types" first in package.json exports
([#330](#330))
([747e0c6](747e0c6))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants