PowerSync is a Postgres-SQLite sync layer, which helps developers to create local-first real-time reactive apps that work seamlessly both online and offline.
This SDK is currently in an alpha release. If you find a bug or issue, please open a GitHub issue. Questions or feedback can be posted on our community Discord - we'd love to hear from you.
- Provides real-time streaming of database changes, using Kotlin Coroutines and Flows.
- Offers direct access to the SQLite database, enabling the use of SQL on both client and server sides.
- Operations are asynchronous, ensuring the user interface remains unblocked.
- Supports concurrent database operations, allowing one write and multiple reads simultaneously.
- Enables subscription to queries for receiving live updates.
- Eliminates the need for client-side database migrations as these are managed automatically.
Supported KMP targets: Android and iOS.
-
- This is the Kotlin Multiplatform SDK implementation.
-
- SupabaseConnector.kt An example connector implementation tailed for Supabase. The backend
connector provides
the connection between your application backend and the PowerSync managed database. It is used to:
- Retrieve a token to connect to the PowerSync service.
- Apply local changes on your backend application server (and from there, to Postgres).
- SupabaseConnector.kt An example connector implementation tailed for Supabase. The backend
connector provides
the connection between your application backend and the PowerSync managed database. It is used to:
The easiest way to test the PowerSync KMP SDK is to run one of our demo applications.
Demo applications are located in the demos/
directory. See their respective README's for testing instructions:
-
demos/hello-powersync: A minimal example demonstrating the use of the PowerSync Kotlin Multiplatform SDK and the Supabase connector.
-
demos/supabase-todolist: ** Currently a work in progress ** A simple to-do list application demonstrating the use of the PowerSync Kotlin Multiplatform SDK and the Supabase connector.
The PowerSync Kotlin Multiplatform SDK is currently in an alpha release and is not yet suitable for production use.
Current limitations:
- Debugging via configurable logging is not yet implemented.
- Integration with SQLDelight schema and API generation (ORM) is not yet supported.
- Supports only a single database file.
Future work/ideas:
- Improved error handling.
- Attachments helper package.
- Management of DB connections on each platform natively.
- Supporting additional targets (JVM, Wasm).
Add the PowerSync Kotlin Multiplatform SDK to your project by adding the following to your build.gradle.kts
file:
kotlin {
//...
sourceSets {
commonMain.dependencies {
api("com.powersync:core:$powersyncVersion")
}
//...
}
}
If want to use the Supabase Connector, also add the following to commonMain.dependencies
:
implementation("com.powersync:connector-supabase:$powersyncVersion")
We recommend using Cocoapods (as opposed to SMP) for iOS targets. Add the following to the cocoapods
config in your build.gradle.kts
cocoapods {
//...
pod("powersync-sqlite-core") {
linkOnly = true
}
framework {
isStatic = true
export("com.powersync:core")
}
//...
}
Note: The linkOnly
attribute is set to true
and framework is set to isStatic = true
to ensure that the powersync-sqlite-core
binaries are only statically linked.
The first step is creating a PowerSync account and setting up a PowerSync instance. If you are using Supabase, we have a step-by-step tutorial available here.
For other Postgres backend providers, follow these steps:
- Sign up for a free PowerSync account here https://www.powersync.com/.
- Visit the PowerSync dashboard to create a PowerSync instance. After signing up you will be prompted to start the onboarding wizard which guides your though the steps required for this, and find database specific instructions here. Existing users: start the onboarding wizard by navigating to Help > Start guide in the top-right corner.
- Developer documentation for PowerSync is available here.
You need to set up your schema in your app project. This involves defining your schema in code using the PowerSync syntax. This schema represents a "view" of the downloaded data. No migrations are required — the schema is applied directly when the PowerSync database is constructed.
import com.powersync.db.schema.Column
import com.powersync.db.schema.Schema
import com.powersync.db.schema.Table
val schema: Schema = Schema(
Table(
name = "customers",
columns = listOf(
Column.text("name"),
Column.text("email")
)
)
)
Note: No need to declare a primary key id
column, as PowerSync will automatically create this.
The PowerSync backend connector provides the connection between your application backend and the PowerSync managed database. It is used to:
- Retrieve a token to connect to the PowerSync instance.
- Apply local changes on your backend application server (and from there, to Postgres)
If you are using Supabase, you can use SupabaseConnector.kt as a starting point.
class MyConnector : PowerSyncBackendConnector() {
override suspend fun fetchCredentials(): PowerSyncCredentials {
// implement fetchCredentials to obtain the necessary credentials to connect to your backend
// See an example implementation in connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt
}
override suspend fun uploadData(database: PowerSyncDatabase) {
// Implement uploadData to send local changes to your backend service
// You can omit this method if you only want to sync data from the server to the client
// See an example implementation in connectors/supabase/src/commonMain/kotlin/com/powersync/connector/supabase/SupabaseConnector.kt
// See https://docs.powersync.com/usage/installation/app-backend-setup/writing-client-changes for considerations.
}
}
You need to instantiate the PowerSync database — this is the core managed database. Its primary functions are to record all changes in the local database, whether online or offline. In addition, it automatically uploads changes to your app backend when connected.
a. Create platform specific DatabaseDriverFactory
to be used by the PowerSyncBuilder
to create the SQLite database driver.
// Android
val driverFactory = DatabaseDriverFactory(this)
// iOS
val driverFactory = DatabaseDriverFactory()
b. Build a PowerSyncDatabase
instance using the PowerSyncBuilder
and the DatabaseDriverFactory
. The schema you created in a previous step is also used as a parameter:
// commonMain
val database = PowerSyncBuilder.from(driverFactory, schema).build()
c. Connect the PowerSyncDatabase
to the backend connector:
// commonMain
database.connect(MyConnector())
Special case: Compose Multiplatform
The artifact com.powersync:powersync-compose
provides a simpler API:
// commonMain
val database = rememberPowerSyncDatabase(schema)
remember {
database.connect(MyConnector())
}
// You can watch any SQL query. This excutes a read query every time the source tables are modified.
fun watchCustomers(): Flow<List<User>> {
// TODO: implement your UI based on the result set
return database.watch("SELECT * FROM customers", mapper = { cursor ->
User(
id = cursor.getString(0)!!,
name = cursor.getString(1)!!,
email = cursor.getString(2)!!
)
})
}
The execute
method executes a write query (INSERT, UPDATE, DELETE) and returns the results (if any).
suspend fun insertCustomer(name: String, email: String) {
database.writeTransaction {
database.execute(
sql = "INSERT INTO customers (id, name, email) VALUES (uuid(), ?, ?)",
parameters = listOf(name, email)
)
}
}
suspend fun updateCustomer(id: String, name: String, email: String) {
database.execute(
sql = "UPDATE customers SET name = ? WHERE email = ?",
parameters = listOf(name, email)
)
}
suspend fun deleteCustomer(id: String? = null) {
// If no id is provided, delete the first customer in the database
val targetId =
id ?: database.getOptional(
sql = "SELECT id FROM customers LIMIT 1",
mapper = { cursor ->
cursor.getString(0)!!
}
) ?: return
database.writeTransaction {
database.execute(
sql = "DELETE FROM customers WHERE id = ?",
parameters = listOf(targetId)
)
}
}