Skip to content

$query

The $query utility allows you to query multiple documents from a Firestore collection reactively and locally cache their data using IndexedDB via @vueuse/integrations. This is part of the arkfire ecosystem and works alongside $pool and listeners to provide seamless, deduplicated reactive data arrays in sync with firestore.

Purpose

To query a Firestore collection with constraints, cache the results locally, and return a live-updating deduplicated array of document data.

Api

Ref may only be used in a component lifecycle to avoid memory leaks.

ts
const users = $query("users", [where("active", "==", true)])

users is a computed ref that will automatically:

  • Fetch initial query results from Firestore
  • Cache those results locally in IndexedDB
  • Reactively update whenever the underlying documents change

Parameters

colId: string

A string representing the collection ID in Firestore.

query: QueryConstraint[]

An array of Firestore query constraints (e.g. where, orderBy, etc).

All firebase functions are passed through (e.g. documentId(), deleteField(), etc).

Returns

ComputedRef<any[]>

An automatically deduplicated, reactive list of the resulting document data. This is readonly. To update use $col or $doc to update firestore directly.

How It Works

  1. Generate a stable cache key: Uses $pool.serializeQueryKey to turn the query and collection ID into a unique key.

  2. Initial fetch and state tracking: Uses computedAsync to fetch uncached data and populate the useState array for the query.

  3. Listen to changes: $pool.listenToCollection is used to setup reactive subscriptions on document updates.

  4. Local cache setup per document: Each document path is mapped to an useIDBKeyval hook to cache and reactively update individual entries.

  5. Automatic deduplication: Uses Set to ensure unique paths only.

  6. Live list: A computed ref returns the list of document data by pulling .data from each IDB-backed ref.

Dev Notes

  • Server-side execution returns an empty array immediately.
  • Uses Nuxt's useState() to persist query results across components.
  • listeners is responsible for wiring up reactive streams to Firestore listeners and IndexedDB updates.

Example

ts
const posts = $query("posts", [where("published", "==", true), orderBy("createdAt", "desc")])

watchEffect(() => {
    console.log(posts.value) // in sync with firestore
})

You can also listen to non existant document that will be picked up when created. This still counts as a document read.

  • $pool
  • listeners
  • useIDBKeyval (from @vueuse/integrations)

Benefits

  • ✅ Deduplicated query results
  • ✅ Local IndexedDB caching for offline use
  • ✅ Reactive updates using Firestore listeners
  • ✅ Seamless integration with Nuxt state and Vue

This utility abstracts the complexity of merging Firestore streams and caching into a single powerful hook.