Response strategies
A response strategy turns a raw body into a PaginatedCollection. There are three starting points,
and picking the right one is usually the whole job.
Flat envelopes
Section titled “Flat envelopes”Everything at the top level, named:
{ "data": [], "current_page": 2, "last_page": 6, "total": 57 }Extend AbstractFlatResponseStrategy with an empty body. The base reads each field by the name in
ResponseOptions.
Used by Laravel and Spatie.
Nested envelopes
Section titled “Nested envelopes”Metadata inside a sub-object:
{ "data": [], "meta": { "pagination": { "page": 2, "pageCount": 6 } } }Extend AbstractDotPathResponseStrategy. Paths in ResponseOptions become dotted:
meta.pagination.page.
It also derives from and to arithmetically when the backend omits them.
Used by Strapi, JSON:API, Payload, PocketBase, Sieve and others.
Everything else
Section titled “Everything else”Implement IResponseStrategy directly when the shape is not a lookup — cursor URLs to parse,
headers to read, or a bare array body.
export class MyResponseStrategy implements IResponseStrategy { public paginate<T extends PaginatedObject>( response: RawResponse, options: ResponseOptions, headers?: HeaderBag, limit?: number, ): PaginatedCollection<T> { // … }}Used by DRF (cursor URLs), OData (@odata.nextLink), PostgREST, WordPress and Ransack (headers).
limit is the page size the request asked for; Paginator.paginate() passes the store’s. Read it
when the response places the rows without stating the page size. PostgREST’s
Content-Range: 30-33/34 spans four rows, but at a limit of 10 it is page 4 of 4, not page 8 of 9.
The other built-in strategies ignore it, and so can yours when the response states its page size or
a next link proves the page full.
Bare-array bodies
Section titled “Bare-array bodies”PostgREST, the WordPress REST API and Rails behind Ransack return rows directly, with metadata in
headers. RawResponse models both shapes:
type RawResponse = Record<string, unknown> | readonly unknown[];Read headers through readHeader(), which accepts anything with a .get() accessor or a plain
object, without importing a framework:
import { readHeader } from '@qubeejs/core';
const range = readHeader(headers, 'Content-Range'); // '0-24/57'Reading paths safely
Section titled “Reading paths safely”readPath() and its typed variants handle both body shapes and missing keys:
import { readNumber, readRows, readString } from '@qubeejs/core';
readRows<T>(response, options.data);readNumber(response, options.total);readString(response, options.nextPageUrl);