Change Feeds
The Changes Interface
interface Changes<T> {
changeType: "added" | "removed" | "modified";
oldValue?: T;
newValue?: T;
}
| Property | Description |
|---|---|
changeType | The type of change: "added", "removed", or "modified". |
oldValue | The document value before the change. Undefined for new documents. |
newValue | The document value after the change. Undefined for deletions. |
Create a Change Feed
Call changes() on a stream or single selection to build a feed query. Consume it with async iteration to receive events as they arrive:
import { Schema } from "@antelopejs/interface-database";
const schema = Schema.get("myapp")!;
const users = schema.instance().table("users");
// Monitor all changes to a table
const allChanges = users.changes();
// Monitor changes to a specific document
const userChanges = users.get("user-123").changes();
Process Change Events
Each change event includes a changeType field that indicates the nature of the change:
for await (const change of users.changes()) {
if (change.changeType === "added") {
console.log("New document:", change.newValue);
} else if (change.changeType === "modified") {
console.log("Updated from:", change.oldValue);
console.log("Updated to:", change.newValue);
} else {
console.log("Deleted document:", change.oldValue);
}
}
Use with Async Iteration
changes() returns Query<Changes<T>[]>, but Query's cursor and async iterator yield each array element: one Changes<T> event, not a batch. A direct await uses run() and asks the provider for a completed result; an open-ended feed may never complete, so use iteration for continuous monitoring.
import { Schema } from "@antelopejs/interface-database";
import type { Changes } from "@antelopejs/interface-database/common";
interface User {
_id: string;
name: string;
}
export async function watchUsers(onChange: (change: Changes<User>) => boolean) {
const schema = Schema.get<{ users: User }>("myapp");
if (!schema) throw new Error("Register the myapp schema first");
const feed = schema.instance().table("users").changes();
for await (const change of feed) {
console.log(`${change.changeType}:`, change.newValue ?? change.oldValue);
if (!onChange(change)) break;
}
}
Breaking out of the loop closes its cursor. When using cursor() manually, call return() when finished.
Use Cases
Change feeds are well suited for:
- Real-time UI updates - Push changes to connected clients as they happen.
- Event-driven architectures - React to data changes with downstream processing.
- Change observation - Observe writes while a feed is active. Durable auditing requires provider-specific delivery and recovery guarantees; this interface alone does not promise a complete audit log.
- Cache invalidation - Clear or update caches when the underlying data changes.
Stream
The Stream<T> class represents a sequence of documents that can be transformed, filtered, joined, grouped, and aggregated. It extends Query<T[]> and serves as the foundation for collection-based operations in AQL.
Query
The Query<T> class is the base class for all query types in the AQL system. It implements PromiseLike<T>, so queries can be awaited directly, and provides cursor-based async iteration for streaming results.