Skip to content

Type pagination metadata on nested toMany relations - #26

Open
kac-axelor wants to merge 1 commit into
axelor:devfrom
kac-axelor:pagination-metadata-on-toMany
Open

kac-axelor wants to merge 1 commit into
axelor:devfrom
kac-axelor:pagination-metadata-on-toMany

Conversation

@kac-axelor

Copy link
Copy Markdown
Contributor

Summary

Nested toMany selects already receive _count / _cursor / _hasNext / _hasPrev at runtime when paginated (they recurse through the same doQuery path), but the result types only exposed those fields at the top level. Reading them on a nested record forced an as any.

This types the metadata uniformly:

  • Extract the four fields into a reusable PaginationMeta type.
  • Intersect it onto the nested toMany element type in PayloadSelectArg.

No runtime change — query.ts already does the right thing. Because find, findOne, create, and update all return Payload/CreatePayload (which share the PayloadSelectArg branch), the fix covers all four.

Test plan

  • Extended client-pagination.test.ts with a nested-pagination test that reads the metadata off nested records without as any across find / findOne / create / update.
  • All 6 pagination e2e tests pass; tsc clean for source + test.

Nested toMany selects already receive _count/_cursor/_hasNext/_hasPrev at
runtime when paginated, but the result types only exposed them at the top
level, forcing casts to read them on nested records.

Extract the four fields into a reusable PaginationMeta type and intersect it
onto the nested toMany element type in PayloadSelectArg, so nested pagination
metadata is typed for find, findOne, create and update alike.
@kac-axelor

Copy link
Copy Markdown
Contributor Author

Heads-up: #32 changes the runtime contract this PR's types describe.

#32 makes nested take/skip paginate per record. As part of that, pagination metadata (_count / _cursor / _hasNext / _hasPrev) on nested toMany rows is now populated only when the parent page resolves to a single record (findOne, create/update payloads, a single-row find — the path GraphQL nested connections use). On a multi-record page with nested take/skip, the fields stay undefined by design: take/skip are stripped from the batched child query and applied via a ranked window, so the child never runs as a page query.

Impact here:

  1. The premise "No runtime change — query.ts already does the right thing" no longer holds once Fix nested collection take/skip to paginate per record #32 lands. The types stay sound (all fields optional), but the body and comments should state the actual contract: metadata is present on single-record pages only.

  2. The find() test assertion passes by construction, not by contract — where: {id: c.id} happens to return exactly one record. Suggest a comment noting that, plus a negative assertion pinning the multi-record behavior (valid only after Fix nested collection take/skip to paginate per record #32 merges):

    // a multi-record page windows each list per record, but carries no
    // nested pagination metadata — single-record pages do
    const many = await client.contact.find({
      where: { id: { in: [c.id, c2.id] } },
      select: { id: true, addresses: { select: { id: true }, orderBy: { id: "ASC" }, take: 2 } },
    });
    many.forEach(r => expect(r.addresses).toHaveLength(2)); // windowed per record
    expect(many[0].addresses![0]._count).toBeUndefined();   // ...but no metadata
  3. _count is mistyped as string. The runtime value is the number returned by getCount() (record._count = count in query.ts), and this PR's own expect(first._count).toBe(3) only passes because it is a number. Since this PR is the one extracting the reusable PaginationMeta, it is the right place to correct it to _count?: number.

Sequencing: merge #32 first, rebase this onto dev, then apply the above — the two PRs touch disjoint files, so the order is conflict-free either way, but the negative test and the reworded contract only make sense on top of #32. After both land, the now-redundant metadata cast in #32's client-nested-pagination.test.ts can be dropped as a follow-up.

@kac-axelor

Copy link
Copy Markdown
Contributor Author

Merge coordination with #33 — src/client/types/query.ts conflicts: this PR extracts the payload metadata block into a reusable PaginationMeta applied to nested toMany rows, while #33 retypes _count as number (it's getCount()'s number at runtime). Whichever lands second, the resolution is the semantic merge of both:

export type PaginationMeta = {
  _count?: number;
  _cursor?: string;
  _hasNext?: boolean;
  _hasPrev?: boolean;
};

export type Payload<
  Type extends Entity,
  Query,
  Empty = ResultIdentity<Type>,
> = PayloadArg<Type, Query, Empty> & PaginationMeta;

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.

1 participant