Firestore real-time updates with tenant isolation

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:
- The frontend subscribes directly to Firestore for live updates
- The backend only issues scoped authentication tokens
- 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
npnuytqgp617t7vepjecwq8udwdgzk.webp)
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:
- Who the user is
- Which organization (tenant) they belong to
Using the Firebase Admin SDK, this information is embedded as custom claims on the user.
Example (NestJS):
typescript
Copied!
>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:
- Custom claims cannot be forged by the client
- Tokens are signed by Firebase
- 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
Copied!
>import { signInWithCustomToken } from "firebase/auth";
>
>await signInWithCustomToken(firebaseAuth, firebaseToken);
>
>After this step:
- Firebase Auth is initialized
- Firestore listeners automatically include the user’s auth context
- 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
Copied!
> 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:
- Even if a client modifies queries, Firestore enforces isolation
- Cross-tenant data leaks are prevented on the server side
- 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
Copied!
> 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:
- Only documents that pass the security rules are delivered
- Updates stream in real time
- No backend polling or WebSockets are required
Why this pattern works well
Benefits
- Ultra-low-latency live updates
- Reduced backend load
- A simple and clear mental model
- Strong tenant isolation enforced at the data layer
Trade-offs
- The backend must handle token issuance correctly
- Security rules need to be carefully tested
- Write operations often still belong in the backend
This pattern is particularly well-suited for:
- Analytics dashboards (our case)
- Activity feeds
- 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.

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.