Keeping Large Developments Fast: Pagination Improvements in v1.0.85
Keeping Large Developments Fast: Pagination Improvements in v1.0.85
Release: v1.0.85
Track: Performance / Data Patterns
Reference: PERF-15
The Problem
As Irish residential developments grow — some with hundreds of units, years of ownership history, multiple AGM cycles, and ongoing maintenance programmes — the volume of data stored in the platform grows with them. A number of list endpoints were not accounting for this growth.
Specifically:
listBuildingsreturned every building record in the database in a single query, with no upper bound.getOwnerandgetPortalDatafetched service charge demands and payment history with a hardcoded.limit(50)— a number that worked during early development but provides no flexibility as data volumes increase.- Detail view sub-lists — ownership history, compliance events, and maintenance notes embedded inside owner and unit detail screens — loaded every historical record unconditionally.
Unbounded queries are expensive. They transfer more data than any screen can display, hold database connections open longer than necessary, and slow down response times for all users on the platform — not just the one triggering the large query.
What Was Fixed
listBuildings now uses cursor pagination
listBuildings in property.ts has been updated to use the same paginationInput utility that other core list endpoints already use. This means:
- Requests specify a
limit(how many results per page) and an optionalcursor(the position to start from). - Responses include a
nextCursorvalue that callers pass into the next request to retrieve the following page. - The first page is returned immediately; subsequent pages are only fetched when needed.
This is consistent with the cursor-based pagination pattern used throughout the rest of the platform.
getOwner and getPortalData use configurable limits
The hardcoded .limit(50) values applied to service charge demand lists and payment history have been replaced with configurable limits. Consumers of these endpoints can now request the page size that suits their context — a compact dashboard widget might request 5 records; a full history view might request 25.
Detail sub-lists adopt a load-more pattern
Historical sub-lists embedded in detail views — including ownership history, compliance event logs, and maintenance notes — now apply a .limit(20) default and support a load-more interaction pattern. The first 20 records load with the parent detail view; additional records are fetched only when the user explicitly requests them.
This is the right trade-off for detail views: enough context is visible immediately, but the server is not burdened with fetching records the user may never scroll to.
Router-wide pagination audit
As part of this work, the compliance.ts, agm.ts, and maintenance.ts routers were audited for any remaining list endpoints with missing or insufficient pagination. Fixes were applied where unbounded queries were found.
What You May Need to Update
If your integration or front-end code calls any of the affected endpoints, review the following:
| Endpoint | Change | Action Required |
|---|---|---|
listBuildings | Now paginated via cursor | Implement page iteration if you previously expected all results in one response |
getOwner (demands/history sub-list) | Configurable limit replaces hardcoded 50 | Pass your desired limit; handle paginated responses |
getPortalData (payment history) | Configurable limit replaces hardcoded 50 | Pass your desired limit; handle paginated responses |
| Detail view sub-lists | .limit(20) + load-more | Ensure UI surfaces a load-more control for ownership history, compliance events, and maintenance notes |
Background: Why Cursor Pagination?
The platform uses cursor-based pagination rather than offset-based pagination (LIMIT x OFFSET y) for most list endpoints. Cursor pagination has two key advantages for data sets that change frequently:
- Consistency — New records inserted between page requests don't cause items to appear twice or be skipped, because the cursor anchors to a specific record rather than a row number.
- Performance — Database engines can seek directly to the cursor position using an index; they don't have to scan and discard the first
nrows as offset queries do.
For a platform managing live financial and compliance data — where records are continuously being added by managing agents, owners, and automated processes — cursor pagination is the appropriate choice.
Summary
v1.0.85 removes the last significant unbounded list queries from the platform's core data paths. Developments of any size — from a small 20-unit scheme to a large multi-building estate — will now experience consistent query performance as their data grows over time.