Skip to content

Reactive local-first queries: subscribe to DB changes instead of manual sync + stream orchestration #3332

@litury

Description

@litury

Problem

Currently, to display messages in a conversation, developers must:

  1. Call sync() (network request, blocks UI)
  2. Call messages() to read from local DB
  3. Subscribe to stream() for real-time updates
  4. Manually merge streamed messages into the UI state

This means users see a loading spinner when opening an existing conversation — even though the messages are already stored locally in the SDK's SQLite database.

The SDK manages the local DB, controls sync(), and handles stream(). But it doesn't expose a way to reactively subscribe to DB state changes. Every consumer has to reinvent this glue layer.

Expected behavior

// Pseudocode
const messages = conversation.observeMessages() // immediately returns local data + emits on any change

// Under the hood:
// 1. Returns cached messages from SQLite instantly
// 2. SDK triggers sync() automatically when needed
// 3. Stream writes go into SQLite → triggers observer
// 4. sync() results go into SQLite → triggers observer
// UI stays in sync without manual orchestration

Opening a conversation with an existing local DB should never block on a network call. Local data first, network updates in the background.

Why this matters

  • Every SDK consumer hits this. Android, iOS, browser, React Native — all have to build the same local-first wrapper around sync() + stream() + manual state merge.
  • The SDK already has all the pieces. SQLite is there, sync is there, streaming is there. The missing part is a reactive read API on top of the DB.
  • Native platforms have established patterns for this. Room + Flow (Android), Core Data + NSFetchedResultsController (iOS), SQLite + observable queries. The SDK's DB layer could expose something similar at the Rust/FFI level.

Scope

This applies to:

  • conversation.messages()conversation.observeMessages()
  • conversations.list()conversations.observe()
  • Consent state, read receipts, metadata — anything backed by local DB

The sync() and stream() APIs can stay as-is for advanced use cases. This would be a higher-level API on top of them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions