frankied003
frankied003

Reputation: 516

How would I paginate Firestore data with cloud functions?

I'm trying to paginate data so I can get an infinite scroll for posts on my app. I have a cloud function called getPosts where there are multiple get functions a reads. Everything works fine with that. But when I try thinking of pagination using cloud functions, I run into a problem sending the last snapshot as a query parameter. This snapshot will be super long and an unexpected length, way more than 3000 characters. Here is my getPosts function:

exports.getPosts = (req, res) => {
    const postId = req.query.postId;

    if(postId != null){
        db
            .collection('posts')
            .doc(postId)
            .get()
            .then((doc) => {
                if(!doc.exists){
                    throw 'postNotFound';
                }
                else{
                    const voteCount = sumValues(doc.data().votingOptions);
                    let liked;
                    let votingOptionsDictionary;
                    return db.collection('votes')
                        .where('postId', '==', doc.id)
                        .where('userHandle', '==', req.user.userHandle)
                        .get()
                        .then((voteDoc) => {
                            return db.collection('likes')
                                    .where('postId', '==', doc.id)
                                    .where('userHandle', '==', req.user.userHandle)
                                    .get()
                                    .then((likeDoc) => {
                                        liked = likeDoc.empty ? false : true;
                                        return res.json([{
                                            postId: doc.id,
                                            userHandle: doc.data().userHandle,
                                            postQuestion: doc.data().postQuestion,
                                            userImageUrl: doc.data().userImageUrl,
                                            imageUrl: doc.data().imageUrl,
                                            postDescription: doc.data().postDescription,
                                            createdAt: doc.data().createdAt
                                        }]);
                                    });
                        });
                }
            })
            .catch((err) => {
                if(err == "postNotFound"){
                    return res.json({'Error': `Post ID ${postId} does not exists`});
                }
                else{
                    console.error(err);
                    return res.json({error: err});
                }
            });
    }
    else{
        db
            .collection('posts')
            .orderBy('createdAt', 'desc')
            .limit(10)
            .get()
            .then(async (data) => {
                const promises = await data.docs.map((doc) => {
                    const voteCount = sumValues(doc.data().votingOptions);
                    let liked;
                    let votingOptionsDictionary;
                    return db.collection('votes')
                        .where('postId', '==', doc.id)
                        .where('userHandle', '==', req.user.userHandle)
                        .get()
                        .then((voteDoc) => {
                            return db.collection('likes')
                                    .where('postId', '==', doc.id)
                                    .where('userHandle', '==', req.user.userHandle)
                                    .get()
                                    .then((likeDoc) => {
                                        liked = likeDoc.empty ? false : true;
                                        return {
                                            postId: doc.id,
                                            userHandle: doc.data().userHandle,
                                            postQuestion: doc.data().postQuestion,
                                            userImageUrl: doc.data().userImageUrl,
                                            imageUrl: doc.data().imageUrl,
                                            postDescription: doc.data().postDescription,
                                            createdAt: doc.data().createdAt
                                        };
                                    });
                        });
                })
                Promise.all(promises)
                    .then((posts) => {
                        res.json(posts);
                    })
            })
            .catch((err) => {
                console.error(err);
                res.status(500).json({ error: err.code});
            });
    }
}

I was thinking of saving the snapshot object on the client side, then sending that as an optional query parameter to the getPosts cloud function to get the data I want, but I'm almost positive I can't send that object as a query paramter...

Upvotes: 2

Views: 1436

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317843

If you can't use an actual DocumentSnapshot object as the anchor document for your pagination, you can simply use the field values in the document that are relevant for sort ordering. This is described in the documentation. So, if you have a single ordering on createdAt, can can just pass the relevant field value to startAt() or startAfter().

If you don't have a order defined at all, then the sort order is based on the document ID, and you can simply use a where clause to get all documents greater than or less than the ID you specify. For example: where(FieldPath.documentId(), ">", id).

Upvotes: 3

Related Questions