
Type-safe, coroutine-first, modular client for Supabase with Result monad, value-class IDs, PostgREST filter DSL, OAuth+MFA, session auto-refresh, realtime, storage, and edge functions.
Kotlin Multiplatform SDK for Supabase — type-safe, coroutine-first, modular client for every platform Kotlin runs on.
Full guides for Authentication, Database, Storage, Realtime and Edge Functions live on the docs site. This README is the quick start.
SupabaseResult<T> with map, flatMap, recover — no exceptions leak to callersUserId, BucketId, SessionId, ChannelId prevent mixups at compile timeColumn<T> tokens with infix operators (eq, neq, greater, like, ilike, inList, isNull, textSearch, contains, …); the compiler rejects type mismatchesSessionStorage), SessionState via StateFlow
ByteArray)429/5xx + Retry-After)supabase-e2ee: derive a shared AES-256-GCM key on-device (ECDH → HKDF) so Supabase only ever stores ciphertext. Includes a KeyDirectory (publish/fetch public keys), out-of-band safetyNumber() verification (MITM protection), and EncryptedRoom — a verify-first encrypt-on-send / decrypt-on-receive chat room over supabase-realtime
Requirements: the published Android/JVM artifacts are compiled for Java 17 and ship inline functions, so consuming modules must build with a
jvmTargetof 17 or higher (AndroidcompileOptions/kotlinOptionsJVM 17+, or JVMjvmToolchain(17)or newer — Java 21 is fine). Only a lower target fails, with "cannot inline bytecode built with JVM target 17 into bytecode being built with JVM target 11"; matching or higher targets inline cleanly.
Add the modules you need to your version catalog:
[versions]
supabase-kmp = "1.0.0"
[libraries]
supabase-client = { module = "io.github.androidpoet:supabase-client", version.ref = "supabase-kmp" }
supabase-auth = { module = "io.github.androidpoet:supabase-auth", version.ref = "supabase-kmp" }
supabase-database = { module = "io.github.androidpoet:supabase-database", version.ref = "supabase-kmp" }
supabase-storage = { module = "io.github.androidpoet:supabase-storage", version.ref = "supabase-kmp" }
supabase-realtime = { module = "io.github.androidpoet:supabase-realtime", version.ref = "supabase-kmp" }
supabase-functions = { module = "io.github.androidpoet:supabase-functions", version.ref = "supabase-kmp" }
supabase-sync = { module = "io.github.androidpoet:supabase-sync", version.ref = "supabase-kmp" } # + supabase-sync-core, supabase-sync-sqldelight for offline-first sync
# Optional add-ons — only add the ones you need
supabase-auth-google = { module = "io.github.androidpoet:supabase-auth-google", version.ref = "supabase-kmp" }
supabase-auth-apple = { module = "io.github.androidpoet:supabase-auth-apple", version.ref = "supabase-kmp" }
supabase-auth-passkey = { module = "io.github.androidpoet:supabase-auth-passkey", version.ref = "supabase-kmp" }
supabase-auth-admin = { module = "io.github.androidpoet:supabase-auth-admin", version.ref = "supabase-kmp" }
supabase-e2ee = { module = "io.github.androidpoet:supabase-e2ee", version.ref = "supabase-kmp" }kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.supabase.client)
implementation(libs.supabase.auth)
implementation(libs.supabase.database)
implementation(libs.supabase.storage)
implementation(libs.supabase.realtime)
implementation(libs.supabase.functions)
}
}
}Only depend on what you use — each module is published independently. Optional add-ons:
supabase-auth-google,supabase-auth-apple,supabase-auth-passkey(native sign-in),supabase-auth-admin(service-role; server-side only) andsupabase-e2ee(client-side end-to-end encryption).
Everything starts from a SupabaseClient. Create one, then build the feature clients you need from it.
val client = Supabase.create(
projectUrl = "https://your-project.supabase.co",
apiKey = "your-anon-key",
) {
logging = true
}
val auth = createAuthClient(client)
val database = createDatabaseClient(client)
val storage = createStorageClient(client)
val realtime = createRealtimeClient(client)
val functions = createFunctionsClient(client)Use the anon key in client apps — never the service-role key.
Every fallible call returns a SupabaseResult<T> — a sealed Success/Failure type. There are no exceptions to catch on the happy path; you branch with onSuccess/onFailure and transform with map/flatMap/recover.
@Serializable
data class Todo(val id: String, val title: String, val done: Boolean)
// Typed column tokens — hand-written here, or emitted by codegen.
object Todos {
val done = Column<Boolean>("done")
val createdAt = Column<String>("created_at")
}
database.selectTyped<Todo>(table = "todos") {
where { Todos.done eq false }
orderBy(Todos.createdAt, Order.DESC)
limit(25)
}.onSuccess { todos ->
println("Got ${todos.size} todos")
}.onFailure { error ->
println("Error: ${error.message}")
}Errors carry a category (CONFLICT, NOT_FOUND, UNAUTHORIZED, RATE_LIMITED, VALIDATION, Internal, NETWORK, UNKNOWN) so you can branch without parsing codes — plus chainable helpers like onUnauthorized { }, onRateLimited { } and onNetworkError { }. See Results & Errors for the full surface.
| Module | Artifact | Description |
|---|---|---|
| supabase-core | io.github.androidpoet:supabase-core |
Result monad, error types, value class IDs, filter DSL |
| supabase-client | io.github.androidpoet:supabase-client |
HTTP transport, platform engines, auth state, factory wiring |
| supabase-auth | io.github.androidpoet:supabase-auth |
Email, phone, OTP, OAuth (17 providers), MFA, PKCE, session management, passkeys |
| supabase-auth-google | io.github.androidpoet:supabase-auth-google |
Native Google sign-in (Android Credential Manager) |
| supabase-auth-apple | io.github.androidpoet:supabase-auth-apple |
Native Sign in with Apple (AuthenticationServices) |
| supabase-auth-passkey | io.github.androidpoet:supabase-auth-passkey |
Passkey/WebAuthn ceremony driver — native on Android, iOS, macOS, JVM & Wasm (or bring your own) |
| supabase-auth-admin | io.github.androidpoet:supabase-auth-admin |
Service-role admin APIs (server-side only) |
| supabase-database | io.github.androidpoet:supabase-database |
PostgREST CRUD, RPC, typed filter extensions |
| supabase-storage | io.github.androidpoet:supabase-storage |
Bucket CRUD, file upload/download, signed & public URLs |
| supabase-realtime | io.github.androidpoet:supabase-realtime |
WebSocket (Phoenix protocol), auto-reconnect, broadcast, presence |
| supabase-functions | io.github.androidpoet:supabase-functions |
Edge function invocation with typed responses |
| supabase-e2ee | io.github.androidpoet:supabase-e2ee |
Optional client-side E2E encryption (ECDH → HKDF → AES-256-GCM) + key directory, safety-number verification & EncryptedRoom over realtime |
| supabase-sync-core | io.github.androidpoet:supabase-sync-core |
Offline-first sync engine — pull/merge/push, conflict resolution, keyset cursor (transport-agnostic) |
| supabase-sync-sqldelight | io.github.androidpoet:supabase-sync-sqldelight |
On-device local store over SQLDelight — typed-table SSOT, outbox, cursor, pagination |
| supabase-sync | io.github.androidpoet:supabase-sync |
Supabase remote source — incremental pull/push over PostgREST + live Realtime deltas |
Android · JVM · iOS · macOS · tvOS · watchOS · Linux · Windows · WasmJs — 16 targets in total, on OkHttp (Android/JVM), Darwin (Apple), CIO (Linux/Windows) and the JS Ktor engine for the browser (wasmJs). The browser target ships only as Kotlin/Wasm via wasmJs — there is no separate Kotlin/JS (js()) artifact.
website/)CHANGELOG.md
CONTRIBUTING.md
SECURITY.md
skills/
./gradlew compileKotlinJvm # compile a single target
./gradlew jvmTest # run JVM unit tests
./gradlew build --no-configuration-cache # full build (all platforms)Quality gates run in CI on every PR — run them locally before pushing:
./gradlew detekt # static analysis (config/detekt/detekt.yml)
./gradlew apiCheck # fails on unintended public/binary API changes
./gradlew jvmTest koverHtmlReport # tests + coverage reportIf you intentionally change the public API, regenerate the dumps with ./gradlew apiDump and commit them. See CONTRIBUTING.md for the full workflow.
Contributions are welcome! If you've found a bug, have an idea for an improvement, or want to contribute new features, please open an issue or submit a pull request.
Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🤩
MIT © 2026 Ranbir Singh
Kotlin Multiplatform SDK for Supabase — type-safe, coroutine-first, modular client for every platform Kotlin runs on.
Full guides for Authentication, Database, Storage, Realtime and Edge Functions live on the docs site. This README is the quick start.
SupabaseResult<T> with map, flatMap, recover — no exceptions leak to callersUserId, BucketId, SessionId, ChannelId prevent mixups at compile timeColumn<T> tokens with infix operators (eq, neq, greater, like, ilike, inList, isNull, textSearch, contains, …); the compiler rejects type mismatchesSessionStorage), SessionState via StateFlow
ByteArray)429/5xx + Retry-After)supabase-e2ee: derive a shared AES-256-GCM key on-device (ECDH → HKDF) so Supabase only ever stores ciphertext. Includes a KeyDirectory (publish/fetch public keys), out-of-band safetyNumber() verification (MITM protection), and EncryptedRoom — a verify-first encrypt-on-send / decrypt-on-receive chat room over supabase-realtime
Requirements: the published Android/JVM artifacts are compiled for Java 17 and ship inline functions, so consuming modules must build with a
jvmTargetof 17 or higher (AndroidcompileOptions/kotlinOptionsJVM 17+, or JVMjvmToolchain(17)or newer — Java 21 is fine). Only a lower target fails, with "cannot inline bytecode built with JVM target 17 into bytecode being built with JVM target 11"; matching or higher targets inline cleanly.
Add the modules you need to your version catalog:
[versions]
supabase-kmp = "1.0.0"
[libraries]
supabase-client = { module = "io.github.androidpoet:supabase-client", version.ref = "supabase-kmp" }
supabase-auth = { module = "io.github.androidpoet:supabase-auth", version.ref = "supabase-kmp" }
supabase-database = { module = "io.github.androidpoet:supabase-database", version.ref = "supabase-kmp" }
supabase-storage = { module = "io.github.androidpoet:supabase-storage", version.ref = "supabase-kmp" }
supabase-realtime = { module = "io.github.androidpoet:supabase-realtime", version.ref = "supabase-kmp" }
supabase-functions = { module = "io.github.androidpoet:supabase-functions", version.ref = "supabase-kmp" }
supabase-sync = { module = "io.github.androidpoet:supabase-sync", version.ref = "supabase-kmp" } # + supabase-sync-core, supabase-sync-sqldelight for offline-first sync
# Optional add-ons — only add the ones you need
supabase-auth-google = { module = "io.github.androidpoet:supabase-auth-google", version.ref = "supabase-kmp" }
supabase-auth-apple = { module = "io.github.androidpoet:supabase-auth-apple", version.ref = "supabase-kmp" }
supabase-auth-passkey = { module = "io.github.androidpoet:supabase-auth-passkey", version.ref = "supabase-kmp" }
supabase-auth-admin = { module = "io.github.androidpoet:supabase-auth-admin", version.ref = "supabase-kmp" }
supabase-e2ee = { module = "io.github.androidpoet:supabase-e2ee", version.ref = "supabase-kmp" }kotlin {
sourceSets {
commonMain.dependencies {
implementation(libs.supabase.client)
implementation(libs.supabase.auth)
implementation(libs.supabase.database)
implementation(libs.supabase.storage)
implementation(libs.supabase.realtime)
implementation(libs.supabase.functions)
}
}
}Only depend on what you use — each module is published independently. Optional add-ons:
supabase-auth-google,supabase-auth-apple,supabase-auth-passkey(native sign-in),supabase-auth-admin(service-role; server-side only) andsupabase-e2ee(client-side end-to-end encryption).
Everything starts from a SupabaseClient. Create one, then build the feature clients you need from it.
val client = Supabase.create(
projectUrl = "https://your-project.supabase.co",
apiKey = "your-anon-key",
) {
logging = true
}
val auth = createAuthClient(client)
val database = createDatabaseClient(client)
val storage = createStorageClient(client)
val realtime = createRealtimeClient(client)
val functions = createFunctionsClient(client)Use the anon key in client apps — never the service-role key.
Every fallible call returns a SupabaseResult<T> — a sealed Success/Failure type. There are no exceptions to catch on the happy path; you branch with onSuccess/onFailure and transform with map/flatMap/recover.
@Serializable
data class Todo(val id: String, val title: String, val done: Boolean)
// Typed column tokens — hand-written here, or emitted by codegen.
object Todos {
val done = Column<Boolean>("done")
val createdAt = Column<String>("created_at")
}
database.selectTyped<Todo>(table = "todos") {
where { Todos.done eq false }
orderBy(Todos.createdAt, Order.DESC)
limit(25)
}.onSuccess { todos ->
println("Got ${todos.size} todos")
}.onFailure { error ->
println("Error: ${error.message}")
}Errors carry a category (CONFLICT, NOT_FOUND, UNAUTHORIZED, RATE_LIMITED, VALIDATION, Internal, NETWORK, UNKNOWN) so you can branch without parsing codes — plus chainable helpers like onUnauthorized { }, onRateLimited { } and onNetworkError { }. See Results & Errors for the full surface.
| Module | Artifact | Description |
|---|---|---|
| supabase-core | io.github.androidpoet:supabase-core |
Result monad, error types, value class IDs, filter DSL |
| supabase-client | io.github.androidpoet:supabase-client |
HTTP transport, platform engines, auth state, factory wiring |
| supabase-auth | io.github.androidpoet:supabase-auth |
Email, phone, OTP, OAuth (17 providers), MFA, PKCE, session management, passkeys |
| supabase-auth-google | io.github.androidpoet:supabase-auth-google |
Native Google sign-in (Android Credential Manager) |
| supabase-auth-apple | io.github.androidpoet:supabase-auth-apple |
Native Sign in with Apple (AuthenticationServices) |
| supabase-auth-passkey | io.github.androidpoet:supabase-auth-passkey |
Passkey/WebAuthn ceremony driver — native on Android, iOS, macOS, JVM & Wasm (or bring your own) |
| supabase-auth-admin | io.github.androidpoet:supabase-auth-admin |
Service-role admin APIs (server-side only) |
| supabase-database | io.github.androidpoet:supabase-database |
PostgREST CRUD, RPC, typed filter extensions |
| supabase-storage | io.github.androidpoet:supabase-storage |
Bucket CRUD, file upload/download, signed & public URLs |
| supabase-realtime | io.github.androidpoet:supabase-realtime |
WebSocket (Phoenix protocol), auto-reconnect, broadcast, presence |
| supabase-functions | io.github.androidpoet:supabase-functions |
Edge function invocation with typed responses |
| supabase-e2ee | io.github.androidpoet:supabase-e2ee |
Optional client-side E2E encryption (ECDH → HKDF → AES-256-GCM) + key directory, safety-number verification & EncryptedRoom over realtime |
| supabase-sync-core | io.github.androidpoet:supabase-sync-core |
Offline-first sync engine — pull/merge/push, conflict resolution, keyset cursor (transport-agnostic) |
| supabase-sync-sqldelight | io.github.androidpoet:supabase-sync-sqldelight |
On-device local store over SQLDelight — typed-table SSOT, outbox, cursor, pagination |
| supabase-sync | io.github.androidpoet:supabase-sync |
Supabase remote source — incremental pull/push over PostgREST + live Realtime deltas |
Android · JVM · iOS · macOS · tvOS · watchOS · Linux · Windows · WasmJs — 16 targets in total, on OkHttp (Android/JVM), Darwin (Apple), CIO (Linux/Windows) and the JS Ktor engine for the browser (wasmJs). The browser target ships only as Kotlin/Wasm via wasmJs — there is no separate Kotlin/JS (js()) artifact.
website/)CHANGELOG.md
CONTRIBUTING.md
SECURITY.md
skills/
./gradlew compileKotlinJvm # compile a single target
./gradlew jvmTest # run JVM unit tests
./gradlew build --no-configuration-cache # full build (all platforms)Quality gates run in CI on every PR — run them locally before pushing:
./gradlew detekt # static analysis (config/detekt/detekt.yml)
./gradlew apiCheck # fails on unintended public/binary API changes
./gradlew jvmTest koverHtmlReport # tests + coverage reportIf you intentionally change the public API, regenerate the dumps with ./gradlew apiDump and commit them. See CONTRIBUTING.md for the full workflow.
Contributions are welcome! If you've found a bug, have an idea for an improvement, or want to contribute new features, please open an issue or submit a pull request.
Support it by joining stargazers for this repository. ⭐
Also, follow me on GitHub for my next creations! 🤩
MIT © 2026 Ranbir Singh