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 class
s for the firestoredocument
s.
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 class
s 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 User
will be used for Document schema (fields).value class UserId
will be used as Identity class. You may useString
instead too."users"
will be used in firebase path.val users = rootCollection<User, UserId>("users")
- Define a sub collection -
messages
underusers
. - Similarly, for
messages
,Message
is the entity class andMessageId
its 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 class
as Identity class for documents, instead ofString
. - Have single method to delete collection and document.
- Non-recursive delete options.
-
add
to returnid
as 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
.