Type-safe API
Dependencies
Add repository URL https://s01.oss.sonatype.org/content/repositories/snapshots/ for SNAPSHOT versions.
plugins {
kotlin("plugin.serialization")
}
dependencies {
implementation("io.firestore4k:typed-api:$latestVersion")
}
Model
Example
In the sample code below, we will define model for:
/users/{useri-d}/messages/{message-id}
So, we have 2 collections - users and messages, each of the collection have documents under them.
users will be a root collection.
messages will be a sub collection under users.
Entity classes
- Define Kotlin
data classs for the firestoredocuments.
Important
Annotate them with @kotlinx.serialization.Serializable
@kotlinx.serialization.Serializable
data class User(
val name: String,
val email: String,
)
@kotlinx.serialization.Serializable
data class Message(
val from: String,
val to: String,
val subject: String,
val body: String,
)
Identity classes (optional)
Define Kotlin value classs for IDs for type-safety.
Note
This is optional, in which case, you may use String as IDs.
Important
override fun toString() to return the string id value.
@JvmInline
value class UserId(private val value: String) {
override fun toString(): String = value
}
@JvmInline
value class MessageId(private val value: String) {
override fun toString(): String = value
}
Collections
- Define root collection -
users. data class Userwill be used for Document schema (fields).value class UserIdwill be used as Identity class. You may useStringinstead too."users"will be used in firebase path.val users = rootCollection<User, UserId>("users")- Define a sub collection -
messagesunderusers. - Similarly, for
messages,Messageis the entity class andMessageIdits identity class.val messages = users.subCollection<Message, MessageId>("messages")
firestore path expression
DSL to express Firestore collection & document path.
val users = rootCollection<User, UserId>("users")
val messages = users.subCollection<Message, MessageId>("messages")
// /users
users
// /users/user1
users / UserId("user1")
// /users/user1/message
users / UserId("user1") / messages
// /users/user1/message/message1
users / UserId("user1") / messages / MessageId("message1")
CRUD operations
Use collection & document path for operations.
add
// add (ID auto generated by Firestore)
// add(<collection path>, <document object>): <String Id>
val userId: String = add(users, User())
put
// put (create or update)
// put(<document path>, <document object>)
put(users / UserId("user1"), User())
get
// get
// get(<document path>): <document object>
val user = get<User>(users / UserId("user1"))
get all
// get all
// getAll(<collection path>): Collection<document object>
val messages = getAll<Message>(users / UserId("user1") / messages)
delete document
// delete document and its child entities
// This can be a document under root collection or sub-collection.
// deleteDocument(<document path>)
deleteDocument(users / UserId("user1"))
delete collection
// delete collection and its child entities.
// This can be root collection or sub-collection.
// deleteCollection(<collection path>)
deleteCollection(users)
Future improvements
- Optional typesafe
value classas Identity class for documents, instead ofString. - Have single method to delete collection and document.
- Non-recursive delete options.
-
addto returnidas Identity class instead ofString. - Scope or context for operations enabling -
- option to use relative path instead of entire path from root.
- CRUD operation on document fields.
- Query filters
- Transactions
- Custom
FirestoreOptions.