Dev diary - 20. January 2026

Firestore real-time updates with tenant isolation

header_image

Firestore was the right choice when one of our clients came to us with a clear requirement: ultra-low-latency live updates.

A typical architecture usually follows a familiar and safe pattern:

Client → Backend → Database → Backend → Client

This approach is correct and widely used, but in our case it introduced avoidable latency and unnecessary backend load for a problem that Firebase already solves extremely well.

Firestore provides native real-time listeners with automatic fan-out, so the real question became:

How do we let the frontend listen directly to Firestore without compromising multi-tenant security?

This Dev Diary describes a clean, production-ready pattern where:

  1. The frontend subscribes directly to Firestore for live updates
  2. The backend only issues scoped authentication tokens
  3. Firestore Security Rules strictly enforce tenant (organization) isolation

The result is real-time data delivery with minimal latency and a significantly simpler backend.

High-level architecture


The backend never proxies reads. Its only responsibility here is vouching for identity and tenant scope.

Step 1: Backend issues a Firebase custom token

The backend is the only trusted system in this setup. It decides:

  1. Who the user is
  2. Which organization (tenant) they belong to

Using the Firebase Admin SDK, this information is embedded as custom claims on the user.

Example (NestJS):

typescript

>import { getAuth } from 'firebase-admin/auth';
>

>const auth = getAuth();
>

>// Associate orgId with the user
>await auth.setCustomUserClaims(userId, {
>orgId: organizationId,
>});
>

>// Issue a custom Firebase token
>const firebaseToken = await auth.createCustomToken(userId);
>

>return { firebaseToken };
>

>

Important properties:

  1. Custom claims cannot be forged by the client
  2. Tokens are signed by Firebase
  3. Firestore Security Rules can safely trust request.auth.token.*

Step 2: Frontend authenticates with Firebase

The frontend receives the token and signs into Firebase once.

typescript

>import { signInWithCustomToken } from "firebase/auth";
>

>await signInWithCustomToken(firebaseAuth, firebaseToken);
>

>

After this step:

  1. Firebase Auth is initialized
  2. Firestore listeners automatically include the user’s auth context
  3. No backend involvement is needed for read operations

Step 3: Firestore security rules enforce tenant isolation

This is the most critical part of the entire setup.

Firestore rules ensure that a user can only read documents that belong to their organization.

Example rule:

javascript

> rules_version = '2';
>service cloud.firestore {
>  match /databases/{database}/documents {
>    match /events/{eventId} {
>      allow read: if request.auth != null
>        && request.auth.token.orgId == resource.data.orgId;
>    }
>  }
>}
>

>

What this guarantees:

  1. Even if a client modifies queries, Firestore enforces isolation
  2. Cross-tenant data leaks are prevented on the server side
  3. Rules apply equally to queries and live listeners

Step 4: Frontend sets up a Live Listener

With authentication and rules in place, setting up real-time updates becomes straightforward.

javascript

> import { onSnapshot, query, collection, where } from 'firebase/firestore';
>

>const q = query(
>  collection(db, 'events'),
>  where('orgId', '==', currentOrgId)
>);
>

>const unsubscribe = onSnapshot(q, snapshot => {
>  const events = snapshot.docs.map(d => d.data());
>  updateUI(events);
>});
>

>

Firestore ensures that:

  1. Only documents that pass the security rules are delivered
  2. Updates stream in real time
  3. No backend polling or WebSockets are required

Why this pattern works well

Benefits

  1. Ultra-low-latency live updates
  2. Reduced backend load
  3. A simple and clear mental model
  4. Strong tenant isolation enforced at the data layer

Trade-offs

  1. The backend must handle token issuance correctly
  2. Security rules need to be carefully tested
  3. Write operations often still belong in the backend

This pattern is particularly well-suited for:

  1. Analytics dashboards (our case)
  2. Activity feeds
  3. Live operational views

Let Firestore do what it does best: real-time fan-out.

Let the backend do what it does best: identity, trust, and scoping.

By combining custom Firebase tokens with strict Firestore rules, you get secure, tenant-aware live updates — without routing every read through your backend.


blog author
Author
Germán Distel

I'm a Fullstack JavaScript Developer who enjoys bringing ideas to life using NestJS, React, and Angular—plus a bit of Python when needed. Outside of coding, I like trying out new technologies, reading, and spending quality time playing with my daughter.

Read more

Contact us

Let's talk