Skip to content

Realtime Event Filtering

Filter realtime events to receive only the data you need. Reduce noise, save bandwidth, and simplify your event handlers.

Overview

Centrali Realtime supports three levels of filtering:

  1. Structure filtering - Only events from specific structures
  2. Event type filtering - Only specific event types (create, update, delete)
  3. Data filtering (CFL) - Filter by record field values

Structure Filtering

Receive events only from specific structures:

centrali.realtime.subscribe({
  structures: ['order', 'invoice'],  // Only these structures
  onEvent: handleEvent
});

Leave empty to receive events from all structures:

centrali.realtime.subscribe({
  // structures: [],  // or omit entirely
  onEvent: handleEvent  // Receives all structure events
});

Event Type Filtering

Filter by event type:

centrali.realtime.subscribe({
  events: ['record_created'],  // Only new records
  onEvent: handleEvent
});

// Multiple event types
centrali.realtime.subscribe({
  events: ['record_created', 'record_updated'],  // Creates and updates only
  onEvent: handleEvent
});

Available event types: - record_created - New record created - record_updated - Record modified - record_deleted - Record deleted

Data Filtering with CFL

Centrali Filter Language (CFL) v1 lets you filter events based on record data values.

Event Data Structure

The data field structure differs by event type:

Event Type Data Structure Filter Path
record_created { field1, field2, ... } data.field1
record_updated { before: {...}, after: {...} } data.after.field1 or data.before.field1
record_deleted { field1, field2, ... } data.field1

For record_updated events, use data.after.* to filter on the new values or data.before.* to filter on the old values.

Basic Syntax

field:value                    # Equality (default)
field:operator:value          # Explicit operator

Examples

// Simple equality
centrali.realtime.subscribe({
  structures: ['order'],
  filter: 'data.status:shipped',  // Only shipped orders
  onEvent: handleEvent
});

// Numeric comparison
centrali.realtime.subscribe({
  structures: ['order'],
  filter: 'data.total:gt:1000',  // Orders over $1000
  onEvent: handleEvent
});

// String matching
centrali.realtime.subscribe({
  structures: ['customer'],
  filter: 'data.email:endswith:@company.com',  // Company emails only
  onEvent: handleEvent
});

Supported Operators

Operator Description Example
eq Equals (default) data.status:shipped or data.status:eq:shipped
ne Not equals data.status:ne:cancelled
gt Greater than data.amount:gt:100
lt Less than data.amount:lt:50
gte Greater than or equal data.priority:gte:5
lte Less than or equal data.quantity:lte:10
in Value in list data.status:in:pending,processing,shipped
nin Value not in list data.status:nin:cancelled,refunded
contains Contains substring data.name:contains:test
startswith Starts with prefix data.sku:startswith:PROD-
endswith Ends with suffix data.email:endswith:@example.com

Nested Fields

Access nested object fields using dot notation:

// Record data: { address: { city: "New York", country: "US" } }
filter: 'data.address.city:New York'
filter: 'data.address.country:in:US,CA,UK'

Data Types

CFL automatically handles type conversion:

Strings:

filter: 'data.status:active'
filter: 'data.name:contains:test'

Numbers:

filter: 'data.price:gt:99.99'
filter: 'data.quantity:lte:10'

Booleans:

filter: 'data.isActive:true'
filter: 'data.isPremium:ne:true'

Combining Filters

You can combine structure, event, and data filters:

centrali.realtime.subscribe({
  structures: ['order'],                    // Only orders
  events: ['record_created'],               // For creates, filter on data directly
  filter: 'data.status:in:shipped,delivered',
  onEvent: handleEvent
});

// For updates, filter on data.after (the new state)
centrali.realtime.subscribe({
  structures: ['order'],
  events: ['record_updated'],
  filter: 'data.after.status:in:shipped,delivered',
  onEvent: handleEvent
});

Filter evaluation order: 1. Structure filter (client-side for efficiency) 2. Event type filter (server-side) 3. CFL data filter (server-side)

Events must pass all filters to be delivered.

Use Cases

High-Value Orders Dashboard

centrali.realtime.subscribe({
  structures: ['order'],
  events: ['record_created'],
  filter: 'data.total:gte:500',
  onEvent: (event) => {
    showNotification(`High-value order: $${event.data.total}`);
  }
});

Status Change Notifications

// For update events, data contains { before, after } objects
centrali.realtime.subscribe({
  structures: ['shipment'],
  events: ['record_updated'],
  filter: 'data.after.status:in:in_transit,delivered',
  onEvent: (event) => {
    const { before, after } = event.data;
    notifyCustomer(after.customerId, after.status);
  }
});

Regional Data

centrali.realtime.subscribe({
  structures: ['store'],
  filter: 'data.region:west_coast',
  onEvent: handleWestCoastStoreEvent
});

VIP Customer Activity

centrali.realtime.subscribe({
  structures: ['customer', 'order'],
  filter: 'data.tier:premium',
  onEvent: (event) => {
    logVIPActivity(event);
  }
});

Performance Considerations

Filter Early

Apply the most restrictive filters to reduce data transfer:

// Good - filter on server
centrali.realtime.subscribe({
  structures: ['order'],
  filter: 'data.status:pending',
  onEvent: handlePendingOrders
});

// Less efficient - filtering in handler
centrali.realtime.subscribe({
  structures: ['order'],
  onEvent: (event) => {
    if (event.data.status === 'pending') {  // Filtering client-side
      handlePendingOrders(event);
    }
  }
});

Use Specific Structures

Always specify structures when possible:

// Good
structures: ['order']

// Less efficient
structures: []  // Receives all structure events

Limitations

Single CFL Expression

Currently, only one CFL filter expression is supported per subscription. For complex filtering:

// Option 1: Multiple subscriptions
const sub1 = centrali.realtime.subscribe({
  filter: 'data.status:pending',
  onEvent: handlePending
});

const sub2 = centrali.realtime.subscribe({
  filter: 'data.status:processing',
  onEvent: handleProcessing
});

// Option 2: Use 'in' operator
centrali.realtime.subscribe({
  filter: 'data.status:in:pending,processing',
  onEvent: (event) => {
    if (event.data.status === 'pending') handlePending(event);
    else handleProcessing(event);
  }
});

No Logical Operators

CFL v1 doesn't support AND/OR operators. Use multiple subscriptions or client-side filtering for complex logic.

Case Sensitivity

String comparisons for contains, startswith, and endswith are case-insensitive. Equality checks (eq) are case-sensitive.

Error Handling

Invalid filter expressions return a 400 error:

centrali.realtime.subscribe({
  filter: 'invalid::filter',  // Malformed
  onError: (error) => {
    // error.code will be 'CONNECTION_ERROR'
    // Check server response for details
    console.error('Filter error:', error.message);
  },
  onEvent: handleEvent
});