Firestore Kotlin Client with strict (and relaxed) type-system.

Overview

Firestore4k

Build Status Kotlin version badge GitHub license

⚠️

Experimental

Firestore Kotlin Client with strict (and relaxed) type-system.
Inspired by Kotlin Path API, where div / symbol is overloaded to express file path.

Code preview

DSL to express Firestore collection & document path

("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")">
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")

Use collection & document path for operations.

(users / UserId("user1")) // get all val messages = getAll (users / UserId("user1") / messages)">
// add (ID auto generated by Firestore)
val userId: String = add(users, User())
// set
put(users / UserId("user1"), User())
// get
val user = get<User>(users / UserId("user1"))
// get all
val messages = getAll<Message>(users / UserId("user1") / messages)

Define flexible dynamic or strict + type-inference typed collection hierarchy.

("users") val messages = users.subCollection ("messages")">
// Using `dynamic` API
val users = collection("users")
val messages = collection("messages")
//       OR
// Using `typed` API
val users = rootCollection<User, UserId>("users")
val messages = users.subCollection<Message, MessageId>("messages")

Preface

GCP Firestore client for Koltin + Gradle project.

Firestore is a NoSQL document-store (tree based) database-as-a-service from Google Cloud Platform.

API in two flavors:

  • Dynamic → Flexible dynamic API with relaxed type checks for DB schema.

  • Typed → Typed-API with type safety for DB schema.

For Typed API, you can optionally use annotations along with KSP (Kotlin Symbol Processing) to autogenerate some of the boilerplate code.

Sample code

Firestore stores the DB in alternate hierarchy of collections and documents.
Ref: https://firebase.google.com/docs/firestore/manage-data/structure-data

This structure is a mirror of the Resource Oriented Design of REST API Design guidelines recommended by Google.
Ref: https://cloud.google.com/apis/design/resources

  • Collection names as plural.

  • Collections and documents are alternative in hierarchy: / / /

  • Top-level is always a collection, not a document.

For the sample code, I will use a root (top-level) collection: users and its sub (child) collection: messages.

Path Description

users

users as root collection

users/user1

user1 document under users root collection

users/user1/messages

messages sub-collection under user1 document

users/user1/messages/message1

message1 document under messages sub-collection under user1 document

For dynamic API

Define collections

val users = collection("users")
val messages = collection("messages")

And then use them in PATHs of CRUD operations such as…​

(users / "user1") val message = get (users / "user1" / messages / "message1") // get all val users: Collection = getAll(users) val messages: Collection = getAll(users / "user1" / messages) // OR val users = getAll (users) val messages = getAll (users / "user1" / messages) // delete delete(users / "user1" / messages / "message1") deleteAll(users / "user1" / messages) delete(users / "user1") deleteAll(users)">
// add (ID auto generated by Firestore)
val userId: String = add(users, User())
val messageId: String = add(users / "user1" / messages, Message())

// set
put(users / "user1", User())
put(users / "user1" / messages / "message1", Message())

// get
val user: User = get(users / "user1")
val message: Message = get(users / "user1" / messages / "message1")
// OR
val user = get<User>(users / "user1")
val message = get<Message>(users / "user1" / messages / "message1")

// get all
val users: Collection<User> = getAll(users)
val messages: Collection<Message> = getAll(users / "user1" / messages)
// OR
val users = getAll<User>(users)
val messages = getAll<Message>(users / "user1" / messages)

// delete
delete(users / "user1" / messages / "message1")
deleteAll(users / "user1" / messages)
delete(users / "user1")
deleteAll(users)

For typed API

Define collection hierarchy and type bindings

("messages")">
val users = rootCollection<User>("users")
val messages = users.subCollection<User, Message>("messages")

CRUD operations for typed are similar to dynamic, but with type safety & inference.

  • So, users have to be root collection and messages under it.

  • Code accepts User / Message objects only in their respective add and put functions.

  • Type inference for return value of object & collection in get and getAll functions respectively.

// add (ID auto generated by Firestore)
val userId: String = add(users, User())
val messageId: String = add(users / "user1" / messages, Message())

// set
put(users / UserId("user1"), User())
put(users / UserId("user1") / messages / MessageId("message1"), Message())

// get
val user = get(users / UserId("user1"))
val message = get(users / UserId("user1") / messages / MessageId("message1"))

// get all
val users = getAll(users)
val messages = getAll(users / UserId("user1") / messages)

// delete
delete(users / UserId("user1") / messages / MessageId("message1"))
deleteAll(users / UserId("user1") / messages)
delete(users / UserId("user1"))
deleteAll(users)

Using annotations + KSP for typed API

Collection hierarchy and type bindings are autogenerated using annotations.
But for simple cases, it is not worth the complexity since it is more verbose.

// root collection will not have @[ChildOf] annotation.
@Collection("users")
data class User(
    val name: String,
) {

    // Needed for DSL
    companion object
}

@IdOf("users")
@JvmInline
value class UserId(private val value: String) {
    override fun toString(): String = value
}

@Collection("messages")
@ChildOf("users")
data class Message(
    val body: String,
) {

    // Needed for DSL
    companion object
}

@IdOf("messages")
@JvmInline
value class MessageId(private val value: String) {
    override fun toString(): String = value
}

Dependencies

For dynamic API

dependencies {
    implementation("dev.vihang.firestore4k:dynamic:$latestVersion")
}

For typed API

dependencies {
    implementation("dev.vihang.firestore4k:typed:$latestVersion")
}

For typed API with annotations & KSP

plugins {
    id("com.google.devtools.ksp")
}

dependencies {
    implementation("dev.vihang.firestore4k:typed:$latestVersion")
    compileOnly(project("dev.vihang.firestore4k:annotations:$latestVersion"))
    ksp(project("dev.vihang.firestore4k:ksp:$latestVersion"))
}
You might also like...
Android Data Managment System Android UI - Kotlin- Firebase
Android Data Managment System Android UI - Kotlin- Firebase

DataManagmentSystem Data Managment System Android UI - Kotlin- Firebase Android Data Managment System App Design And Kotlin with Firebase The project

Task Manager feat. real-time competitive system and user engagement
Task Manager feat. real-time competitive system and user engagement

Dira Что из себя представляет Dira? Android-приложение Directa (сокр. Dira) - это планер, который способен улучшить жизнь пользователей. Он позволяет

This project is to create a system that uses DeFi technology to enforce contracts.
This project is to create a system that uses DeFi technology to enforce contracts.

This project is to create a system that uses DeFi technology to enforce contracts. Users will be able to set up contracts between each other, this includes an escrow service for payments. If users disagree over whether a contract was fulfilled, a jury appointed by the system will make the final decision.

An android app using Mongodb for logistics tracking system.

DeliveryGo An android application for logistics tracking. Used technology Kotlin: In this project kotlin is used to code the full application on Andro

Trawler - Trawler is a BaaS system. Backend as a service

Trawler is a BaaS system. Backend as a service. BaaS Generate REST and GraphQL f

Sushi Design System - UI Kit for Android apps
Sushi Design System - UI Kit for Android apps

Sushi Design System ⚡️ Android UI Kit ⚡️ Application is available here: Latest release: Usage The master branch is being used for release and dev is t

Alternative to DreamStorageService, but instead of storing files on a database, it is stored on the file system itself.

EtherealGambi Alternative to DreamStorageService, but instead of storing files on a database, it is stored on the file system itself. I made this beca

Android implementation of the Flamingo Design System
Android implementation of the Flamingo Design System

It is an Android implementation of the Flamingo Design System. It Consists Of: Palette, theme colors, typography, icon set, illustrations, gradients a

Integration Testing Kotlin Multiplatform Kata for Kotlin Developers. The main goal is to practice integration testing using Ktor and Ktor Client Mock
Integration Testing Kotlin Multiplatform Kata for Kotlin Developers. The main goal is to practice integration testing using Ktor and Ktor Client Mock

This kata is a Kotlin multiplatform version of the kata KataTODOApiClientKotlin of Karumi. We are here to practice integration testing using HTTP stub

Owner
Vihang Patil
Kotlin Backend Developer with 16 yrs experience. Exploring Semantic Ontological Knowledge Graphs. Like GCP, neo4j graph DB, k8s.
Vihang Patil
Quiz Zone is a simple ✅ Quiz Android application 📱 using Firbase Firestore Database and Material Design.

Quiz Zone Quiz Zone is a simple ✅ Quiz Android application ?? using Firbase Firestore Database and Material Design. You can Install and test Quiz Zone

MOHIT GUPTA 6 Dec 24, 2022
Kotlin extension function provides a facility to "add" methods to class without inheriting a class or using any type of design pattern

What is Kotlin Extension Function ? Kotlin extension function provides a facility to "add" methods to class without inheriting a class or using any ty

mohsen 21 Dec 3, 2022
Lambë Language 7 Dec 21, 2022
Esito ambition is to be your return type for suspending functions.

Esito ambition is to be your return type for suspending functions.

null 58 Oct 21, 2022
The sample App implements type safe SQL by JOOQ & DB version control by Flyway

The sample App implements type safe SQL by JOOQ & DB version control by Flyway Setup DB(PostgreSQL) $ docker compose up -d Migration $ ./gradlew flywa

t-kurihara 3 Jan 1, 2022
Bring type-safety to your GitHub actions' API!

GitHub Actions typing Bring type-safety to your GitHub actions' API! This is a GitHub action that validates your action's type specs (action-types.y(a

Piotr Krzemiński 23 Dec 18, 2022
The WeeBe application is a social media-type app built on Ktor framework

The WeeBe application is a social media-type app built on Ktor framework that allows users to exchange various content connected with mental health, motivation, psychology, and improving oneself. Users can share posts with texts, images, videos, and links, as well as discuss the content in the comment section

Perpetio 3 Aug 5, 2022
KTor-Client---Android - The essence of KTor Client for network calls

KTor Client - Android This project encompasses the essence of KTor Client for ne

Mansoor Nisar 2 Jan 18, 2022
A command-line student management system in Kotlin

Student Management System Done as a part of NITK GSDC Android Study Jams 2021 Th

Ranjana Kambhammettu 2 Dec 24, 2021
PlanetFacts - An educational android app for kids to learn about the planets in our solar system. Built with Kotlin.

PlanetFacts PlanetFacts is an offline simple, modern & material-designed educational Android application for kids. It contains basic facts with visual

Saikat Datta 1 Oct 16, 2022