Advanced / CODE NOTE

Back to Snippets

Firestore: Next-action queue buckets (Overdue / Today / Tomorrow)

A bounded-query approach for nextActionAt that keeps your queue fast, predictable, and index-friendly.

FirestoreOpsQuery
Receipt: overdue doesn’t hide. it screams.

What this is

The ops queue pattern that prevents “lost leads.”

Queues rot when they’re unbounded. This keeps everything inside time windows, so results stay small and indexes stay sane.

Use it

  • Store nextActionAt as a Firestore Timestamp.
  • Query *only* bounded buckets (before today, today range, tomorrow range).
  • Always orderBy nextActionAt and apply a limit.

Make it yours

  • Change the bucket boundaries and limit while preserving bounded queries.

Reuse contract

Supported environments

  • Firebase Web SDK 12+
  • Firestore composite indexes as required

Security notes

  • Firestore Security Rules and server-side authorization remain required; a bounded client query is not access control.

Code

Language: ts
import {
  collection, getDocs, query, where, orderBy, limit, Timestamp
} from 'firebase/firestore';

type BucketOpts = { before?: Date; start?: Date; end?: Date; lim?: number };

export async function fetchNextActionBucket(
  fs: any,
  colPath: string,
  opts: BucketOpts
) {
  const base = collection(fs, colPath);
  const cs: any[] = [];

  const lim = opts.lim ?? 50;

  if (opts.before) {
    cs.push(where('opsAdmin.nextActionAt', '<', Timestamp.fromDate(opts.before)));
  } else if (opts.start && opts.end) {
    cs.push(where('opsAdmin.nextActionAt', '>=', Timestamp.fromDate(opts.start)));
    cs.push(where('opsAdmin.nextActionAt', '<', Timestamp.fromDate(opts.end)));
  } else {
    return [];
  }

  cs.push(orderBy('opsAdmin.nextActionAt', 'asc'));
  cs.push(limit(lim));

  const snap = await getDocs(query(base, ...cs));
  return snap.docs.map((d: any) => ({ id: d.id, ...(d.data() ?? {}) }));
}