Skip to main content

Installation

Gradle (Kotlin DSL)

Add the Supabase Kotlin dependency to your build.gradle.kts:
dependencies {
    implementation("io.github.jan-tennert.supabase:postgrest-kt:2.0.0")
    implementation("io.github.jan-tennert.supabase:realtime-kt:2.0.0")
    implementation("io.github.jan-tennert.supabase:storage-kt:2.0.0")
    implementation("io.github.jan-tennert.supabase:gotrue-kt:2.0.0")
    implementation("io.github.jan-tennert.supabase:functions-kt:2.0.0")
}

Gradle (Groovy)

dependencies {
    implementation 'io.github.jan-tennert.supabase:postgrest-kt:2.0.0'
    implementation 'io.github.jan-tennert.supabase:realtime-kt:2.0.0'
    implementation 'io.github.jan-tennert.supabase:storage-kt:2.0.0'
    implementation 'io.github.jan-tennert.supabase:gotrue-kt:2.0.0'
    implementation 'io.github.jan-tennert.supabase:functions-kt:2.0.0'
}

Initializing

Create a Supabase client to interact with your database.
import io.github.jan.supabase.createSupabaseClient
import io.github.jan.supabase.postgrest.Postgrest
import io.github.jan.supabase.gotrue.Auth
import io.github.jan.supabase.storage.Storage
import io.github.jan.supabase.realtime.Realtime
import io.github.jan.supabase.functions.Functions

val supabase = createSupabaseClient(
    supabaseUrl = "YOUR_SUPABASE_URL",
    supabaseKey = "YOUR_SUPABASE_ANON_KEY"
) {
    install(Postgrest)
    install(Auth)
    install(Storage)
    install(Realtime)
    install(Functions)
}
supabaseUrl
String
required
The unique Supabase URL for your project.
supabaseKey
String
required
The Supabase anon key for your project.
configuration
SupabaseClientBuilder.() -> Unit
Configuration block for installing plugins.

Database Operations

Select Data

Query data from your tables.
import io.github.jan.supabase.postgrest.from
import kotlinx.serialization.Serializable

@Serializable
data class Country(
    val id: Int,
    val name: String,
    val code: String,
    val capital: String?
)

val countries = supabase.from("countries")
    .select()
    .decodeList<Country>()
result
List<T>
The returned rows from the query, deserialized to your data class.

Select with Filters

val country = supabase.from("countries")
    .select {
        filter {
            eq("id", 1)
        }
    }
    .decodeSingle<Country>()

Select Specific Columns

val countries = supabase.from("countries")
    .select(columns = Columns.list("name", "capital"))
    .decodeList<Country>()

Common Filters

// Equal to
val data = supabase.from("countries")
    .select {
        filter {
            eq("name", "Albania")
        }
    }
    .decodeList<Country>()

// Not equal to
val data = supabase.from("countries")
    .select {
        filter {
            neq("name", "Albania")
        }
    }
    .decodeList<Country>()

// Greater than
val data = supabase.from("countries")
    .select {
        filter {
            gt("population", 1000000)
        }
    }
    .decodeList<Country>()

// Less than
val data = supabase.from("countries")
    .select {
        filter {
            lt("population", 1000000)
        }
    }
    .decodeList<Country>()

// Like (pattern matching)
val data = supabase.from("countries")
    .select {
        filter {
            ilike("name", "%Alba%")
        }
    }
    .decodeList<Country>()

// In list
val data = supabase.from("countries")
    .select {
        filter {
            isIn("name", listOf("Albania", "Algeria"))
        }
    }
    .decodeList<Country>()

Insert Data

Insert rows into your tables.
@Serializable
data class NewCountry(
    val name: String,
    val code: String
)

val newCountry = NewCountry(name = "Denmark", code = "DK")

val result = supabase.from("countries")
    .insert(newCountry) {
        select()
    }
    .decodeSingle<Country>()
value
T
required
The value to insert.

Insert Multiple Rows

val countries = listOf(
    NewCountry(name = "Denmark", code = "DK"),
    NewCountry(name = "Norway", code = "NO")
)

val result = supabase.from("countries")
    .insert(countries) {
        select()
    }
    .decodeList<Country>()

Update Data

Update existing rows in your tables.
@Serializable
data class CountryUpdate(
    val name: String
)

val update = CountryUpdate(name = "Australia")

val result = supabase.from("countries")
    .update(update) {
        filter {
            eq("id", 1)
        }
        select()
    }
    .decodeList<Country>()
value
T
required
The values to update.

Upsert Data

Insert or update rows based on unique constraints.
val country = Country(
    id = 1,
    name = "Australia",
    code = "AU",
    capital = "Canberra"
)

val result = supabase.from("countries")
    .upsert(country) {
        select()
    }
    .decodeSingle<Country>()

Delete Data

Delete rows from your tables.
supabase.from("countries")
    .delete {
        filter {
            eq("id", 1)
        }
    }

Authentication

Sign Up

Create a new user account.
import io.github.jan.supabase.gotrue.auth
import io.github.jan.supabase.gotrue.providers.builtin.Email

val result = supabase.auth.signUpWith(Email) {
    email = "[email protected]"
    password = "example-password"
}
email
String
required
The user’s email address.
password
String
required
The user’s password.
data
JsonObject
Additional user metadata.
redirectUrl
String
A URL to redirect to after signup.
result
AuthResponse

Sign In

Sign in an existing user.
val result = supabase.auth.signInWith(Email) {
    email = "[email protected]"
    password = "example-password"
}

Sign Out

Sign out the current user.
supabase.auth.signOut()

Get Session

Get the current session.
val session = supabase.auth.currentSessionOrNull()
session
SessionInfo?
The current session object or null if no active session.

Get User

Get the current user.
val user = supabase.auth.currentUserOrNull()
user
UserInfo?
The current user object or null if not authenticated.

Auth State Changes

Listen to authentication state changes.
import io.github.jan.supabase.gotrue.SessionStatus

supabase.auth.sessionStatus.collect { status ->
    when (status) {
        is SessionStatus.Authenticated -> {
            println("User signed in: ${status.session.user?.email}")
        }
        is SessionStatus.NotAuthenticated -> {
            println("User signed out")
        }
        else -> {}
    }
}

Storage

Upload File

Upload a file to a storage bucket.
import io.github.jan.supabase.storage.storage
import io.github.jan.supabase.storage.upload

val fileData = File("path/to/file").readBytes()

val result = supabase.storage
    .from("avatars")
    .upload("public/avatar1.png", fileData) {
        contentType = "image/png"
    }
path
String
required
The file path including the file name.
data
ByteArray
required
The file data to upload.
upsert
Boolean
default:"false"
When true, overwrites existing file with the same path.
contentType
String
MIME type of the file.

Download File

Download a file from storage.
import io.github.jan.supabase.storage.download

val data = supabase.storage
    .from("avatars")
    .download("public/avatar1.png")
path
String
required
The file path to download.
data
ByteArray
The file data.

List Files

List all files in a bucket.
val files = supabase.storage
    .from("avatars")
    .list("public")

Delete Files

Delete files from storage.
supabase.storage
    .from("avatars")
    .delete(listOf("public/avatar1.png", "public/avatar2.png"))

Get Public URL

Get the public URL for a file.
import io.github.jan.supabase.storage.publicUrl

val url = supabase.storage
    .from("avatars")
    .publicUrl("public/avatar1.png")

println(url)

Realtime

Subscribe to Changes

Listen to database changes in realtime.
import io.github.jan.supabase.realtime.channel
import io.github.jan.supabase.realtime.postgresChangeFlow
import io.github.jan.supabase.realtime.PostgresAction

val channel = supabase.channel("db-changes")

val changeFlow = channel.postgresChangeFlow<PostgresAction>(schema = "public") {
    table = "countries"
}

changeFlow.collect { action ->
    when (action) {
        is PostgresAction.Insert -> println("New country: ${action.record}")
        is PostgresAction.Update -> println("Updated country: ${action.record}")
        is PostgresAction.Delete -> println("Deleted country")
    }
}

channel.subscribe()
schema
String
required
The database schema to listen to.
table
String
required
The table to listen to.

Unsubscribe

Stop listening to changes.
supabase.removeChannel(channel)

Edge Functions

Invoke Function

Invoke a Supabase Edge Function.
import io.github.jan.supabase.functions.functions
import io.github.jan.supabase.functions.invoke
import kotlinx.serialization.Serializable

@Serializable
data class FunctionPayload(val name: String)

@Serializable
data class FunctionResponse(val message: String)

val response = supabase.functions.invoke<FunctionResponse>(
    function = "hello-world",
    body = FunctionPayload(name = "Functions")
)
function
String
required
The name of the Edge Function to invoke.
body
T
The request body to send to the function.
headers
Map<String, String>
Custom headers to send with the request.
method
HttpMethod
default:"HttpMethod.Post"
HTTP method to use.
response
T
The response data from the function, deserialized to your data class.

Error Handling

Handle errors using try-catch blocks:
try {
    val countries = supabase.from("countries")
        .select()
        .decodeList<Country>()
    println("Countries: $countries")
} catch (e: RestException) {
    println("Database error: ${e.message}")
} catch (e: Exception) {
    println("Unexpected error: ${e.message}")
}

Compose Multiplatform Integration

Use Supabase with Compose Multiplatform:
import androidx.compose.runtime.*
import kotlinx.coroutines.launch

@Composable
fun CountriesScreen() {
    var countries by remember { mutableStateOf<List<Country>>(emptyList()) }
    val scope = rememberCoroutineScope()
    
    LaunchedEffect(Unit) {
        scope.launch {
            try {
                countries = supabase.from("countries")
                    .select()
                    .decodeList<Country>()
            } catch (e: Exception) {
                println("Error loading countries: ${e.message}")
            }
        }
    }
    
    LazyColumn {
        items(countries) { country ->
            Text(country.name)
        }
    }
}

Additional Resources

GitHub Repository

View the source code and contribute

Maven Central

View package details and versions

Build docs developers (and LLMs) love