PowerSync is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres, MongoDB or MySQL on the server-side.
PowerSync Rust
[!CAUTION] The PowerSync Rust SDK is currently experimental and in a pre-alpha state. We offer no stability guarantees for this SDK, and can't guarantee continued support for it. If you're interested in using this, please reach out!
Setup
PowerSync is implemented over local SQLite databases. The main entrypoint for this library, PowerSyncDatabase,
requires three external dependencies to be provided:
- An HTTP client implementation from the
http-clientcrate.- The
async-h1implementation is known not to work with PowerSync, we recommend theIsahcClientinstead.
- The
- A
ConnectionPoolof SQLite connections.- Create one with
ConnectionPool::open(path). - For in-memory databases, use
ConnectionPool::single_connection().
- Create one with
- A timer implementation, used to delay reconnects when a sync connection gets interrupted.
These three external dependencies are bundled into the PowerSyncEnvironment class. At the moment, all three of them
need to provided manually via PowerSyncEnvironment::custom. We may offer a default configuration in the future.
After obtaining a PowerSyncEnvironment, construct a Schema instance describing the local schema of your database.
The PowerSync SDK will create and auto-migrate schemas.
Finally, create a database with PowerSyncDatabase::new.
Async runtimes
This crate provides asynchronous APIs to:
- Manage concurrent access to SQLite connections.
- Request tokens to authenticate against the PowerSync service.
- Stream changes from the PowerSync service to your local database.
- Upload local writes to your backend.
For maximum flexibility, the powersync crate is executor-agnostic and can run on async runtime. All tasks that run
concurrently to the main application need to be spawned before starting PowerSync:
async
The crate generally operates on a bring-your-own-runtime assumption, although optional features are available for popular runtimes. The above snippet can be simplified to:
async
Running queries
There is no dedicated query API as part of this SDK. Instead, the SDK gives out scoped access to sqlite3* connection
pointers (or the rusqlite::Connection struct for safe Rust).
async
Because contents of the database can change at any time (for instance, due to new data being synced), it's convenient
to get notifications about updates on tables.
PowerSync exposes the watch_tables method returning a futures_core::stream::Stream emitting an event whenever a
watched table changes. This is useful as a building block for auto-updating queries.
Note that unlike the other PowerSync SDKs, watch_tables is never throttled! You'd have to implement that logic as a
stream transformer.
Connecting
To automatically keep the local SQLite database in-sync with a backend process, call PowerSyncDatabase::connect.
This requires passing your own BackendConnector implementation, see the examples for a possible implementation.
Sync status
PowerSync exposes the current sync status through the PowerSyncDatabase::status() method. This status instance can be
used to query for current sync progress or errors.
Similarly, PowerSyncDatabase::watch_status returns a stream of status data that re-emits whenver it changes.
Sync streams
The native SDK is built under the assumption that data to sync is specified in sync streams.
To subscribe to a stream, use PowerSyncDatabase::sync_stream(db, name, params).subscribe(). The stream will be synced
as long as the returned subscription handle is active (and for a configured TTL afterwards).
Limitations
This SDK is in development. Some items that are still being worked on are:
- Token prefetching and caching.
- Unit tests for CRUD uploads.
Also, this crate's build.rs dynamically downloads a binary
(the PowerSync core extension) to link into the final
executable. The reason is that, while this library works with stable Rust, the core extension requires a nightly build.
We'll work towards making the core extension a regular Rust crate supporting stable compilers in the future.