Topic Filters
Filters narrow delivery within a topic to specific entities. Instead of receiving every update and discarding most of them, subscribe with filters and kraken only delivers the messages you asked for.
Why Filters?
Without filters, subscribing to a topic delivers every message published to it. A dashboard showing 3 bookings out of thousands would receive updates for all of them.
Filters solve this in the broker. Subscribe to bookings with filters ["booking_1", "booking_2"] and only updates for those two bookings are delivered. When the user moves to different bookings, swap the filters without resubscribing.
How It Works
kraken routes each filter value as its own sub-topic of the base topic, so filtering happens before messages reach your client, not by inspecting payloads.
A subscription without filters is a wildcard: it receives every message on the topic, filtered or not. A subscription with filters receives only messages published with one of its filter values. A message published without a filter reaches only the wildcard subscribers.
No filters (wildcard): receives everything on the topic, including filtered publishes
subscribe("bookings")
With filters: receives only publishes carrying one of these values
subscribe("bookings", { filters: ["booking_1"] })
Publish with a filter: reaches subscribers filtering on "booking_1" AND every wildcard subscriber
emit("bookings", data, { filter: "booking_1" })
Publish without a filter: reaches wildcard subscribers only
emit("bookings", data)
Subscribe with Filters
Pass a filters array when subscribing. As with any subscription, do it in the connect handler so it is re-established after a reconnect: kraken does not restore filters for you.
import { NoLag } from "@nolag/js-sdk";
const client = NoLag(token, { url: "ws://localhost:8080/ws" });
const room = client.setApp("dashboard").setRoom("ops");
client.on("connect", () => {
// Only receive updates for these bookings
room.subscribe("bookings", { filters: ["booking_1", "booking_2"] });
});
room.on("bookings", (data, meta) => {
console.log("Booking update:", data);
console.log("Filter:", meta.filter); // e.g. 'booking_1'
});
await client.connect();The Go SDK v0.5.0 re-subscribes its topics after an automatic reconnect but without their options, so a filtered subscription silently becomes unfiltered. A Go client that depends on filters should turn automatic reconnect off and reconnect itself, re-subscribing with the filters each time; the Go SDK page has a complete example.
Dynamic Filter Management
Change filters at any time without unsubscribing. kraken subscribes the new filter values before removing the old ones, to keep the gap small.
// Replace all filters (full swap)
room.setFilters("bookings", ["booking_3", "booking_4"]);
// Add filters to the existing set
room.addFilters("bookings", ["booking_5"]);
// Now active: booking_3, booking_4, booking_5
// Remove specific filters
room.removeFilters("bookings", ["booking_3"]);
// Now active: booking_4, booking_5
// Remove all filters (switch to wildcard, receives everything)
room.setFilters("bookings", []);addFilters and removeFilters are computed by the SDK from the filters it last sent, and sent to kraken as a full replacement. Removing the last filter therefore sends an empty list, which switches the subscription to wildcard mode and delivers everything on the topic. Changing filters on a topic you are not subscribed to is refused with not_subscribed.
Publishing with Filters
Set filter when publishing to target specific subscribers:
// Reaches subscribers with 'booking_1' in their filters,
// plus every wildcard (no-filter) subscriber
room.emit("bookings", { status: "confirmed" }, { filter: "booking_1" });
// Reaches wildcard subscribers only
room.emit("bookings", { type: "system_update" });Two things to plan for. Messages published without a filter only reach wildcard subscribers; filtered subscribers do not receive them, so use a separate topic for broadcasts that everyone must see. And a wildcard subscriber receives everything, including every filtered publish: filters narrow what a subscriber gets, they are not a privacy boundary. Use rooms and access control for that.
AND Groups
A filter entry can itself be a list, which matches only messages tagged with all of its values. Publish to an AND group with filters (plural): kraken lowercases and sorts the values and joins them into one routing key.
// (alice AND admin) OR bob
room.subscribe("bookings", { filters: [["alice", "admin"], "bob"] });
// Reaches subscribers whose group is exactly {alice, admin}, plus wildcard subscribers
room.emit("bookings", { status: "confirmed" }, { filters: ["admin", "alice"] });An AND group matches the exact set of values: a subscriber filtering on bob alone does not receive a publish to ["admin", "alice"], and a publish to ["alice", "admin", "bob"] reaches neither of the groups above. If both filter and filters are set on a publish, filter wins.
Example: Realtime Dashboard
The most common use of filters is a dashboard showing a subset of entities that update live:
const room = client.setApp("dashboard").setRoom("ops");
// Subscribe only to the bookings currently visible
let visibleBookingIds = ["booking_42", "booking_87", "booking_153"];
client.on("connect", () => {
room.subscribe("bookings", { filters: visibleBookingIds });
});
room.on("bookings", (data, meta) => {
updateBookingCard(meta.filter, data);
});
// User navigates to a different page of bookings
function onPageChange(newBookingIds: string[]) {
visibleBookingIds = newBookingIds;
room.setFilters("bookings", newBookingIds);
}
// User opens a booking detail view
function onBookingOpen(bookingId: string) {
room.addFilters("bookings", [bookingId]);
}
// User closes a booking detail view
function onBookingClose(bookingId: string) {
room.removeFilters("bookings", [bookingId]);
}
Filter Rules
- At most 100 filters per subscribe or filter change; more is refused with
too_many_filters (max 100) - Filter values must not be empty or contain
/,#,+or|; kraken refuses them withinvalid_filter_chars (/, #, +, | not allowed)on theerrorevent - An AND group needs at least two values
- A single filter value is matched exactly, case included. AND groups and a publish's
filterslist are lowercased, so stick to lowercase values to avoid surprises - Filters work with load balancing: each filter value joins the same shared group
- Setting filters to an empty array switches back to wildcard mode (receives everything)
meta.filtertells a filtered subscription which value a message matched. On the defaultsynbroker, a subscription without filters sees#there for every message, so do not use it to recover the filter on a wildcard subscription
Filters vs Wildcards
| Scenario | Approach |
|---|---|
| Receive all messages on a topic, filtered or not | Subscribe without filters (wildcard) |
| Receive updates for specific entities | Subscribe with filters |
| Change which entities to track | setFilters / addFilters / removeFilters |
| Broadcast to every subscriber including filtered ones | Use a separate topic (filtered subscribers do not receive filterless publishes) |
| Hide one entity's messages from other subscribers | Filters cannot do this; use a room per entity and access control |