Skip to content

Describe the three pagination shapes, and give new endpoints one - #322

Merged
haksungjang merged 1 commit into
mainfrom
er24-pagination-contract
Sep 3, 2026
Merged

Describe the three pagination shapes, and give new endpoints one#322
haksungjang merged 1 commit into
mainfrom
er24-pagination-contract

Conversation

@haksungjang

Copy link
Copy Markdown
Contributor

api-overview.md documented a single pagination contract: limit / offset, default 50, max 200, with an envelope carrying limit and offset back. Counted from the published OpenAPI schema, that describes 8 endpoints. Fifteen take page / page_size and six take page / size, and both groups return page / page_size in the body regardless of which query parameters they accept. Defaults and maxima vary inside each group too: limit maxes at 200 on the inventory endpoints and 500 on the per-project ones; size defaults to 20, except 25 on search and 100 on the source tree. Anyone writing a client or an export script from that page got it right for a fifth of the API and silently wrong for the rest.

The page now says there are three, which endpoints use each, what each returns, and that the values vary so they should not be hard-coded. It also says why three exist and what happens next, because a reader who finds this needs to know whether it is about to change under them. The plan called for sort to be corrected as well; it is accurate as written, accepted by ten endpoints, so it is left alone.

PageParams and Page in core/pagination.py are the half that stops the count growing: one query-parameter spelling and one envelope for endpoints written from here on, sitting next to the PAGE_MAX bound that module already owns. The batch endpoints in the bulk-onboarding unit are the first callers.

Existing endpoints are not touched, and that is the substantive decision. The plan called for accepting both spellings everywhere with Deprecation and Sunset headers, retiring the old one a minor later. I did not do that. SECURITY.md says a pre-1.0 minor may change the HTTP API; a compatibility layer would promise more stability than that in code while the documentation promises less, and would leave two paths to keep correct in every list endpoint meanwhile. Deprecation / Sunset are how a stable API announces a retreat, and attaching them to an API that has said it is not stable yet overstates the guarantee. Converging the 29 belongs at 1.0.0, where a caller expects one breaking change instead of a series of small ones, and it is on the roadmap under a new "For 1.0.0" heading.

The contract tests drive the dependency through a real FastAPI app rather than instantiating the dataclass, because the failure worth catching is the wiring: parameters that never reach the query string, or bounds that never reach the schema, still look right in a constructor call. They assert the spelling from the generated schema, that bounds are published and not merely enforced, that page is 1-based (page 3 of 25 starts at row 50, an off-by-one no single response would reveal), and that out-of-range is a 422 rather than a silent clamp. I mutated the module three ways to check they bite: offset off by one page, page_size renamed to size, and the cap raised tenfold; they caught 1, 9 and 2 tests respectively. Two further tests pin the endpoint counts in the documentation against the schema, so a new endpoint on an old spelling fails here rather than quietly making the page wrong again, and a fourth spelling is caught separately since the count table would not see it.

Verified locally: those 14 tests, the mutations above, ruff, mypy over 836 files, ko-style, em-dash, docs-uat lint, a docs-site production build with no new broken anchors (the #openapi link I first wrote did not resolve; the target heading now carries an explicit {#openapi-schema} anchor in both languages), and the backend unit suite at 6897 passed. Two test_admin_team_service failures were 736 organizations accumulated in a database my earlier runs shared; both pass on a fresh one.

api-overview.md said list endpoints take limit/offset, default 50 and max 200,
with a matching envelope. That is true of 8 endpoints. Fifteen take
page/page_size and six take page/size, and defaults and maxima vary inside each
group, so anyone writing a client against the page got it wrong for most of
the API.

The page now states all three, which endpoints use each, and that they will
converge at 1.0.0 rather than across minors. PageParams and Page give new
endpoints one shape so the count stops growing. Existing endpoints are left
alone: a compatibility layer accepting both spellings would promise more
stability than a pre-1.0 project offers and leave two paths to keep correct.
@haksungjang
haksungjang merged commit fd99a45 into main Sep 3, 2026
30 checks passed
@haksungjang
haksungjang deleted the er24-pagination-contract branch September 3, 2026 08:26
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