Pagination
Pagination is the process of dividing data into discrete pages. In Appwrite, it is achieved by using an offset or a cursor, which both come with their own use case and benefits.
Offset Pagination
Using Query.limit()
and Query.offset()
you can achieve one of the most common approaches to pagination. With Query.limit()
you can define to how many documents that can be returned from one request, which can be up to a maximum of 100 documents. The Query.offset()
is simply the number of records you wish to skip before selecting records.
-
Web
import { Client, Databases, Query } from "appwrite"; const client = new Client() .setEndpoint('https://cloud.appwrite.io/v1') .setProject('[PROJECT_ID]'); const databases = new Databases(client); // Page 1 const page1 = await databases.listDocuments( '[DATABASE_ID]', '[COLLECTION_ID]', [ Query.limit(25), Query.offset(0) ] ); // Page 2 const page2 = await databases.listDocuments( '[DATABASE_ID]', '[COLLECTION_ID]', [ Query.limit(25), Query.offset(25) ] );
-
Flutter
import 'package:appwrite/appwrite.dart'; void main() async { final client = Client() .setEndpoint('https://cloud.appwrite.io/v1') .setProject('[PROJECT_ID]'); final databases = Databases(client); final page1 = await databases.listDocuments( databaseId: '[DATABASE_ID]', collectionId: '[COLLECTION_ID]', queries: [ Query.limit(25), Query.offset(0) ] ); final page2 = await databases.listDocuments( databaseId: '[DATABASE_ID]', collectionId: '[COLLECTION_ID]', queries: [ Query.limit(25), Query.offset(25) ] ); }
-
Android
import io.appwrite.Client import io.appwrite.Query import io.appwrite.services.Databases suspend fun main() { val client = Client(applicationContext) .setEndpoint("https://cloud.appwrite.io/v1") .setProject("[PROJECT_ID]") val databases = Databases(client) val page1 = databases.listDocuments( databaseId = "[DATABASE_ID]", collectionId = "[COLLECTION_ID]", queries = [ Query.limit(25), Query.offset(0) ] ) val page2 = databases.listDocuments( databaseId = "[DATABASE_ID]", collectionId = "[COLLECTION_ID]", queries = [ Query.limit(25), Query.offset(25) ] ) }
-
Apple
import Appwrite import AppwriteModels func main() async throws { let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") .setProject("[PROJECT_ID]") let databases = Databases(client) let page1 = try await databases.listDocuments( databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", queries: [ Query.limit(25), Query.offset(0) ] ) let page2 = try await databases.listDocuments( databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", queries: [ Query.limit(25), Query.offset(25) ] ) }
The maximum offset is 5000, since the request gets slower as the number of records increases because the database has to read up to the offset number of rows to know where it should start selecting data. Also when there is data added in high frequency - the individual pages might skip results.
Cursor Pagination
The cursor is a unique identifier for a document that points to where the next page should start. After reading a page of documents, pass the last document's ID into the Query.cursorAfter(lastId)
query method to get the next page of documents. Pass the first document's ID into the Query.cursorBefore(firstId)
query method to retrieve the previous page.
-
Web
import { Databases, Query } from "appwrite"; const client = new Client() .setEndpoint("https://cloud.appwrite.io/v1") .setProject("[PROJECT_ID]"); const databases = new Databases(client); // Page 1 const page1 = await databases.listDocuments( '[DATABASE_ID]', '[COLLECTION_ID]', [ Query.limit(25), ] ); const lastId = page1.documents[page1.documents.length - 1].$id; // Page 2 const page2 = await databases.listDocuments( '[DATABASE_ID]', '[COLLECTION_ID]', [ Query.limit(25), Query.cursorAfter(lastId), ] );
-
Flutter
import 'package:appwrite/appwrite.dart'; void main() async { final client = Client() .setEndpoint('https://cloud.appwrite.io/v1') .setProject('[PROJECT_ID]'); final databases = Databases(client); final page1 = await databases.listDocuments( databaseId: '[DATABASE_ID]', collectionId: '[COLLECTION_ID]', queries: [ Query.limit(25) ] ); final lastId = page1.documents[page1.documents.length - 1].$id; final page2 = await databases.listDocuments( databaseId: '[DATABASE_ID]', collectionId: '[COLLECTION_ID]', queries: [ Query.limit(25), Query.cursorAfter(lastId) ] ); }
-
Android
import android.util.Log import io.appwrite.AppwriteException import io.appwrite.Client import io.appwrite.Query import io.appwrite.services.Databases suspend fun main() { val client = Client(applicationContext) .setEndpoint("https://cloud.appwrite.io/v1") .setProject("[PROJECT_ID]") val databases = Databases(client) val page1 = databases.listDocuments( databaseId = "[DATABASE_ID]", collectionId = "[COLLECTION_ID]", queries = [ Query.limit(25) ] ) val lastId = page1.documents[page1.documents.size - 1].$id val page2 = databases.listDocuments( databaseId = "[DATABASE_ID]", collectionId = "[COLLECTION_ID]", queries = [ Query.limit(25), Query.cursorAfter(lastId) ] ) }
-
Apple
import Appwrite import AppwriteModels func main() async throws { let client = Client() .setEndpoint("https://cloud.appwrite.io/v1") .setProject("[PROJECT_ID]") let databases = Databases(client) let page1 = try await databases.listDocuments( databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", queries: [ Query.limit(25) ] ) let lastId = page1.documents[page1.documents.count - 1].$id let page2 = try await databases.listDocuments( databaseId: "[DATABASE_ID]", collectionId: "[COLLECTION_ID]", queries: [ Query.limit(25), Query.cursorAfter(lastId) ] ) }
When to use what?
The different scenarios in which offset or cursor pagination make the most sense depends on the data itself and how often new records are added. When querying static data, the performance cost alone may not be enough for you to use a cursor, as the added complexity that comes with it may be more than you need.
Offset pagination should be used for static data with an indicator to what is the current page and how many pages are available in total. For example a list with up to 20 pages or static data like a list of countries or currencies.
Cursor pagination should be used high-frequency data which is lazy-loaded with infinite scrolling. For example a feed, comment section, chat history or high volume datasets.