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.
- Type-safe Result monad —
SupabaseResult<T>withmap,flatMap,recover— no exceptions leak to callers - Value class IDs —
UserId,BucketId,SessionId,ChannelIdprevent mixups at compile time - PostgREST filter DSL —
eq,neq,gt,like,ilike,in,is,textSearch,contains, and more - OAuth (17 providers) + MFA — TOTP and phone-based multi-factor auth with CSPRNG-backed PKCE
- Native passkeys — cross-platform WebAuthn ceremony (Android, iOS, macOS, JVM, Wasm) behind a pluggable authenticator, or bring your own
- Session management — Single-flight auto-refresh, pluggable persistence (
SessionStorage),SessionStateviaStateFlow - Realtime WebSocket — Phoenix protocol with auto-reconnection, exponential backoff, presence, offline send buffering, binary broadcast (raw
ByteArray) - Secure by default — Credential headers redacted from logs, smart retries (
429/5xx+Retry-After) - End-to-end encryption — Optional
supabase-e2ee: derive a shared AES-256-GCM key on-device (ECDH → HKDF) so Supabase only ever stores ciphertext - 16 platform targets — Android, iOS, macOS, tvOS, watchOS, JVM, Linux, Windows, and WasmJs
Requirements: the published Android/JVM artifacts are compiled for Java 17 and ship inline functions, so consuming modules must build with
jvmTarget = 17(AndroidcompileOptions/kotlinOptionsJVM 17, or JVMjvmToolchain(17)). Building at a lower target fails with "cannot inline bytecode built with JVM target 17 into bytecode being built with JVM target 11".
Add the modules you need to your version catalog:
[versions]
supabase-kmp = "0.9.1"
[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" }
# 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)
database.selectTyped<Todo>(table = "todos") {
eq("done", "false")
order("created_at", ascending = false)
limit(25)
}.onSuccess { todos ->
println("Got ${todos.size} todos")
}.onFailure { error ->
println("Error: ${error.message}")
}Errors carry a category (Conflict, NotFound, Unauthorized, RateLimited, 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) |
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.
- Docs site: https://androidpoet.github.io/supabase-kmp/ (source in
website/) - Release notes:
CHANGELOG.md - Contributing:
CONTRIBUTING.md - Security policy:
SECURITY.md - AI Agent Skill (Claude Code / Cursor / Copilot):
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.
MIT © 2026 Ranbir Singh
