Skip to main content

OpenAPI - Pagination

Psychic provides a nice wrapper around Dream's paginate method within the OpenAPI decorator, to enable you to explicitly render the results of a paginate call within Dream. When passing paginate: true, to your OpenAPI decorator, Psychic will automatically provide the necessary outer structure to reflect the call to Dream, and will correctly serialize the results field on the incoming payload, same as it would in an ordinary many: true scenario.

class PlacesController {
@OpenAPI(Place, {
paginate: true,
serializerKey: 'summary',
})
public async index() {
const paginated = await this.currentHost.associationQuery('places').paginate({
page: this.castParam('page', 'integer', { allowNull: true }),
pageSize: this.castParam('pageSize', 'integer', { allowNull: true }),
})
this.ok(paginated)
}
}

which will produce an openapi shape like this for the response body:

{
"type": "object",
"required": ["recordCount", "pageCount", "currentPage", "results"],
"properties": {
"recordCount": {
"type": "number"
},
"pageCount": {
"type": "number"
},
"currentPage": {
"type": "number"
},
"results": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PostSummary"
}
}
}
}

Scroll Pagination (Cursor-Based)

For high-performance scenarios with large datasets, use scrollPaginate: true instead of paginate: true to enable cursor-based pagination:

class PlacesController {
@OpenAPI(Place, {
scrollPaginate: true,
serializerKey: 'summary',
})
public async index() {
const paginated = await this.currentHost.associationQuery('places').scrollPaginate({
pageSize: this.castParam('pageSize', 'integer', { allowNull: true }),
cursor: this.castParam('cursor', 'string', { allowNull: true }),
})
this.ok(paginated)
}
}

This produces a different OpenAPI shape optimized for cursor-based navigation:

{
"type": "object",
"required": ["cursor", "results"],
"properties": {
"cursor": {
"type": "string",
"nullable": true,
"description": "Cursor for next page, null if no more pages"
},
"results": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PlaceSummary"
}
}
}
}

Scroll pagination provides better performance than offset-based pagination by avoiding expensive OFFSET queries. It's ideal for infinite scroll UX patterns and remains consistent when new records are added during navigation. Use cursor: null for the first page, then pass the returned cursor value for subsequent pages.