Skip to main content

Server-Side Filters

Beta

This feature is in beta. Core behavior is stable and ready to try, but some APIs or configuration may still evolve before general availability.

Reduce bandwidth and client-side processing by letting the server evaluate filter conditions before sending subscription events.

Overview

EdgeBase supports two filtering modes for database subscriptions. JavaScript, Dart/Flutter, C#/Unity, and C++/Unreal forward chained table filters to the server by default. Swift, Kotlin, and Java currently apply chained subscription filters on the client after receiving events.

Client-SideServer-Side
HowServer sends all events; SDK filters locallyServer evaluates filters; only matching events sent
BandwidthHigher — all events transmittedLower — only matching events
CPUClient evaluatesServer evaluates
FlexibilityAny filter logic8 operators, max 5 conditions per type
Best forOpt-out compatibility or local debuggingProduction, high-traffic tables

Quick Start

const unsubscribe = client.db('app').table('posts')
.where('status', '==', 'published')
.onSnapshot((snapshot) => {
console.log('Published posts:', snapshot.items);
});

JavaScript Default

On JavaScript/TypeScript, any filtered table subscription created with .where() or .whereOr() now uses server-side filtering automatically.

If you intentionally want the old client-side behavior, opt out explicitly:

const unsubscribe = client.db('app').table('posts')
.where('status', '==', 'published')
.onSnapshot(handler, { serverFilter: false });

AND Filters

AND filters require all conditions to match. Chain multiple .where() calls:

// Both conditions must be true
const unsubscribe = client.db('app').table('posts')
.where('status', '==', 'published')
.where('authorId', '==', 'user-123')
.onSnapshot(handler);

OR Filters

OR filters require at least one condition to match. Use .whereOr():

// status is 'published' OR 'featured'
const unsubscribe = client.db('app').table('posts')
.where('authorId', '==', 'user-123')
.whereOr('status', '==', 'published')
.whereOr('status', '==', 'featured')
.onSnapshot(handler);

This is equivalent to: WHERE authorId = 'user-123' AND (status = 'published' OR status = 'featured')

Supported Operators

OperatorDescriptionExample
==Equal to['status', '==', 'published']
!=Not equal to['status', '!=', 'draft']
<Less than['price', '<', 100]
<=Less than or equal['price', '<=', 100]
>Greater than['score', '>', 90]
>=Greater than or equal['score', '>=', 90]
inContained in array['category', 'in', ['tech', 'science']]
containsArray field contains value['tags', 'contains', 'featured']

Changing Filters

The high-level query API does not expose an in-place updateFilters handle. Unsubscribe and create a new filtered subscription:

// Initially subscribe to published posts
let unsubscribe = client.db('app').table('posts')
.where('status', '==', 'published')
.onSnapshot(handler);

// Later, switch to draft posts.
unsubscribe();
unsubscribe = client.db('app').table('posts')
.where('status', '==', 'draft')
.onSnapshot(handler);

Limits

LimitValue
AND filter conditions5 max per subscription
OR filter conditions5 max per subscription

Exceeding these limits returns an INVALID_FILTERS error and the subscription is rejected.

Filter Evaluation Order

When the server processes a database change event, it evaluates in this order:

  1. Access rule check — The table's read rule was evaluated at subscribe time.
  2. AND filter evaluation — Every AND condition must match the event data.
  3. OR filter evaluation — At least one OR condition must match (if OR filters exist).
  4. Delivery — Only if both AND and OR filters pass, the event is sent to the subscriber.
info

Filters can only restrict which events you receive — they cannot bypass access rules. If your read rule denies access, no filter combination will override it.

Hibernation Recovery

When a Durable Object hibernates (idle WebSocket) and wakes up, all in-memory filter state is lost. The server sends a FILTER_RESYNC message to all authenticated sockets, and the SDK automatically re-registers all filter conditions.

SDK transports that registered server-side filters retain those conditions and re-send them automatically. This happens transparently to the active onSnapshot subscription.