---
title: "Read application data"
section: "frontend/data"
platforms: ["android", "angular", "flutter", "javascript", "nextjs", "react", "react-native", "swift", "vue"]
gen: 2
last-updated: "2026-03-25T17:40:00.000Z"
url: "https://docs.amplify.aws/react/frontend/data/query-data/"
---

<!-- Platform: javascript,  react-native, angular, nextjs, react, vue -->
You can read application data using the Amplify Data client. In this guide, we will review the difference between reading data and getting data, how to filter query results to get just the data you need, and how to paginate results to make your data more manageable. We will also show you how to cancel these requests when needed.

Before you begin, you will need:

- An [application connected to the API](/[platform]/frontend/data/connect-to-API/)
- Data already created to view

## List and get your data

Queries are used to read data through the API and include the `list` and `get` operations. Amplify Data automatically creates `list` and `get` queries for any `a.model()` type in your schema. The `list` query retrieves multiple items, such as Todo items, without needing to specific an identifier for a particular record. This is best suited for getting an overview or summary of items, or for enhancing the `list` operation to filter the items by specific criteria. When you want to query a single entry by an identifier, you would use `get` to retrieve a specific Todo item.

<Callout>

**Note:** The cost structure of your underlying data source can impact the cost to run some queries. For example, the `list` operation uses Amazon DynamoDB "scan operations," which can use more read request units than the `get` operation. You will want to review the associated costs for these operations for your data source. In our example, we are using DynamoDB. You can learn more about how DynamoDB costs are calculated by visiting [Amazon DynamoDB pricing](https://aws.amazon.com/dynamodb/pricing/).

</Callout>

You can list items by first generating the Data client with your backend Data schema. Then you can list items of your desired model:

```ts
import { generateClient } from 'aws-amplify/data';
import { type Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

// list items
const { data: todos, errors } = await client.models.Todo.list();

// get a specific item
const { data: todo, errors } = await client.models.Todo.get({
  id: '...',
});
```

<Accordion title='Troubleshoot unauthorized errors' headingLevel='4' eyebrow='Troubleshooting'>

Each API request uses an authorization mode. If you get unauthorized errors, you may need to update your authorization mode. To override the default authorization mode defined in your **amplify/data/resource.ts** file, pass an `authMode` property to the request or the client. The following examples show how you can mutate data with a custom authorization mode:

```ts
import { generateClient } from 'aws-amplify/data';
import { type Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

const { errors, data: todos } = await client.models.Todo.list({
  authMode: 'apiKey',
});
```

</details>

## Filter list queries

As your data grows, you will need to paginate your list queries. Fortunately, this is already built in to Amplify Data.

```ts
import { generateClient } from 'aws-amplify/data';
import { type Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

const { data: todos, errors } = await client.models.Todo.list({
  filter: {
    content: {
      beginsWith: 'hello'
    }
  }
});
```

### Compound filters

You can combine filters with `and`, `or`, and `not` Boolean logic. Observe that `filter` is recursive in respect to those fields. So if, for example, you wanted to filter for `priority` values of 1 _or_ 2, you would do this:

```ts
import { generateClient } from 'aws-amplify/data';
import { type Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

const { data: todos, errors } = await client.models.Todo.list({
  filter: {
    or: [
      {
        priority: { eq: '1' }
      },
      {
        priority: { eq: '2' }
      }
    ]
  }
});
```

Note that querying for `priority` of 1 and 2 would return no results, because this is Boolean logic instead of natural language.

## Paginate list queries

To paginate your list query results, make a subsequent list query request with the `nextToken` and `limit` input variable set. The `limit` variable limits how many results are returned. The response will include a `nextToken` you can use to request the next page of data. A `nextToken` is a very long string that represents the cursor to the starting item of the next query made with these filters.

```ts
import { generateClient } from 'aws-amplify/data';
import { type Schema } from '@/amplify/data/resource';

const client = generateClient<Schema>();

const {
  data: todos,
  nextToken, // Repeat this API call with the nextToken until the returned nextToken is `null`
  errors
} = await client.models.Todo.list({
  limit: 100, // default value is 100
  nextToken: 'eyJ2ZXJzaW9uejE1a2...' // previous nextToken
});
```

<!-- Platform: react, javascript, nextjs, react-native -->
If you're building a React application, you can use the `usePagination` hook in Amplify UI to help with managing the pagination user experience.

```js
import * as React from 'react';
import { Pagination } from '@aws-amplify/ui-react';

export const PaginationHasMorePagesExample = () => {
  const [pageTokens, setPageTokens] = React.useState([null]);
  const [currentPageIndex, setCurrentPageIndex] = React.useState(1);
  const [hasMorePages, setHasMorePages] = React.useState(true);

  const handleNextPage = async () => {
    if (hasMorePages && currentPageIndex === pageTokens.length) {
      const { data: todos, nextToken } = await client.models.Todo.list({
        nextToken: pageTokens[pageTokens.length - 1]
      });

      if (!nextToken) {
        setHasMorePages(false);
      }

      setPageTokens([...pageTokens, nextToken]);
    }

    setCurrentPageIndex(currentPageIndex + 1);
  };

  return (
    <Pagination
      currentPage={currentPageIndex}
      totalPages={pageTokens.length}
      hasMorePages={hasMorePages}
      onNext={handleNextPage}
      onPrevious={() => setCurrentPageIndex(currentPageIndex - 1)}
      onChange={(pageIndex) => setCurrentPageIndex(pageIndex)}
    />
  );
};
```
<!-- /Platform -->

<Callout>

**Limitations:**

- There is no API to get a total page count at this time. Note that scanning all items is a [potentially expensive operation](https://github.com/aws-amplify/amplify-js/issues/2901).
- You [cannot query by `page` number](https://github.com/aws-amplify/amplify-cli/issues/5086); you have to query by `nextToken`.

</Callout>

## Fetch only the data you need with custom selection set

A business domain model may contain many models with numerous fields. However, apps typically only need subsets of the data or fields to meet the requirements of different components or screens. It is necessary to have a mechanism to retrieve subsets of models and their relationships. This mechanism would help optimize data usage for screens and components by only transferring needed data. Having this capability would improve the app's data efficiency, latency, and the end user's perceived performance.

A **custom selection set** allows consumers to specify, on a per-call basis, the fields the consumer wants to retrieve; this is possible for all operations that return data (CRUDL + `observeQuery`). The desired fields are specified in a strongly typed way (discoverable through IntelliSense) with a "dot notation".

```ts
// same way for all CRUDL: .create, .get, .update, .delete, .list, .observeQuery
const { data: blogWithSubsetOfData, errors } = await client.models.Blog.get(
  { id: blog.id },
  {
    selectionSet: ['author.email', 'posts.*'],
  }
);
```

## TypeScript type helpers for Amplify Data

When using TypeScript, you frequently need to specify data model types for type generics. 

<!-- Platform: react, javascript, nextjs, react-native -->
For instance, with React's `useState`, you provide a type in TypeScript to ensure type-safety in your component code using the state. Use the `Schema["MODEL_NAME"]["type"]` pattern to get TypeScript types for the shapes of data models returned from the backend API.

```ts
import { type Schema } from '@/amplify/data/resource';

type Post = Schema['Post']['type'];

const [posts, setPosts] = useState<Post[]>([]);
```
<!-- /Platform -->

<!-- Platform: angular, vue -->
```ts
import { type Schema } from '../../../amplify/data/resource';

type Post = Schema['Post']['type'];
```
<!-- /Platform -->

You can combine the `Schema["MODEL_NAME"]["type"]` type with the `SelectionSet` helper type to describe the return type of API requests using the `selectionSet` parameter:

<!-- Platform: react, javascript, nextjs, react-native -->
```ts
import type { SelectionSet } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource';

const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const;
type PostWithComments = SelectionSet<Schema['Post']['type'], typeof selectionSet>;

// ...
const [posts, setPosts] = useState<PostWithComments[]>([]);

const fetchPosts = async () => {
  const { data: postsWithComments } = await client.models.Post.list({
    selectionSet,
  });
  setPosts(postsWithComments);
}
```
<!-- /Platform -->

<!-- Platform: vue -->
```ts
<script setup lang="ts">
import type { Schema } from '../../../amplify/data/resource';
import { ref, onMounted } from 'vue';
import { generateClient, type SelectionSet } from 'aws-amplify/data';

const client = generateClient<Schema>();

const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const;

type PostWithComments = SelectionSet<
  Schema['Post']['type'], 
  typeof selectionSet
>;

const posts = ref<PostWithComments[]>([]);

const fetchPosts = async (): Promise<void> => {
  const { data: postsWithComments } = await client.models.Post.list({
    selectionSet,
  });
  posts.value = postsWithComments;
};

onMounted(() => {
  fetchPosts();
});
</script>

<template v-for="post in posts" :key="post.id">
  <li>{{ post.content }}</li>
</template>
```
<!-- /Platform -->

<!-- Platform: angular -->
```ts
import type { Schema } from '../../../amplify/data/resource';
import { Component, OnInit } from '@angular/core';
import { generateClient, type SelectionSet } from 'aws-amplify/data';
import { CommonModule } from '@angular/common';

const client = generateClient<Schema>();

const selectionSet = ['content', 'blog.author.*', 'comments.*'] as const;

type PostWithComments = SelectionSet<
  Schema['Post']['type'],
  typeof selectionSet
>;

@Component({
  selector: 'app-todos',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './todos.component.html',
  styleUrls: ['./todos.component.css'],
})
export class TodosComponent implements OnInit {
  posts: PostWithComments[] = [];

  constructor() {}

  ngOnInit(): void {
    this.fetchPosts();
  }

  async fetchPosts(): Promise<void> {
    const { data: postsWithComments } = await client.models.Post.list({
      selectionSet,
    });
    this.posts = postsWithComments;
  }
}
```
<!-- /Platform -->

## Cancel read requests

You can cancel any query API request by calling `.cancel` on the query request promise that's returned by `.list(...)` or `.get(...)`.

```javascript
const promise = client.models.Todo.list();
//  ^ Note: we're not awaiting the request, we're returning the promise

try {
  await promise;
} catch (error) {
  console.log(error);
  // If the error is because the request was cancelled you can confirm here.
  if (client.isCancelError(error)) {
    console.log(error.message); // "my message for cancellation"
    // handle user cancellation logic
  }
}
...

// To cancel the above request
client.cancel(promise, "my message for cancellation");
```

You need to ensure that the promise returned from `.list()` or `.get()` has not been modified. Typically, async functions wrap the promise being returned into another promise. For example, the following will **not** work:

```javascript
async function makeAPICall() {
  return client.models.Todo.list();
}
const promise = makeAPICall();

// The following will NOT cancel the request.
client.cancel(promise, 'my error message');
```

## Conclusion

Congratulations! You have finished the **Read application data** guide. In this guide, you learned how to read your data through `get` and `list` queries.

### Next steps

Our recommended next steps include subscribing to real-time events to look for mutations in your data and continuing to build out and customize your information architecture for your data. Some resources that will help with this work include:

- [Subscribe to real-time events](/[platform]/frontend/data/subscribe-data/)
- [Customize your auth rules](/[platform]/build-a-backend/data/customize-authz/)
- [Customize your data model](/[platform]/build-a-backend/data/data-modeling/)
- [Add custom business logic](/[platform]/build-a-backend/data/custom-business-logic/)
<!-- /Platform -->

<!-- Platform: swift -->
## Query by Id

Now that you were able to make a mutation, take the `Id` that was printed out and use it in your query to retrieve data.

#### [Async/Await]

```swift
func getTodo() async {
    do {
        let result = try await Amplify.API.query(
           request: .get(Todo.self, byId: "9FCF5DD5-1D65-4A82-BE76-42CB438607A0")
        )

        switch result {
        case .success(let todo):
            guard let todo = todo else {
                print("Could not find todo")
                return
            }
            print("Successfully retrieved todo: \(todo)")
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    } catch let error as APIError {
        print("Failed to query todo: ", error)
    } catch {
        print("Unexpected error: \(error)")
    }
}
```

#### [Combine]

```swift
func getTodo() -> AnyCancellable {
    let sink = Amplify.Publisher.create {
        try await Amplify.API.query(
            request: .get(Todo.self, byId: "9FCF5DD5-1D65-4A82-BE76-42CB438607A0")
        )
    }
    .sink {
        if case let .failure(error) = $0 {
            print("Got failed event with error \(error)")
        }
    }
    receiveValue: { result in
        switch result {
        case .success(let todo):
            guard let todo = todo else {
                print("Could not find todo")
                return
            }
            print("Successfully retrieved todo: \(todo)")
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    }
    return sink
}
```

## List Query

You can get the list of items using `.list` with optional parameters `limit` and `where` to specify the page size and condition. By default, the page size is 1000.

#### [Async/Await]

```swift
func listTodos() async {
    let todo = Todo.keys
    let predicate = todo.name == "my first todo" && todo.description == "todo description"
    let request = GraphQLRequest<Todo>.list(Todo.self, where: predicate, limit: 1000)
    do {
        let result = try await Amplify.API.query(request: request)
        switch result {
        case .success(let todos):
            print("Successfully retrieved list of todos: \(todos)")
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    } catch let error as APIError {
        print("Failed to query list of todos: ", error)
    } catch {
        print("Unexpected error: \(error)")
    }
}
```

#### [Combine]

```swift
func listTodos() -> AnyCancellable {
    let todo = Todo.keys
    let predicate = todo.name == "my first todo" && todo.description == "todo description"
    let request = GraphQLRequest<Todo>.list(Todo.self, where: predicate, limit: 1000)
    let sink = Amplify.Publisher.create {
        try await Amplify.API.query(request: request)
    }
    .sink {
        if case let .failure(error) = $0 {
            print("Got failed event with error \(error)")
        }
    }
    receiveValue: { result in
    switch result {
        case .success(let todos):
            print("Successfully retrieved list of todos: \(todos)")
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    }
    return sink
}
```

### List subsequent pages of items

If you are using SwiftUI and have SwiftUI imported in the same code file, you will need to import the class `Amplify.List` to resolve name collision with `SwiftUI.List`:

```swift
import SwiftUI
import Amplify
import class Amplify.List
```

For large data sets, you'll need to paginate through the results. After receiving the first page of results, you can check if there is a subsequent page and obtain the next page.

```swift
var todos: [Todo] = []
var currentPage: List<Todo>?

func listFirstPage() async {
    let todo = Todo.keys
    let predicate = todo.name == "my first todo" && todo.description == "todo description"
    let request = GraphQLRequest<Todo>.list(Todo.self, where: predicate, limit: 1000)
    do {
        let result = try await Amplify.API.query(request: request)
        switch result {
        case .success(let todos):
            print("Successfully retrieved list of todos: \(todos)")
            self.currentPage = todos
            self.todos.append(contentsOf: todos)
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    } catch let error as APIError {
        print("Failed to query list of todos: ", error)
    } catch {
        print("Unexpected error: \(error)")
    }
}

func listNextPage() async {
    if let current = self.currentPage, current.hasNextPage() {
        do {
            let todos = try await current.getNextPage()
            self.todos.append(contentsOf: todos)
            self.currentPage = todos
        } catch {
            print("Failed to get next page \(error)")
        }
    }
}
```

## List all pages

If you want to get all pages, retrieve the subsequent page when you have successfully retrieved the first or next page.

1. Update the above method `listFirstPage()` to `listAllPages()`
2. Call `listNextPageRecursively()` in the success block of the query in `listAllPages()`
2. Update the `listNextPage()` to `listNextPageRecursively()`
3. Call `listNextPageRecursively()` in the success block of the query in `listNextPageRecursively()`

The completed changes should look like this:

```swift
var todos: [Todo] = []
var currentPage: List<Todo>?

func listAllPages() async { // 1. Updated from `listFirstPage()`
    let todo = Todo.keys
    let predicate = todo.name == "my first todo" && todo.description == "todo description"
    let request = GraphQLRequest<Todo>.list(Todo.self, where: predicate, limit: 1000)
    do {
        let result = try await Amplify.API.query(request: request)
        switch result {
        case .success(let todos):
            print("Successfully retrieved list of todos: \(todos)")
            self.currentPage = todos
            self.todos.append(contentsOf: todos)
            await self.listNextPageRecursively() // 2. Added
        case .failure(let error):
            print("Got failed result with \(error.errorDescription)")
        }
    } catch let error as APIError {
        print("Failed to query list of todos: ", error)
    } catch {
        print("Unexpected error: \(error)")
    }
}

func listNextPageRecursively() async { // 3. Updated from `listNextPage()`
    if let current = currentPage, current.hasNextPage() {
        do {
            let todos = try await current.getNextPage()
            self.todos.append(contentsOf: todos)
            self.currentPage = todos
            await self.listNextPageRecursively() // 4. Added
        } catch {
            print("Failed to get next page \(error)")
        }
    }
}
```
<!-- /Platform -->

<!-- Platform: android -->
## Query item

Now that you were able to make a mutation, take the `Id` that was printed out and use it in your query to retrieve data.

#### [Java]

```java
private void getTodo(String id) {
    Amplify.API.query(
        ModelQuery.get(Todo.class, id),
        response -> Log.i("MyAmplifyApp", ((Todo) response.getData()).getName()),
        error -> Log.e("MyAmplifyApp", error.toString(), error)
    );
}
```

#### [Kotlin - Callbacks]

```kotlin
private fun getTodo(id: String) {
    Amplify.API.query(ModelQuery.get(Todo::class.java, id),
        { Log.i("MyAmplifyApp", "Query results = ${(it.data as Todo).name}") },
        { Log.e("MyAmplifyApp", "Query failed", it) }
    );
}
```

#### [Kotlin - Coroutines]

```kotlin
suspend fun getTodo(id: String) {
   try {
       val response = Amplify.API.query(ModelQuery.get(Todo::class.java, id))
       Log.i("MyAmplifyApp", response.data.name)
   } catch (error: ApiException) {
       Log.e("MyAmplifyApp", "Query failed", error)
   }
}
```

#### [RxJava]

```java
private void getTodo(String id) {
  RxAmplify.API.query(ModelQuery.get(Todo.class, id))
          .subscribe(
              response -> Log.i("MyAmplifyApp", ((Todo) response.getData()).getName()),
              error -> Log.e("MyAmplifyApp", error.toString(), error)
          );
}
```

## List items

You can get the list of items that match a condition that you specify in `Amplify.API.query`:

#### [Java]

```java
Amplify.API.query(
    ModelQuery.list(Todo.class, Todo.NAME.contains("first")),
    response -> {
        for (Todo todo : response.getData()) {
            Log.i("MyAmplifyApp", todo.getName());
        }
    },
    error -> Log.e("MyAmplifyApp", "Query failure", error)
);
```

#### [Kotlin - Callbacks]

```kotlin
Amplify.API.query(
    ModelQuery.list(Todo::class.java, Todo.NAME.contains("first")),
    { response ->
        response.data.forEach { todo ->
            Log.i("MyAmplifyApp", todo.name)
        }
    },
    { Log.e("MyAmplifyApp", "Query failure", it) }
)
```

#### [Kotlin - Coroutines]

```kotlin
try {
    Amplify.API
        .query(ModelQuery.list(Todo::class.java, Todo.NAME.contains("first")))
        .response.data
        .items.forEach { todo -> Log.i("MyAmplifyApp", todo.name) }
} catch (error: ApiException) {
    Log.e("MyAmplifyApp", "Query failure", error)
}
```

#### [RxJava]

```java
RxAmplify.API.query(ModelQuery.list(Todo.class, Todo.NAME.contains("first"))
    .subscribe(
        response -> {
            for (Todo todo : response.getData()) {
                Log.i("MyAmplifyApp", todo.getName());
            }
        },
        error -> Log.e("MyAmplifyApp", "Query failure", error)
    ));
```

> **Note**: This approach will only return up to the first 1,000 items.  To change this limit or make requests for additional results beyond this limit, use *pagination* as discussed below.

## List subsequent pages of items

A list query only returns the first 1,000 items by default, so for large data sets, you'll need to paginate through the results.  After receiving a page of results, you can obtain a `GraphQLRequest` for requesting the next page, if there are more results available.  The page size is configurable as well, as in the example below.

#### [Java]

```java
public void queryFirstPage() {
    query(ModelQuery.list(Todo.class, ModelPagination.limit(1_000)));
}

private static void query(GraphQLRequest<PaginatedResult<Todo>> request) {
    Amplify.API.query(
        request,
        response -> {
            if (response.hasData()) {
                for (Todo todo : response.getData()) {
                    Log.d("MyAmplifyApp", todo.getName());
                }
                if (response.getData().hasNextResult()) {
                    query(response.getData().getRequestForNextResult());
                }
            }
        },
        failure -> Log.e("MyAmplifyApp", "Query failed.", failure)
    );
}
```

#### [Kotlin - Callbacks]

```kotlin
fun queryFirstPage() {
    query(ModelQuery.list(Todo::class.java, ModelPagination.limit(1_000)))
}

fun query(request: GraphQLRequest<PaginatedResult<Todo>>) {
    Amplify.API.query(request,
        { response ->
            if (response.hasData()) {
                response.data.items.forEach { todo ->
                    Log.d("MyAmplifyApp", todo.name)
                }
                if (response.data.hasNextResult()) {
                    query(response.data.requestForNextResult)
                }
            }
        },
        { Log.e("MyAmplifyApp", "Query failed", it) }
    )
}
```

#### [Kotlin - Coroutines]

```kotlin
suspend fun queryFirstPage() {
    query(ModelQuery.list(Todo::class.java,
        ModelPagination.firstPage().withLimit(1_000)))
}

suspend fun query(request: GraphQLRequest<PaginatedResult<Todo>>) {
    try {
        val response = Amplify.API.query(request)
        response.data.items.forEach { todo ->
            Log.d("MyAmplifyApp", todo.name)
        }
        if (response.data.hasNextResult()) {
            query(response.data.requestForNextResult)
        }
    } catch (error: ApiException) {
        Log.e("MyAmplifyApp", "Query failed.", error)
    }
}
```

#### [RxJava]

```java
BehaviorSubject<GraphQLRequest<PaginatedResult<Todo>>> subject =
        BehaviorSubject.createDefault(ModelQuery.list(Todo.class, ModelPagination.limit(1_000)));
subject.concatMap(request -> RxAmplify.API.query(request).toObservable())
    .doOnNext(response -> {
        if (response.hasErrors()) {
            subject.onError(new Exception(String.format("Query failed: %s", response.getErrors())));
        } else if (!response.hasData()) {
            subject.onError(new Exception("Empty response from AppSync."));
        } else if(response.getData().hasNextResult()) {
            subject.onNext(response.getData().getRequestForNextResult());
        } else {
            subject.onComplete();
        }
    })
    .concatMapIterable(GraphQLResponse::getData)
    .subscribe(
        todo -> Log.d(TAG, "Todo: " + todo),
        error -> Log.e(TAG, "Error: " + error)
    );
```

<!-- /Platform -->

<!-- Platform: flutter -->
## Query item

Now that you were able to make a mutation, take the `id` from the created `Todo` instance and use it to retrieve data.

```dart
Future<Todo?> queryItem(Todo queriedTodo) async {
  try {
    final request = ModelQueries.get(
      Todo.classType,
      queriedTodo.modelIdentifier,
    );
    final response = await Amplify.API.query(request: request).response;
    final todo = response.data;
    if (todo == null) {
      safePrint('errors: ${response.errors}');
    }
    return todo;
  } on ApiException catch (e) {
    safePrint('Query failed: $e');
    return null;
  }
}
```

## List items

You can get the list of items in `Amplify.API.query`:

```dart
Future<List<Todo?>> queryListItems() async {
  try {
    final request = ModelQueries.list(Todo.classType);
    final response = await Amplify.API.query(request: request).response;

    final todos = response.data?.items;
    if (todos == null) {
      safePrint('errors: ${response.errors}');
      return const [];
    }
    return todos;
  } on ApiException catch (e) {
    safePrint('Query failed: $e');
    return const [];
  }
}
```

### List subsequent pages of items

For large data sets, you'll need to paginate through the results. After receiving the first page of results, you can check if there is a subsequent page and obtain the next page.

```dart
const limit = 100;

Future<List<Todo?>> queryPaginatedListItems() async {
  final firstRequest = ModelQueries.list<Todo>(Todo.classType, limit: limit);
  final firstResult = await Amplify.API.query(request: firstRequest).response;
  final firstPageData = firstResult.data;

  // Indicates there are > 100 todos and you can get the request for the next set.
  if (firstPageData?.hasNextResult ?? false) {
    final secondRequest = firstPageData!.requestForNextResult;
    final secondResult =
        await Amplify.API.query(request: secondRequest!).response;
    return secondResult.data?.items ?? <Todo?>[];
  } else {
    return firstPageData?.items ?? <Todo?>[];
  }
}
```

## Query Predicates

Models also support the use of query predicates for comparison. These are accessible from the Model's attributes, for example `Blog["attribute"]["operator"]`.

Supported operators:
- `eq` - equal
- `ne` - not equal
- `gt` - greater than
- `ge` - greater than or equal
- `lt` - less than
- `le` - less than or equal
- `beginsWith` - Matches models where the given field begins with the provided value.
- `between` - Matches models where the given field is between the provided start and end values.
- `contains` - Matches models where the given field contains the provided value.

### Basic Equality Operator

Query for equality on a model's attribute.

```dart
const blogTitle = 'Test Blog 1';
final queryPredicate = Blog.NAME.eq(blogTitle);

final request = ModelQueries.list<Blog>(
  Blog.classType,
  where: queryPredicate,
);
final response = await Amplify.API.query(request: request).response;
final blogFromResponse = response.data?.items.first;
```

### Fetch by Parent ID

Get all Posts by parent ID

```dart
final blogId = blog.id;

final request = ModelQueries.list(
  Post.classType,
  where: Post.BLOG.eq(blogId),
);
final response = await Amplify.API.query(request: request).response;
final data = response.data?.items ?? <Post?>[];
```

### Less than

Return Posts with a rating less than 5.

```dart
const rating = 5;

final request = ModelQueries.list(
  Post.classType,
  where: Post.RATING.lt(rating),
);
final response = await Amplify.API.query(request: request).response;

final data = response.data?.items ?? <Post?>[];
```
<!-- /Platform -->
