Realtime Queries
With the @hyper-fetch/firebase-admin adapter, you can listen to realtime updates from both Firestore and
Realtime Database. This allows you to build reactive applications that respond instantly to data changes on the
backend.
This guide will teach you how to:
- Set up the firebase admin socket adapter.
- Listen to realtime data streams from Firestore and Realtime Database.
- Handle incoming data and additional details.
- Use advanced options for Firestore and Realtime Database listeners.
- Filter realtime queries to get only the data you need.
Setup
First, you need to have the Firebase Admin SDK configured. Then, you can create a Socket instance from
@hyper-fetch/sockets and pass the FirebaseSocketsAdminAdapter to it.
import { initializeApp, cert } from "firebase-admin/app";
import { getFirestore } from "firebase-admin/firestore";
import { getDatabase } from "firebase-admin/database";
import { Socket } from "@hyper-fetch/sockets";
import { FirebaseSocketsAdminAdapter } from "@hyper-fetch/firebase-admin";
// Initialize Firebase Admin
const serviceAccount = require("./serviceAccountKey.json");
initializeApp({
credential: cert(serviceAccount),
databaseURL: "https://<DATABASE_NAME>.firebaseio.com",
});
// Get Firestore and Realtime Database instances
const firestoreDb = getFirestore();
const realtimeDb = getDatabase();
// Create a Socket instance for Firestore
const firestoreSocket = new Socket({
adapter: FirebaseSocketsAdminAdapter(firestoreDb),
});
// Create a Socket instance for Realtime Database
const realtimeSocket = new Socket({
adapter: FirebaseSocketsAdminAdapter(realtimeDb),
});
The firebase-admin SDK is intended for backend use. Never expose your service account credentials in a client-side
application.
Listening for Changes
To listen for realtime updates, you create a listener from your Socket instance. The listener is then used to open a
connection and receive data. Firebase offers two primary methods for realtime updates: onSnapshot for Firestore and
onValue for Realtime Database.
The .listen() method takes a callback that fires whenever new data is available. It returns an unmount function,
which you can call to close the connection.
Firestore
For Firestore, the adapter uses onSnapshot to listen for document or query changes.
// types.ts
interface Tea {
id: string;
name: string;
type: "Green" | "Black" | "Oolong";
}
// listener.ts
import { firestoreSocket } from "./setup"; // Assume setup is in a separate file
const teasListener = firestoreSocket.createListener<Tea[]>()({
endpoint: "teas",
});
const unmount = teasListener.listen({
callback: ({ data, extra }) => {
console.log("Received data:", data);
// `extra` contains additional details like the raw snapshot
console.log("Extra details:", extra);
},
});
// To stop listening for updates
// unmount();
The callback receives an object with:
data: The data from the snapshot, typed according to the listener.extra: An object containing:ref: The Firebase reference to the path.snapshot: The raw Firebase snapshot.status: A status indicating whether the query succeeded or failed.
Advanced Options
The firebase-admin adapter provides special options for both Firestore and Realtime Database listeners.
Firestore Options
groupByChangeType
When listening to a collection, you can set groupByChangeType: true in the listener's options. This adds a
groupedResult object to the extra parameter in your callback, which categorizes document changes into added,
modified, and removed.
const teasListener = firestoreSocket.createListener<Tea[]>()({
endpoint: "teas",
options: { groupByChangeType: true },
});
teasListener.listen({
callback: ({ data, extra }) => {
const { added, modified, removed } = extra.groupedResult;
console.log("Added teas:", added);
console.log("Modified teas:", modified);
console.log("Removed teas:", removed);
},
});
Realtime Database Options
onlyOnce
For Realtime Database listeners, you can use the onlyOnce: true option to fetch the data a single time, behaving like
get() instead of establishing a persistent listener.
import { realtimeSocket } from "./setup";
const configListener = realtimeSocket.createListener<any>()({
endpoint: "config",
options: { onlyOnce: true },
});
configListener.listen({
callback: ({ data }) => {
console.log("Fetched config once:", data);
},
});
Filtering Queries
You can apply filters to your realtime queries to receive only the data that matches specific criteria. The adapter
provides helper functions like $where to construct these filters. These functions are available for import from the
@hyper-fetch/firebase and @hyper-fetch/firebase-admin packages.
import { $where } from "@hyper-fetch/firebase-admin";
import { firestoreSocket } from "./setup";
// Listen for changes only for Green teas
const greenTeasListener = firestoreSocket.createListener<Tea[]>()({
endpoint: "teas",
options: {
constraints: [$where("type", "==", "Green")],
},
});
greenTeasListener.listen({
callback: ({ data }) => {
console.log("Green teas:", data);
},
});
You can use all the available firebase constraints like $limit, $orderBy etc.
