Query

overview

A query aims to target documents based on a set of criteria, instead of targeting documents through their reference.

the result of a query: a query snapshot

Firestore answers a query with a query snapshot. It wraps a list of document snapshots. The list stays empty if there was no match.

The document snapshots are technically of type QueryDocumentSnapshot, but this type has the same API surface than DocumentSnapshot, so they are conceptually the same.

querySnaptshot.docs // list of document snapshots
querySnaptshot.empty

Document snapshots in the array are guaranteed to have an underlying document at snapshot.data() (QueryDocumentSnapshot reflects that, removing undefined from data())

const cats = querySnapshot.docs.map((docSnapshot) => docSnapshot.data())

a collection reference is technically a query

A collection ref is accepted as a query. In that case, we receive all documents:

getDocs(collectionRef)
collectionRef.get()

build a query

build a query

We query documents that match some criteria. We request a specific order and limit the document count.

const q = query(collectionRef, where(..), where(..), orderBy(..), limit(..))
const q = collection(..).where(..).orderBy(..).limit(..)

where and where operators

We filter documents based on a property. We may request an exact value, or one within a range.

Note: documents that do not possess the property are filtered out.

where(propertyName, operator, value)
where("id", "==", user.id)

where operators (strings):

<
<=
>
>=
==
!=
array-contains // the array contains the specified value
array-contains-any // the array contains at least one of A, B or C ..
in // the property is equal to either A, B or C
not-in // the property is different from A, B and C.

order documents based on a field

We order documents based on the value of a given field. By default, it sorts documents so that the value is ascending. It's best to be explicit about the ordering rather than rely on the default one.

orderBy(propertyName, orderDirection)
orderBy("postCount", "asc")
orderBy("postCount", "desc")
orderBy("postCount") // ascending

we can start from a given value, for example, documents that have at least 10 posts, or more than 10 posts:

startAt(10)
startAfter(10)

limit the size of the query

get at most n documents

limit(n)
limit(5)

pagination: start at or after a given document, that acts as a cursor

When doing pagination, we store the document snapshot we received last, and provide it in the new query.

startAfter(docSnapshot) // start after the docSnapshot
startAt(docSnapshot) // start at the docSnapshot (include it again)

run the query

The client SDK's function name (getDocs) hints that we may receive several documents. The admin SDK is a get function from the query object.

getDocs(query)
query.get()

real-time listener over the query

we provide a callback to handle the query snapshot.

const unsub = onSnapshot(query, (qs) => {
    const documents = qs.docs.map((docSnapshot) => docSnapshot.data())
    setMessages(documents)
})
earlymorning logo

© Antoine Weber 2026 - All rights reserved

Query

overview

A query aims to target documents based on a set of criteria, instead of targeting documents through their reference.

the result of a query: a query snapshot

Firestore answers a query with a query snapshot. It wraps a list of document snapshots. The list stays empty if there was no match.

The document snapshots are technically of type QueryDocumentSnapshot, but this type has the same API surface than DocumentSnapshot, so they are conceptually the same.

querySnaptshot.docs // list of document snapshots
querySnaptshot.empty

Document snapshots in the array are guaranteed to have an underlying document at snapshot.data() (QueryDocumentSnapshot reflects that, removing undefined from data())

const cats = querySnapshot.docs.map((docSnapshot) => docSnapshot.data())

a collection reference is technically a query

A collection ref is accepted as a query. In that case, we receive all documents:

getDocs(collectionRef)
collectionRef.get()

build a query

build a query

We query documents that match some criteria. We request a specific order and limit the document count.

const q = query(collectionRef, where(..), where(..), orderBy(..), limit(..))
const q = collection(..).where(..).orderBy(..).limit(..)

where and where operators

We filter documents based on a property. We may request an exact value, or one within a range.

Note: documents that do not possess the property are filtered out.

where(propertyName, operator, value)
where("id", "==", user.id)

where operators (strings):

<
<=
>
>=
==
!=
array-contains // the array contains the specified value
array-contains-any // the array contains at least one of A, B or C ..
in // the property is equal to either A, B or C
not-in // the property is different from A, B and C.

order documents based on a field

We order documents based on the value of a given field. By default, it sorts documents so that the value is ascending. It's best to be explicit about the ordering rather than rely on the default one.

orderBy(propertyName, orderDirection)
orderBy("postCount", "asc")
orderBy("postCount", "desc")
orderBy("postCount") // ascending

we can start from a given value, for example, documents that have at least 10 posts, or more than 10 posts:

startAt(10)
startAfter(10)

limit the size of the query

get at most n documents

limit(n)
limit(5)

pagination: start at or after a given document, that acts as a cursor

When doing pagination, we store the document snapshot we received last, and provide it in the new query.

startAfter(docSnapshot) // start after the docSnapshot
startAt(docSnapshot) // start at the docSnapshot (include it again)

run the query

The client SDK's function name (getDocs) hints that we may receive several documents. The admin SDK is a get function from the query object.

getDocs(query)
query.get()

real-time listener over the query

we provide a callback to handle the query snapshot.

const unsub = onSnapshot(query, (qs) => {
    const documents = qs.docs.map((docSnapshot) => docSnapshot.data())
    setMessages(documents)
})