---
title: Paging Operations
llmstxt: true
---

import { ClientTabs, ClientTabItem } from "@components/client-tabs";

This doc details what emitters will generate for paging operations.

## Using next link to indicate how to get the next page

Next link is an absolute url returned by the paging operation, which indicates how to get the next page.
If the response does not return a next link, it indicates the last page of results.
Next link should be annotated in the response model with `@nextLink`.

There are two ways to indicate a paging operation with `@nextLink`:

1. Use `@list`, `@nextLink` and `@items`

<ClientTabs>

```typespec
@list op listWithPage(): UserList;

model User {
  id: string;
  name: string;
}

model UserList {
  @pageItems
  value: User[];

  @nextLink
  nextLink?: url;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
public virtual Pageable<User> ListWithPage(CancellationToken cancellationToken = default);
```

```typescript
export interface ListWithPageOptionalParams extends OperationOptions {}
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
public PagedIterable<User> listWithPage();
```

```go
type User struct {
  ID   string `json:"id"`
  Name string `json:"name"`
}

type UserList struct {
  Value    []User
  NextLink *string
}

func (c *Client) ListWithPage(ctx context.Context) (UserList, error)

```

</ClientTabs>

2. Use the `@list` and `@pageItems` decorators from TypeSpec core.

<ClientTabs>

```typespec
@list
op listWithPage(): UserList;

model User {
  id: string;
  name: string;
}

model UserList {
  @pageItems
  value: User[];

  @nextLink
  nextLink?: url;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
public virtual Pageable<User> ListWithPage(CancellationToken cancellationToken = default);
```

```typescript
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
public PagedIterable<User> listWithPage();
```

```go
type User struct {
  ID   string
  Name string
}

type UserList struct {
  Value    []User
  NextLink *string
}

func (c *Client) ListWithPage(ctx context.Context) (UserList, error)

```

</ClientTabs>

## Using continuation token to indicate how to get the next page

A continuation token is a string returned by a paging operation, which is used as a parameter value for the paging operation to get the next page.
If the response does not return a continuation token, it indicates the last page of results.
The request parameter that corresponds to the continuation token value in the paging operation should be decorated with `@continuationToken`. Similarly, the response property that contains the continuation token value should also be decorated with `@continuationToken`.

1. Continuation token in query parameter and response body.

<ClientTabs>

```typespec
@list
op listWithPage(@query @continuationToken continuationToken?: string): UserList;

model User {
  id: string;
  name: string;
}

model UserList {
  @pageItems
  value: User[];

  @continuationToken
  continuationToken?: string;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
namespace TestService
{
  public partial class User
  {
    public string Id { get; }
    public string Name { get; }
  }

  public partial class TestServiceClient
  {
    public TestServiceClient(Uri endpoint);
    public virtual Pageable<User> GetWithPage(string continuationToken = default, CancellationToken cancellationToken = default);
    public virtual AsyncPageable<User> GetWithPageAsync(string continuationToken = default, CancellationToken cancellationToken = default);
  }
}
```

```typescript
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
NOT_SUPPORTED
```

```go
func (client *MyServiceClient) NewListWithPagePager(options *MyServiceClientListWithPageOptions) *runtime.Pager[MyServiceClientListWithPageResponse]

type MyServiceClientListWithPageOptions struct {
    ContinuationToken *string
}
```

</ClientTabs>

2. Continuation token in header parameter and response body.

<ClientTabs>

```typespec
@list
op listWithPage(@header @continuationToken continuationToken?: string): UserList;

model User {
  id: string;
  name: string;
}

model UserList {
  @pageItems
  value: User[];

  @continuationToken
  continuationToken?: string;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
namespace TestService
{
  public partial class User
  {
    public string Id { get; }
    public string Name { get; }
  }

  public partial class TestServiceClient
  {
    public TestServiceClient(Uri endpoint);
    public virtual Pageable<User> GetWithPage(string continuationToken = default, CancellationToken cancellationToken = default);
    public virtual AsyncPageable<User> GetWithPageAsync(string continuationToken = default, CancellationToken cancellationToken = default);
  }
}
```

```typescript
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
NOT_SUPPORTED
```

```go
func (client *MyServiceClient) NewListWithPagePager(options *MyServiceClientListWithPageOptions) *runtime.Pager[MyServiceClientListWithPageResponse]

type MyServiceClientListWithPageOptions struct {
    ContinuationToken *string
}
```

</ClientTabs>

3. Continuation token in query parameter and response header.

<ClientTabs>

```typespec
@list
op listWithPage(@query @continuationToken continuationToken?: string): {
  @header
  @continuationToken
  continuationToken?: string;

  @pageItems
  value: User[];
};

model User {
  id: string;
  name: string;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
namespace TestService
{
  public partial class User
  {
    public string Id { get; }
    public string Name { get; }
  }

  public partial class TestServiceClient
  {
    public TestServiceClient(Uri endpoint);
    public virtual Pageable<User> GetWithPage(string continuationToken = default, CancellationToken cancellationToken = default);
    public virtual AsyncPageable<User> GetWithPageAsync(string continuationToken = default, CancellationToken cancellationToken = default);
  }
}
```

```typescript
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
NOT_SUPPORTED
```

```go
func (client *MyServiceClient) NewListWithPagePager(options *MyServiceClientListWithPageOptions) *runtime.Pager[MyServiceClientListWithPageResponse]

type MyServiceClientListWithPageOptions struct {
    ContinuationToken *string
}
```

</ClientTabs>

4. Continuation token in header parameter and response header.

<ClientTabs>

```typespec
@list
op listWithPage(@query @continuationToken continuationToken?: string): {
  @header
  @continuationToken
  continuationToken?: string;

  @pageItems
  value: User[];
};

model User {
  id: string;
  name: string;
}
```

```python
class User(_model_base.Model):
    id: str = rest_field()
    name: str = rest_field()

def list_with_page(self, **kwargs: Any) -> ItemPaged["_models.User"]:
    ...
```

```csharp
namespace TestService
{
  public partial class User
  {
    public string Id { get; }
    public string Name { get; }
  }

  public partial class TestServiceClient
  {
    public TestServiceClient(Uri endpoint);
    public virtual Pageable<User> GetWithPage(string continuationToken = default, CancellationToken cancellationToken = default);
    public virtual AsyncPageable<User> GetWithPageAsync(string continuationToken = default, CancellationToken cancellationToken = default);
  }
}
```

```typescript
// JS CodeGen api layer
export function listWithPage(
  context: Client,
  options: ListWithPageOptionalParams = { requestOptions: {} },
): PagedAsyncIterableIterator<User>;

// JS CodeGen classical client layer
export class TestServiceClient {
  listWithPage(
    options: ListWithPageOptionalParams = { requestOptions: {} },
  ): PagedAsyncIterableIterator<User>;
}
```

```java
NOT_SUPPORTED
```

```go
func (client *MyServiceClient) NewListWithPagePager(options *MyServiceClientListWithPageOptions) *runtime.Pager[MyServiceClientListWithPageResponse]

type MyServiceClientListWithPageOptions struct {
    ContinuationToken *string
}
```

</ClientTabs>

## Advanced

### Parameterized next links (against guidelines)

:::danger
Parameterized next links are against guidelines. Please don't follow this unless explicitly told to do so
:::

In very rare cases, there are cases of next links that require parameterization. These cases exist outside of the Azure guidelines for paging, but must be supported for legacy reasons.

In cases like this, you may use the special scalar type `Azure.Core.Legacy.parameterizedNextLink`. You can specify which parameters must be reformatted into the next link. Your emitted SDK will handle the reformatting based on the tsp definition

```tsp
model ListCertificateOptions {
  includePending?: string;
}
model Certificate {
  name: string;
}
model Page {
  @pageItems items: Certificate[];
  @nextLink nextLink: Azure.Core.Legacy.parameterizedNextLink<[
    ListCertificateOptions.includePending
  ]>;
}
```

## Legacy Paging Decorators

:::caution
The decorators in this section are in the `Azure.ClientGenerator.Core.Legacy` namespace and should only be used for legacy compatibility scenarios.
:::

### Forcing an operation to be treated as pageable (`@markAsPageable`)

If you have an operation that returns a paged result but isn't recognized as a paging operation by default (e.g., it doesn't use `@list`), you can force it to be treated as a pageable operation using `@markAsPageable`.

The decorator will look for a property decorated with `@pageItems` in the response model. If no such property exists, it will look for a property named `value`. If neither is found, a warning is emitted.

```tsp
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core.Legacy;

@markAsPageable
op listItems(): {
  @pageItems items: Widget[];
  nextLink?: string;
};
```

### Disabling paging for an operation (`@disablePageable`)

If an operation is automatically detected as a paging operation (e.g., because it uses `@list`) but you don't want it treated as pageable in the generated client, use `@disablePageable`.

```tsp
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core.Legacy;

@disablePageable
@list
op listItems(): {
  @pageItems items: Widget[];
  nextLink?: string;
};
```

### Specifying the HTTP verb for next link requests (`@nextLinkVerb`)

By default, the next link request uses a `GET` verb. If your service requires a `POST` verb for fetching subsequent pages, use `@nextLinkVerb`.

```tsp
import "@azure-tools/typespec-client-generator-core";

using Azure.ClientGenerator.Core.Legacy;

@nextLinkVerb("POST")
@list
op listItems(): {
  @pageItems items: Widget[];
  @nextLink nextLink?: string;
};
```
