Skip to main content
All Docs
FeaturesBlockManOSUpdated March 26, 2026

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:

  • listBuildings returned every building record in the database in a single query, with no upper bound.
  • getOwner and getPortalData fetched 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 optional cursor (the position to start from).
  • Responses include a nextCursor value 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:

EndpointChangeAction Required
listBuildingsNow paginated via cursorImplement page iteration if you previously expected all results in one response
getOwner (demands/history sub-list)Configurable limit replaces hardcoded 50Pass your desired limit; handle paginated responses
getPortalData (payment history)Configurable limit replaces hardcoded 50Pass your desired limit; handle paginated responses
Detail view sub-lists.limit(20) + load-moreEnsure 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:

  1. 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.
  2. Performance — Database engines can seek directly to the cursor position using an index; they don't have to scan and discard the first n rows 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.