I can’t really seem to wrap my head around how this is done. Don’t think the documentation is explaining it very well, or maybe I’m just not getting it 🙂
What I’m trying to do is have a list that start out by showing X items. Then with a click of a button load the next X items in the collection…
I’ve created this generic function which I can call:
let latestDoc = null; export const getDocumentsPaginated = async <T extends { id?: string }>(collectionPath: string, numberOfDocs: number) => { const items: T[] = []; const q = query(collection(db, collectionPath), orderBy('createdAt', 'desc'), startAfter(latestDoc || 0), limit(numberOfDocs)); const querySnapshot = await getDocs(q); console.log('querySnapshot', querySnapshot) latestDoc = querySnapshot.docs[querySnapshot.docs.length - 1]; querySnapshot.forEach((doc) => { const data = doc.data(); items.push({ ...data, id: doc.id } as T); }); return items; };
Then trying to call it from a Vue component:
const fetchExercises = async () => { isLoading.value = true; try { const result = await getDocumentsPaginated<ExerciseDocument>(`exercises/${companyId}/exercises`, 5); console.log('internal', result); exercises.value = result; isLoading.value = false; } catch (e) { throw new Error('cant load exercises ' + e); } };
This does not seem to work. I just get an empty array back even though I know the collection has a lot of documents?
What am I doing wrong? Just really use a helping hand
Advertisement
Answer
Firestore pagination does not work based on offsets, so you’re adding 20 to a counter will not work (as you’ve found).
Firestore pagination works based on cursors object. This means that to query for the documents on the second page, you must know the data of the last item on the first page.
Since you orderBy('createdAt', 'desc')
, you will need to know the createdAt
value (and the document ID) of the last document on the first page, and pass those to startAfter
to get the documents for the second page.
The easiest way to make pagination work in Firestore is to keep the DocumentSnapshot
of the last item of the current page around in a variable with something like:
const querySnapshot = await getDocs(q); lastDocumentOnCurrentPage = querySnapshot.docs[querySnapshot.docs.length-1];
Then when you need to load the next page of data, you pass this variable to the startAfter
condition of the query.