How to build a CRUD app on Google Cloud Firestore
Model collections and perform CRUD operations and queries on Google Cloud Firestore from a web app, including real-time listeners and composite indexes.
Google Cloud Firestore is a serverless document database with real-time sync and offline support, popular for web and mobile apps. Data lives in collections of documents; documents can hold subcollections. Firestore charges per read, write, and delete, so query design matters.
Prerequisites
- A Firebase or Google Cloud project with Firestore enabled.
- Node.js and the
firebaseSDK installed.
Steps
1. Enable Firestore
In the Firebase console, create a Firestore database in production mode and choose a region close to your users.
2. Model collections
A tasks collection where each document is a task works well. Keep documents small and avoid deeply nested data you will not query.
3. Add documents
import { getFirestore, collection, addDoc } from "firebase/firestore";
const db = getFirestore();
const ref = await addDoc(collection(db, "tasks"), { title: "write tutorial", done: false });
addDoc generates an ID; use setDoc to control the ID yourself.
4. Read documents
import { getDoc, doc } from "firebase/firestore";
const snap = await getDoc(doc(db, "tasks", ref.id));
console.log(snap.data());
5. Update and delete
import { updateDoc, deleteDoc } from "firebase/firestore";
await updateDoc(doc(db, "tasks", ref.id), { done: true });
await deleteDoc(doc(db, "tasks", ref.id));
6. Query and index
import { query, where, getDocs } from "firebase/firestore";
const q = query(collection(db, "tasks"), where("done", "==", false));
const results = await getDocs(q);
Compound queries (multiple filters or filter plus order) need a composite index; Firestore prints a link to create it on first run.
Verification
Watch documents appear and change in the Firestore Data viewer in the console. Add a real-time listener with onSnapshot and confirm updates push to the client without a refresh.
Next Steps
Write security rules so clients can only access their own data, batch writes for atomicity, and paginate large collections with startAfter cursors.