Skip to content

Pagination

TypeSpec provide built-in support for some of the common pagination pattern used.

Pagination can be categorized into two types:

  • Client driven pagination: In this pattern, the client is responsible for managing the pagination state(figuring out the next page, previous page, etc).
  • Server driven pagination: In this pattern the server gives the client information on how to navigate to the next, previous, first, last page, etc.

To enable pagination for an operation the first step is to decorate it with the @list decorator and have the return type contain a property decorated with @pageItems

@list op listPets(): {
@pageItems pets: Pet[];
};

For client driven pagination there are 3 decorators that can be used to annotate the operation parameters.

  • @pageSize: Number of items to return per page.
  • @offset: Number of items to skip
  • @pageIndex: Page index.

@offset and @pageIndex are not necessary mutually exclusive but they are used to achieve the same goal.

@list op listPets(@offset skip?: int32 = 0): {
@pageItems pets: Pet[];
};
@list op listPets(@offset skip?: int32, @pageSize perPage?: int32 = 100): {
@pageItems pets: Pet[];
};
@list op listPets(@pageIndex page?: int32 = 1, @pageSize perPage?: int32 = 100): {
@pageItems pets: Pet[];
};

For server driven pagination, the server returns information on how to navigate to other pages. There is 5 decorators that can be used to annotate the corersonding properties:

  • @nextLink: Link to the next page.
  • @prevLink: Link to the previous page.
  • @firstLink: Link to the first page.
  • @lastLink: Link to the last page.
  • @continuationToken: Should be specified in both the parameters and return type of the operation. In the return type it is the token to navigate to the next page and in the parameters it mark which parameter to use to pass the next continuation token.

Example 1: Using continuation token for an HTTP service

Section titled “Example 1: Using continuation token for an HTTP service”
@list op listPets(@query @continuationToken token?: string): {
@pageItems pets: Pet[];
@continuationToken nextToken?: string;
};

In this case, the body of the server response for a page has a property marked as continuation token, which can be used to fetch the next page. When fetching the next page, request will include the continuation token as a query parameter.

The continuation token can be in other locations as well, for instance, the below spec indicates that the server response for a page can have a header containing the continuation token. When fetching the next page, request will include the continuation token as a query parameter.

@list op listPets(@query @continuationToken token?: string): {
@pageItems pets: Pet[];
@continuationToken @header nextToken?: string;
};
@list op listPets(): {
@pageItems pets: Pet[];
links: {
@nextLink next?: url;
@prevLink prev?: url;
@firstLink first?: url;
@lastLink last?: url;
};
};

Example 3: Combining client and server driven pagination for an HTTP service

Section titled “Example 3: Combining client and server driven pagination for an HTTP service”
@list op listPets(@query @pageIndex page?: int32 = 1, @query @pageSize perPage?: int32 = 100): {
@pageItems pets: Pet[];
// Links would return the url resolve with page and perPage set
links: {
@nextLink next?: url;
@prevLink prev?: url;
@firstLink first?: url;
@lastLink last?: url;
};
};

A paged operation can offer additional parameters that are not used as paging control parameters like a filter parameter for example. The expectation is those would be carried over to the next page requests with the exception of the link cases(next, prev, first and last links) where each protocol might have a different interpretation of what the link exactly represents. The link may encode parameters such as query parameters in HTTP. In such cases those parameters must be passed.

For example, HTTP links are expected to be opaque and contain all the necessary information for the next page URL. This means that query and path parameters are expected to already have been included in the link. On the other hand, any header parameters are expected to be resent in the next request as those cannot be represented in the link.

@route("pets")
@list
op listPets(
@query filter?: string,
@query expand?: string,
@query @pageIndex page?: int32 = 1,
@query @pageSize perPage?: int32 = 100,
@header specialHeader?: "x-special-value",
): {
@pageItems pets: Pet[];
@nextLink next?: url;
};
// First request
GET /pets?filter=dog
Special-Header: x-special-value
{"pets": [...], "nextLink": "/pets?filter=dog&page=2&perPage=100"}
---
// Second request
GET /pets?filter=dog&page=2&perPage=100
Special-Header: x-special-value
{"pets": [...], "nextLink": "/pets?filter=dog&page=3&perPage=100"}
@route("pets")
@list
op listPets(
@query filter?: string,
@query expand?: string,
@query @continuationToken token?: string,
@header specialHeader?: "x-special-value",
): {
@pageItems pets: Pet[];
@continuationToken next?: url;
};
// First request
GET /pets?filter=dog
Special-Header: x-special-value
{"pets": [...], "continuationToken": "token2"}
---
// Second request
GET /pets?filter=dog&token=token2
Special-Header: x-special-value
{"pets": [...], "continuationToken": "token3"}