Skip to main content

Server-Side Filters

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

Overview

EdgeBase supports two filtering modes for realtime subscriptions:

Client-Side (Default)Server-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 forDevelopment, low-traffic tablesProduction, high-traffic tables

Quick Start

const unsubscribe = client.db('shared').table('posts')
.where('status', '==', 'published')
.onSnapshot((event) => {
console.log('Published post changed:', event.data);
}, { serverFilter: true });

AND Filters

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

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

The subscribe message sent to the server:

{
"type": "subscribe",
"channel": "realtime:shared:posts",
"filters": [
["status", "==", "published"],
["authorId", "==", "user-123"]
]
}

OR Filters

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

// status is 'published' OR 'featured'
const unsubscribe = client.db('shared').table('posts')
.where('authorId', '==', 'user-123')
.whereOr('status', '==', 'published')
.whereOr('status', '==', 'featured')
.onSnapshot(handler, { serverFilter: true });

The subscribe message:

{
"type": "subscribe",
"channel": "realtime:shared:posts",
"filters": [["authorId", "==", "user-123"]],
"orFilters": [["status", "==", "published"], ["status", "==", "featured"]]
}

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

WebSocket Subscribe with Filters

When subscribing to a channel, you can add server-side filters directly in the WebSocket message:

{
"type": "subscribe",
"channel": "db:posts",
"filters": [
["status", "==", "published"],
["authorId", "==", "user-123"]
],
"orFilters": [
["category", "==", "news"],
["category", "==", "tech"]
]
}
FieldTypeRequiredDescription
channelstringYesThe channel to subscribe to
filtersarrayNoAND conditions — all must match for the change to be delivered
orFiltersarrayNoOR conditions — any one match is sufficient

Each filter is a tuple in the format [field, operator, value]. Maximum 5 conditions per filter array.

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']

Updating Filters at Runtime

Change your filter conditions without re-subscribing using update_filters:

// Initially subscribe to published posts
const sub = client.db('shared').table('posts')
.where('status', '==', 'published')
.onSnapshot(handler, { serverFilter: true });

// Later, switch to draft posts without reconnecting
sub.updateFilters([
{ field: 'status', op: '==', value: 'draft' },
]);

// Remove all filters (receive everything)
sub.updateFilters(null);

The update_filters message sent to the server:

{
"type": "update_filters",
"channel": "realtime:shared:posts",
"filters": [["status", "==", "draft"]],
"orFilters": null
}

The server responds with a filters_updated confirmation:

{
"type": "filters_updated",
"channel": "realtime:shared:posts",
"serverFilter": true
}
FieldTypeDescription
filtersarray | nullNew AND conditions. Set to null to clear AND filters.
orFiltersarray | nullNew OR conditions. Set to null to clear OR filters.
  • Set a field to null to clear that filter type
  • No need to unsubscribe and resubscribe -- filters are updated in-place
note

You must already be subscribed to the channel before calling update_filters. Attempting to update filters on an unsubscribed channel returns a NOT_SUBSCRIBED error.

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. If it passed, the subscription is active.
  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.

This happens transparently — your onSnapshot callbacks continue working without interruption.

note

FILTER_RESYNC is sent only to authenticated sockets (unlike PRESENCE_RESYNC which goes to all sockets). Clients must complete re-authentication before their filters are restored.