0Pricing
Android Academy · Lesson

Cloud Firestore Basics

Read and write realtime data.

Cloud Firestore Basics is a free Android Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Android Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Is Cloud Firestore?

Cloud Firestore is Firebase's flexible, scalable NoSQL database. It stores data as documents grouped into collections.

  • A document is a set of key/value fields (like a JSON object).
  • A collection is a container of documents.
  • Documents can contain subcollections, forming a tree.

Firestore also offers realtime updates and works offline out of the box.

Get the Firestore Instance

Access the database through Firebase.firestore. From there you reach a collection with collection("name") and a specific document with document("id").

References are cheap to create — they describe a location, not the data itself.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore

val db = Firebase.firestore

val usersRef = db.collection("users")
val oneUser = db.collection("users").document("abc123")

Modeling Data

Firestore maps cleanly to Kotlin data classes. Each property becomes a field in the document.

Give it a no-argument constructor (default values handle this) so Firestore can deserialize documents back into objects.

data class Note(
    val title: String = "",
    val body: String = "",
    val done: Boolean = false,
    val createdAt: Long = 0L
)

Writing a Document

Use set to create or overwrite a document. Let Firestore generate an ID with add, or choose your own ID with document(id).set(...).

These calls return Tasks; with coroutines you await() them.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun addNote(note: Note): String {
    val ref = Firebase.firestore
        .collection("notes")
        .add(note)          // auto-generated ID
        .await()
    return ref.id
}

Reading a Document

get() fetches a document once. Convert it to your data class with toObject(Note::class.java).

Always check exists() — a reference to a missing document returns a snapshot that simply doesn't exist.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun loadNote(id: String): Note? {
    val snapshot = Firebase.firestore
        .collection("notes")
        .document(id)
        .get()
        .await()
    return if (snapshot.exists()) snapshot.toObject(Note::class.java) else null
}

Updating and Deleting

update changes specific fields without touching the rest of the document. delete removes the document entirely.

Use set(..., SetOptions.merge()) if you want a partial write that also creates the doc when missing.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun markDone(id: String) {
    Firebase.firestore.collection("notes").document(id)
        .update("done", true)
        .await()
}

suspend fun removeNote(id: String) {
    Firebase.firestore.collection("notes").document(id)
        .delete()
        .await()
}

Querying a Collection

Build queries by chaining filters: whereEqualTo, whereGreaterThan, orderBy, limit and more.

A query returns a QuerySnapshot; iterate its documents to map each into your model.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.tasks.await

suspend fun pendingNotes(): List<Note> {
    val snap = Firebase.firestore.collection("notes")
        .whereEqualTo("done", false)
        .orderBy("createdAt")
        .limit(20)
        .get()
        .await()
    return snap.documents.mapNotNull { it.toObject(Note::class.java) }
}

Realtime Listeners

Firestore's superpower is realtime. addSnapshotListener delivers the current data immediately, then pushes every change automatically.

It returns a ListenerRegistration — call remove() when you're done to stop receiving updates.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore

val registration = Firebase.firestore.collection("notes")
    .whereEqualTo("done", false)
    .addSnapshotListener { snapshot, error ->
        if (error != null || snapshot == null) return@addSnapshotListener
        val notes = snapshot.toObjects(Note::class.java)
        println("Now have ${notes.size} pending notes")
    }

// Later: registration.remove()

Realtime Data as a Flow

In Compose, it's idiomatic to expose Firestore as a Kotlin Flow. callbackFlow bridges the listener callback into a flow, and awaitClose removes the listener when collection stops.

import com.google.firebase.Firebase
import com.google.firebase.firestore.firestore
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow

fun notesFlow(): Flow<List<Note>> = callbackFlow {
    val reg = Firebase.firestore.collection("notes")
        .addSnapshotListener { snap, err ->
            if (err == null && snap != null) {
                trySend(snap.toObjects(Note::class.java))
            }
        }
    awaitClose { reg.remove() }
}

Showing Data in Compose

Collect the flow with collectAsStateWithLifecycle() and render it in a LazyColumn. The list updates itself whenever Firestore changes — no manual refresh.

@Composable
fun NotesScreen(viewModel: NotesViewModel) {
    val notes by viewModel.notes.collectAsStateWithLifecycle()

    LazyColumn {
        items(notes) { note ->
            ListItem(
                headlineContent = { Text(note.title) },
                supportingContent = { Text(note.body) }
            )
        }
    }
}

Security Rules Matter

By default Firestore is locked down. Security rules decide who can read or write each path — they run on the server and cannot be bypassed by the client.

A common rule: a user may only access their own data, keyed by their auth uid.

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}/{document=**} {
      allow read, write: if request.auth != null
                         && request.auth.uid == userId;
    }
  }
}

Quick Check

You want a Compose list that updates instantly whenever the underlying Firestore data changes, without manual refreshing. Which API do you use?

Recap: Firestore

You can now store and sync data with Cloud Firestore:

  • Data lives as documents in collections; model it with Kotlin data classes.
  • Write with add/set, read with get, change with update/delete.
  • Query with whereEqualTo, orderBy, limit.
  • Use addSnapshotListener (or a callbackFlow) for realtime UI.
  • Protect data with security rules keyed on request.auth.uid.

Next: reaching users when the app is closed with Cloud Messaging.

Frequently asked questions

Is the “Cloud Firestore Basics” lesson free?

Yes — the full text of “Cloud Firestore Basics” is free to read here on the web, and the Android Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Android Academy course, upgrade to CoddyKit PRO.

What will I learn in “Cloud Firestore Basics”?

Read and write realtime data. You practise Android Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Android Academy?

No prior experience is required. Android Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Cloud Firestore Basics” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Android Academy lesson?

Yes. Every Android Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Setting Up Firebase
  2. Firebase Authentication
  3. Cloud Firestore Basics
  4. Push with Cloud Messaging
← Back to Android Academy