feat: Add pagination args to getConversations - #49
Merged
Conversation
olderThan (Date), beforeId, and limit page through /conversations/get. Paired olderThan+beforeId forms the strict compound (last_active, id) boundary added in twist-new-backend#690. The Date converts to older_than_ts in the client because the transport's generic snake-casing turns a Date into an empty object. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
doistbot
reviewed
Jul 10, 2026
doistbot
left a comment
Member
There was a problem hiding this comment.
This PR adds optional pagination arguments (olderThan, beforeId, limit) to getConversations, enabling cursor-based paging to exhaustion so quiet conversations no longer go missing.
Few things worth tightening:
- Guard the empty-page case: When a page has zero conversations (empty workspace or final page), dereferencing
last.lastActivewill throw. Break the pagination loop on an empty page or only issue the next request whenlastexists. - Encode the compound-cursor contract in the type: The current
GetConversationsArgstype permitsbeforeIdwithoutolderThan, but the documented strict cursor requires both together. A union or refinement would prevent consumers from constructing requests with undefined pagination semantics. - Prefer explicit field-by-field params construction: The spread pattern (
const { olderThan, ...rest } = args) implicitly forwards all remaining schema fields, unlike the explicit picking ingetThreads/getComments. If a futurez.date()field is added to the schema without a matching conversion,snakeCaseKeyswould silently turn it into{}on the wire. Matching the sibling methods' style makes that bug class structurally impossible.
I also included a few optional follow-up notes in the details below.
Optional follow-up note (1)
src/clients/conversations-client.ts:78: The spread pattern (
const { olderThan, ...rest } = args; const params = { ...rest }) differs from the explicit field-by-field construction ingetThreadsandgetComments. Those methods pick each field individually (e.g.if (args.channelId != null) params.channelId = args.channelId), making the field-to-wire-key mapping explicit and auditable. The spread approach implicitly forwards all remaining schema fields: if a future dev adds anotherz.date()toGetConversationsArgsSchemawithout adding a matching conversion here,snakeCaseKeyswould turn it into{}andparamsSerializerwould serialize[object Object]on the wire. The test guards against this forolderThanspecifically, but the explicit pattern used in sibling methods makes that class of bug structurally impossible rather than test-caught.
doistbot
reviewed
Jul 10, 2026
doistbot
left a comment
Member
There was a problem hiding this comment.
This PR adds support for cursor-based pagination in getConversations by introducing optional olderThan, beforeId, and limit arguments with safe Date serialization on the wire.
Few things worth tightening:
- Empty-page crash guard: The JSDoc paging example throws a TypeError when a page is empty (as
page[page.length - 1]is undefined); adding a quickif (page.length === 0) breakguard will prevent developers from copying a crashing pattern. - Strict cursor validation: The schema currently permits
beforeIdwithoutolderThan, which can result in undefined pagination behavior. Consider using a union or.refine()to enforce thatbeforeIdrequiresolderThanat the type level. - Explicit parameter mapping: Instead of spreading arguments implicitly into
snakeCaseKeys, using the explicit field-by-field assignment pattern from sibling methods (likegetThreads) would prevent future date/object fields from accidentally getting serialized incorrectly.
I also included a few optional follow-up notes in the details below.
Optional follow-up note (1)
src/clients/conversations-client.ts:78: The spread pattern (
const { olderThan, ...rest } = args; const params = { ...rest }) implicitly forwards every remaining schema field throughsnakeCaseKeys, unlike the explicit field-by-field construction ingetThreads(lines 82–88) andgetComments(lines 58–62). Those sibling methods pick each field individually withif (args.X != null) params.X = args.X, making the field-to-wire-key mapping explicit and auditable. If a futurez.date()field is added toGetConversationsArgsSchemawithout a matching conversion here,snakeCaseKeyswould turn it into{}andparamsSerializerwould serialize[object Object]on the wire — a bug the explicit pattern makes structurally impossible.
Explicit field-by-field params (matching getThreads/getComments) so a future Date field cannot silently hit the generic snake-casing; the doc example no longer dereferences an empty page; beforeId's standalone id-order mode is documented. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
doist-release-bot Bot
added a commit
that referenced
this pull request
Jul 10, 2026
## [0.11.0](v0.10.0...v0.11.0) (2026-07-10) ### Features * Add pagination args to getConversations ([#49](#49)) ([65f1217](65f1217))
Contributor
|
🎉 This PR is included in version 0.11.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
getConversationscould only fetch one server-default page (20 rows), which is how quiet conversations went missing fromtdc conversation list("Disappearing group DMs"). Consumers need a cursor to page to exhaustion.What was changed
GetConversationsArgsSchema: optionalolderThan(Date),beforeId,limit.olderThan→older_than_tsexplicitly (the transport's generic snake-casing turns aDateinto an empty object);beforeId/limitride the normal snake-casing.olderThan + beforeIdtogether form the strict compound(last_active, id)cursor from Doist/twist-new-backend#690; doc block explains the paging recipe.{ workspaceId }call is unchanged.Out of scope
olderThan-only paging keeps legacy inclusive semantics.Refs
🤖 Generated with Claude Code