How to Build Document Search Screens Without N+1 API Calls

Return the fields a result list needs, then load detail on demand

Published

Cream, lavender, and orange paper-cut illustration of matching document filters, returning list fields together, and opening document detail, with the FormKiQ wordmark.

A contract search returns 50 documents. The application then makes 50 separate attribute requests to fill in contract number, counterparty, and status. That is the N+1 pattern: one search request plus one extra request for every result. The screen waits for many responses before it becomes useful.

Return the fields needed for each row with the search results, then load the rest when someone opens a document. This removes the browser's repeated attribute requests. It does not guarantee fewer database reads, a fixed latency improvement, or a consistent snapshot.

Separate matching, row fields, and detail

  1. 1 · Query

    Find matching documents

    For example, contracts with an approved status.

  2. 2 · List fields

    Render useful rows

    Return contract number, counterparty, status, and expiry date with each result.

  3. 3 · Detail

    Open one document

    Fetch the preview, history, or other information needed for that view.

These are separate decisions. Filtering by status does not automatically return every field needed for the row. Requesting counterparty as a response field does not filter or sort by counterparty. Use supported query and sorting options for those operations.

Choose a search path for the filters users need

A renewals team needs “approved contracts for Acme Services that expire from January 1 through March 31, 2027.” That combines two exact matches with a date range, while contract number is still only a display field. Apply the combined criteria on the server before paginating. Intersecting the first page of separate searches in the browser can miss qualifying documents.

Repeated operational queries

For a familiar combination such as counterparty and status, start with supported metadata search and an access pattern suited to that combination. Measure candidate reads as well as returned matches.

Changing filters and summaries

Evaluate OpenSearch when users combine changing metadata filters, document text, sort orders, or aggregations such as counts by counterparty. Its usefulness extends beyond full-text search.

Choose the fields before building the screen

For the contract list, write down the minimum useful row. Keep stable document identity for navigation and actions; use the path as a display value that can change.

A small contract-search result
Include in the listLoad when opened
Document ID, current path, content typePreview or download link
Contract number, counterparty, status, expiry dateFull attributes and related records
Last-modified date, where availableVersions and processing history

Handle a missing field with an explicit empty state, rather than silently trigger another request for every incomplete row. Preserve the difference between missing, false, zero, and an empty string. If a detail screen needs to investigate a missing value, it can do so explicitly.

Keep pagination tied to the active search

For token-paginated metadata search, pass returned page tokens back unchanged through the endpoint's pagination parameters. Keep them with the current filters, selected fields, site, and authorization context. Start again when that context changes. Do not invent page numbers or sort one fetched page as though it were the complete result set.

Cancel obsolete requests where possible, and ignore late responses from an earlier search. For filtered searches that can return an empty page with a continuation token, keep a visible next-page action or continue with a bounded loading strategy. An empty page alone need not mean the search is complete.

Keep speed, permissions, and correctness together

One response is not a transaction across all returned records and attributes. An “approved” badge can be stale by the time someone acts on it. The application's command handling must check current authorization and business preconditions; do not assume a generic update endpoint rejects conflicting approvals automatically.

Keep cached results isolated by user, site, query, selected fields, and relevant permission context. Clear them on sign-out, site changes, or authorization changes. A cached row must not grant access after the underlying permission is removed.

Measure time to usable rows, response size, errors, browser request count, and backend work. A smaller selected response can reduce transferred data, but AWS notes that DynamoDB projection expressions do not reduce read capacity consumption. Validate cost and latency with the actual workload.

Check the interaction before release

  • A normal result page renders without one attribute request per row; opening a document loads only its needed detail.
  • Missing and multivalued attributes render correctly without automatic fallback calls.
  • A filter change cannot display an earlier request's results or reuse its page token.
  • Permission changes and competing actions are checked beyond the displayed row state.

Make each search response useful

Return enough information to choose a document, then load deeper detail when needed. Treat request count, backend cost, and correctness as separate things to verify, so a faster screen also behaves reliably.

    Start with FormKiQ Core

    The open-source foundation — API-first, deployable into your own AWS account, and free to use. Right for architecture validation and early implementation.

    Get Started Free

    Deploy FormKiQ Essentials or Advanced

    Production-ready editions for departments and complex workflows. Start with a Proof-of-Value deployment or go straight to production.

    Explore Options

    Plan an Enterprise Rollout

    For governance-heavy environments with residency, sovereignty, assurance, and multi-jurisdiction requirements. Talk to us about the right deployment model.

    Book a Call