Reactivity

ElectricSQL is a reactive database system. It allows you to subscribe and bind queries to data changes. This applies to both local changes (when, as the user, you change the data) and replicated changes (when someone else changes the data).

Data flow

ElectricSQL integrates with both SQLite and Postgres. For Postgres, integration is via logical replication. For SQLite, writes are made to the local SQLite database. Replication is implemented using triggers. These triggers record all insert, update and delete operations in an oplog table.

After each transaction that may have changed the data in the local database, the electrified database driver emits a “potential data change” event. This triggers the background replication process to check the oplog. If new entries are found, these are replicated and an “actual data changed” notification is emitted, with the tablename and primary key identifiers of the changes data. Components and dynamic queries can subscribe to these events to re-query / re-render.

The same applies to updates that are applied from the replication stream. Except in this case the background replication process simply emits the change events directly.

Note that the background replication process also polls the oplog, so if for some reason data is changed without triggering a “potential data change” event, then the changes will eventually get picked up.

Notifier API

The electrified database client has an electric namespace added to it as a public property, e.g.:

const db = electrify(original, config)
// has `db.electric` property

This electric property provides the ElectricNamespace interface:

  • adapter: a normalised database client adapter that provides the DatabaseAdapter interface
  • notifier: a notifier instance that provides the Notifier interface
  • potentiallyChanged(): a method that calls notifier.potentiallyChanged()

The Notifier interface provides methods to emit and subscribe to the events used in the reactive dataflow. You can use this to develop custom integrations and hooks and to manually trigger replication by calling electric.potentiallyChanged().

Framework Hooks

ElectricSQL implements reactive framework hooks for popular frameworks. These include useElectric and useElectricQuery React hooks. See the frameworks guide for more information. You can also use the source code as a reference if implementing your own framework integrations.

For example, the following is a snippet from within the useElectricQuery hook:

// Once we have the tablenames, we then establish the data change
// notification subscription, comparing the tablenames used by the
// query with the changed tablenames in the data change notification
// to determine whether to re-query or not.
//
// If we do need to re-query, then we call `bustCache` to set a new
// `cacheKey`, which is a dependency of the next useEffect below
useEffect(() => {
  if (electric === undefined || tablenames === undefined) {
    return
  }

  const notifier = electric.notifier
  const handleChange = (notification: ChangeNotification): void => {
    // Reduces the `ChangeNotification` to an array of namespaced tablenames,
    // in a way that supports both the main namespace for the primary database
    // and aliases for any attached databases.
    const changedTablenames = notifier.alias(notification)

    if (hasIntersection(tablenames, changedTablenames)) {
      bustCache()
    }
  }

  const key = notifier.subscribeToDataChanges(handleChange)
  if (changeSubscriptionKey !== undefined) {
    notifier.unsubscribeFromDataChanges(changeSubscriptionKey)
  }
  setChangeSubscriptionKey(key)

  return () => notifier.unsubscribeFromDataChanges(key)
}, [electric, tablenamesKey])

Next step