appwrite-kmp
0.1.1indexedType-safe client for Appwrite offering coroutine-based APIs: errors-as-values, typed IDs, query DSL, Flow realtime with auto-reconnect, chunked upload progress, modular services.
Type-safe client for Appwrite offering coroutine-based APIs: errors-as-values, typed IDs, query DSL, Flow realtime with auto-reconnect, chunked upload progress, modular services.
A Kotlin Multiplatform SDK for Appwrite — built from scratch with type safety, coroutines, and multiplatform support at its core.
Targets: Android, iOS, JVM
The official Appwrite Android SDK is tightly coupled to Android (OkHttp, Gson, SharedPreferences). This SDK is designed for Kotlin Multiplatform from day one:
dependencies {
implementation("io.github.androidpoet:appwrite-client:<version>")
implementation("io.github.androidpoet:appwrite-auth:<version>")
implementation("io.github.androidpoet:appwrite-database:<version>")
implementation("io.github.androidpoet:appwrite-storage:<version>")
implementation("io.github.androidpoet:appwrite-realtime:<version>")
implementation("io.github.androidpoet:appwrite-teams:<version>")
implementation("io.github.androidpoet:appwrite-functions:<version>")
implementation("io.github.androidpoet:appwrite-locale:<version>")
implementation("io.github.androidpoet:appwrite-avatars:<version>")
}
val appwrite = Appwrite("your-project-id") {
endpoint = "https://cloud.appwrite.io/v1"
}
appwrite.sessionStore = SessionStore()
import io.appwrite.auth.auth
val user = appwrite.auth.signUp(
email = "user@example.com",
password = "password123",
name = "Jane Doe",
)
when (val result = appwrite.auth.signInWithEmail("user@example.com", "password123")) {
is AppwriteResult.Success -> println()
AppwriteResult.Failure -> println()
}
appwrite.auth.mfa.enable()
appwrite.auth.mfa.createAuthenticator(AuthenticationFactor.Totp)
appwrite.auth.mfa.verifyAuthenticator(AuthenticationFactor.Totp, otp = )
appwrite.auth.signOut()
import io.appwrite.database.databases
db = appwrite.databases
users = db[DatabaseId()][CollectionId()]
users.create(
= mapOf( to , to , to ),
)
result = users.list {
( greaterThan )
( equal )
orderBy()
limit()
}
users.update(
documentId = DocumentId(),
= mapOf( to ),
)
import io.appwrite.realtime.realtime
appwrite.realtime
.documents(DatabaseId("main"), CollectionId("messages"))
.onEach { event -> updateUI(event.payload) }
.launchIn(viewModelScope) // auto-cleanup on scope cancellation
appwrite.realtime.account()
.collect { event -> handleAccountEvent(event) }
import io.appwrite.teams.teams
appwrite.teams.create(TeamId.unique(), name = "Engineering")
appwrite.teams.createMembership(
teamId = TeamId("eng-team"),
roles = listOf("developer"),
email = "dev@example.com",
)
import io.appwrite.functions.functions
val execution = appwrite.functions.createExecution(
functionId = FunctionId("send-welcome-email"),
body = """{"userId": "abc123"}""",
)
┌─────────────────────────────────────┐
│ Public API (what devs touch) │ DSL builders, typed IDs, Flows
├─────────────────────────────────────┤
│ Domain (business logic) │ Session management, query builder,
│ │ chunked upload orchestration
├─────────────────────────────────────┤
│ Protocol (Appwrite specifics) │ Header injection, error mapping,
│ │ response deserialization
├─────────────────────────────────────┤
│ Transport (HTTP/WS engine) │ Ktor client, expect/actual
└─────────────────────────────────────┘
Errors as values, not exceptions
sealed interface AppwriteResult<out T> {
data class Success<T>(val data: T) : AppwriteResult<T>
data class Failure(val error: AppwriteError) : AppwriteResult<Nothing>
}
Value class IDs prevent string mix-ups
fun getDocument(databaseId: DatabaseId, collectionId: CollectionId, documentId: DocumentId)
Session persistence is opt-in and platform-aware
appwrite.sessionStore = SessionStore()
| Concern | Library |
|---|
git checkout -b feature/amazing-thing)./gradlew jvmTestSupport it by joining stargazers for this repository. :star:
Also, follow me on GitHub for my next creations! 🤩
AppwriteResult<T> instead of thrown exceptionsDatabaseId, CollectionId, FileId — no more string mix-upswhere("age" greaterThan 18) instead of raw stringsFlow<UploadState> with progress trackingimport io.appwrite.storage.storage
val file = InputFile.fromBytes(imageBytes, "photo.jpg", "image/jpeg")
appwrite.storage.upload(BucketId("photos"), FileId.unique(), file)
.collect { state ->
when (state) {
is UploadState.Progress -> println("${state.chunksUploaded}/${state.chunksTotal}")
is UploadState.Complete -> println("Uploaded: ${state.file.id}")
is UploadState.Failed -> println("Error: ${state.error.message}")
}
}
val bytes = appwrite.storage.download(BucketId("photos"), FileId("abc123"))
val thumbnail = appwrite.storage.preview(BucketId("photos"), FileId("abc123")) {
width = 200
height = 200
gravity = ImageGravity.Center
quality = 80
}
| Module | Description |
|---|
appwrite-core | Models, typed IDs, AppwriteResult, query DSL |
appwrite-client | Appwrite entry point, Ktor transport, session persistence |
appwrite-auth | Authentication, sessions, MFA, verification, recovery |
appwrite-database | Document CRUD with scoped navigation, atomic ops |
appwrite-storage | File upload (chunked with Flow), download, preview |
appwrite-realtime | WebSocket subscriptions as Kotlin Flows |
appwrite-teams | Team and membership management |
appwrite-functions | Serverless function execution |
appwrite-locale | Languages, countries, currencies, timezones |
appwrite-avatars | Generated avatars, flags, QR codes |
| HTTP | Ktor |
| Serialization | kotlinx.serialization |
| Async | Kotlin Coroutines |
| Date/Time | kotlinx-datetime |
Surfaced from shared tags and platforms — no rankings paid for.