Kotlin SDK

Kotlin Quick Start

WowSQL Kotlin SDK — Get started in minutes

Get your API keys: In your WowSQL project dashboard, go to Settings → API to find your anon key (wowsql_anon_...) and service role key (wowsql_service_...). The anon key is safe for client-side use (respects Row Level Security). The service role key bypasses RLS and should only be used server-side.

Installation

Official package: View on Maven Central →

// Gradle (Kotlin DSL)
implementation("com.WowSQL:WowSQL-kotlin:1.0.0")

Initialize Client

The SDK talks to PostgREST at /rest/v1/ under the hood. Your project URL resolves to https://myproject.wowsqlconnect.com.

import com.WowSQL.WowSQLClient

// Initialize client — URL resolves to https://myproject.wowsqlconnect.com
// SDK communicates with PostgREST at /rest/v1/
val client = WowSQLClient(
    projectUrl = "myproject",
    apiKey = "wowsql_anon_your_key_here"
)

// Or with full URL
val clientFull = WowSQLClient(
    projectUrl = "https://myproject.wowsqlconnect.com",
    apiKey = "wowsql_anon_your_key_here"
)

// Always use select() before get()/execute()
val users = client.table<Map<String, Any?>>("users").select("*").get()

Query Data

Basic Queries

// Get all records
val users = client.table("users").select("*").get()

// With filters
val adults = client.table("users").select("*").gte("age", 18).get()

// Select specific columns
val names = client.table("users").select("id", "name").get()

Advanced Queries

// Multiple filters with sorting
val activeUsers = client.table("users")
    .select("*")
    .eq("status", "active")
    .gt("age", 18)
    .order("created_at", "desc")
    .limit(20)
    .offset(0)
    .get()

println("Found ${activeUsers.count} users")
activeUsers.data.forEach { println(it["name"]) }

Filter Helper Methods

// Using helper methods: eq, gt, like, in, between
val users = client.table("users")
    .eq("status", "active")
    .gt("age", 18)
    .like("email", "%@gmail.com")
    .in("role", listOf("admin", "moderator"))
    .between("created_at", "2024-01-01", "2024-12-31")
    .get()

GROUP BY and Aggregates

Basic GROUP BY

// Group by category with counts
val result = client.table("products")
    .select("category", "COUNT(*) as count", "AVG(price) as avg_price")
    .groupBy("category")
    .get()

// Group by date
val result2 = client.table("orders")
    .select("DATE(created_at) as date", "COUNT(*) as orders", "SUM(total) as revenue")
    .groupBy("DATE(created_at)")
    .orderBy("date", "desc")
    .get()

HAVING Clause

val result = client.table("products")
    .select("category", "COUNT(*) as count")
    .groupBy("category")
    .having("COUNT(*)", "gt", 10)
    .get()

CRUD Operations

Create Record

val result = client.table("users").create(mapOf(
    "name" to "John Doe",
    "email" to "john@example.com",
    "age" to 30
))
println("Created user with ID: ${result["id"]}")

Update Record

client.table("users").update(123, mapOf("email" to "newemail@example.com"))
println("User updated successfully")

Delete Record

client.table("users").delete(123)
println("User deleted successfully")

Get Single Record by ID

val user = client.table("users").getById(123)
println("User: ${user?.get("name")}")

Get First Record

val user = client.table("users").eq("status", "active").first()
user?.let { println("First active user: ${it["name"]}") }

Filter Operators

Available operators for filtering data:

OperatorDescriptionExample (Kotlin)
eqEquals.eq("status", "active")
neqNot equals.neq("status", "deleted")
gtGreater than.gt("age", 18)
gteGreater than or equal.gte("age", 18)
ltLess than.lt("price", 100)
lteLess than or equal.lte("price", 100)
likePattern matching.like("name", "john")
inIN operator.in("category", listOf("electronics", "books"))
not_inNOT IN.notIn("status", listOf("deleted", "archived"))
betweenBETWEEN.between("price", 10, 100)

Error Handling

try {
    val users = client.table("users").select("*").get()
    println("Success: ${users.data.size} users")
} catch (e: WowSQLException) {
    when (e.statusCode) {
        401 -> println("Authentication failed — check your API key")
        404 -> println("Table or resource not found")
        else -> println("Error (${e.statusCode}): ${e.message}")
    }
}

Authentication

Auth endpoints are served at /auth/v1/. Use the anon key for client-side auth operations.

Sign Up & Sign In

val auth = ProjectAuthClient(projectUrl = "myproject", apiKey = "wowsql_anon_your_key_here")

// Sign up — POST /auth/v1/signup
val result = auth.signUp("user@example.com", "SecurePassword123", "John Doe", null)
println("Access token: ${result.session.accessToken}")

// Sign in — POST /auth/v1/login
val login = auth.signIn("user@example.com", "SecurePassword123")
println("User ID: ${login.user?.id}")

Password Reset

auth.forgotPassword("user@example.com")
auth.resetPassword("reset_token_from_email", "NewSecurePassword123")

OTP Authentication

auth.sendOtp("user@example.com", "login")
val result = auth.verifyOtp(
  email = "user@example.com",
  otp = "123456",
  purpose = "login"
)
println("Logged in: ${result.user.id}")

Magic Link & Email Verification

auth.sendMagicLink("user@example.com", "login")

OAuth Authentication

Setup checklist — do this once per provider, per project:
1. Dashboard → Project → Auth → Providers: enable the provider, paste Client ID and Client Secret (no leading/trailing spaces).
2. Copy the Redirect URI shown: https://<slug>.wowsqlconnect.com/auth/v1/oauth/<provider>/callback and register it in your provider's developer console.
3. The callback URL is handled entirely by WowSQL — your app does not send an API key on it. WowSQL exchanges the code and redirects to your frontend_redirect_uri with ?access_token=&refresh_token=.

// ── Step 1: Server/ViewModel — get the authorization URL ─────────────────────
val oauthUrl = auth.getOAuthAuthorizationUrl(
  "google",
  "https://yourapp.com/auth/callback"
)
// Open a browser / custom tab to oauthUrl.authorizationUrl
// Google calls: https://<slug>.wowsqlconnect.com/auth/v1/oauth/google/callback
// WowSQL exchanges the code and redirects to your frontend_redirect_uri.


// ── Step 2: App — handle the redirect ────────────────────────────────────────
// WowSQL redirects to: https://yourapp.com/auth/callback?access_token=...&refresh_token=...
// Use an Android deep-link / intent-filter to intercept the URI and extract the tokens.


// ── (Advanced) Manual code exchange — only if you handle the callback yourself
val result = auth.exchangeOAuthCallback(
  "google",
  "authorization_code_from_callback",
  "https://yourapp.com/auth/callback"  // must match step 1
)
println("OAuth login successful: ${result.user.id}")

With ViewModel (Android)

Use the client inside an Android ViewModel with coroutines:

class UsersViewModel(
    private val client: WowSQLClient
) : ViewModel() {

    private val _users = MutableLiveData<List<Map<String, Any?>>>()
    val users: LiveData<List<Map<String, Any?>>> = _users

    fun loadUsers() {
        viewModelScope.launch {
            try {
                val response = client.table("users")
                    .select("*")
                    .limit(10)
                    .execute()
                _users.value = response.data
            } catch (e: Exception) {
                // Handle error
            }
        }
    }
}

Storage Operations

Storage endpoints are served at /storage/v1/.

Initialize Storage

val storage = WowSQLStorage(
    projectUrl = "myproject",
    apiKey = "wowsql_anon_your_key_here"
)

File Operations

// Upload file
val result = storage.uploadFromPath("path/to/document.pdf", "uploads/document.pdf", "documents")
println("Uploaded: ${result["file_key"]}")

// Get file URL
val urlData = storage.getFileUrl("uploads/document.pdf", 3600)
println("Download URL: ${urlData["file_url"]}")

// List files
val files = storage.listFiles("uploads/")
files.forEach { println("${it.key}: ${it.sizeMb} MB") }

// Delete file
storage.deleteFile("uploads/document.pdf")

// Check quota
val quota = storage.getQuota()
println("Used: ${quota.usedGb} GB / ${quota.quotaGb} GB")

Download or View File

Via REST:

  • Upload: POST /storage/v1/object/{bucket}/{path}
  • Download: GET /storage/v1/object/{bucket}/{path}
  • List: POST /storage/v1/object/list/{bucket}
  • Public URL: GET /storage/v1/object/public/{bucket}/{path}

Schema DDL (WowSQLSchema)

Requires service role key. createTable requires primaryKey to name a column whose type is UUID. Use auto_increment on that column for DEFAULT gen_random_uuid(). Raw CREATE TABLE via execute SQL cannot use SERIAL PRIMARY KEY (or similar integer PK). Existing tables are unchanged.

val schema = WowSQLSchema(projectUrl = "myproject", apiKey = "wowsql_service_your_key_here")
schema.createTable(
    tableName = "items",
    columns = listOf(
        mapOf("name" to "id", "type" to "UUID", "auto_increment" to true),
        mapOf("name" to "title", "type" to "TEXT"),
    ),
    primaryKey = "id",
)