RowFilterSpec
Defined in: packages/contracts/src/config.ts:427
Properties
Section titled “Properties”columns?
Section titled “columns?”
optionalcolumns?:string[]
Defined in: packages/contracts/src/config.ts:447
Column projection for the shape URL (e.g. [“id”, “source_text”]).
customWhere?
Section titled “customWhere?”
optionalcustomWhere?: (claims,params?) =>string|SQL<unknown> |null
Defined in: packages/contracts/src/config.ts:445
The row filter: returns the Electric shape where for this request, or null to bypass
filtering (e.g. admin access). Prefer returning a Drizzle SQL fragment built from the
table’s columns: reference each column through c (a bare, rename-safe identifier) and
embed request-derived values directly — they become bound $n params, never hand-escaped
literals. Enum columns must be cast to text (${c(col)}::text = 'x') for Electric’s grammar,
and subqueries must be self-contained (not correlated), since Electric needs plain column refs.
Returning a raw string is the escape hatch for a predicate Drizzle can’t express. SECURITY:
a string is interpolated verbatim into the where — it is NOT escaped, so any request-derived
value you embed must be escaped/validated (escapeSqlLiteral) inside this function, or it is a
SQL-injection vector. Reach for the string form only when the Drizzle fragment cannot express it.
Must be pure. The proxy already calls this fresh on every shape request; the client also
probes it with empty claims ({}) to detect claims-dependence (ADR-0039 —
isClaimsDependentRowFilter). Do not memoize, mutate external state, or assume it runs once.
Parameters
Section titled “Parameters”claims
Section titled “claims”app_metadata?
Section titled “app_metadata?”{[key: string]: unknown; roles?: string[]; } = ...
app_metadata.roles?
Section titled “app_metadata.roles?”string[] = ...
string = ...
params?
Section titled “params?”Record<string, unknown>
Returns
Section titled “Returns”string | SQL<unknown> | null
revision?
Section titled “revision?”
optionalrevision?:string|number
Defined in: packages/contracts/src/config.ts:455
An opaque version tag for the part of this filter the fingerprint cannot see — the customWhere
body (you cannot hash a closure; only its presence is fingerprinted). Bump this (any new
string/number) whenever you change that logic so the fingerprint shifts and the local read cache
rebuilds + the shape subscription resets. Leaving it unchanged after a customWhere
authorization change would silently serve the stale shape.