Cloud Firestore is a NoSQL, document-oriented database.
On this page
Introduction
Cloud Firestore is a flexible, scalable NoSQL cloud database, built on Google Cloud infrastructure, to store and sync data for client- and server-side development.
Go to the Firebase console and create a project.
Data model
The Cloud Firestore data model supports whatever data structure works best for our app: unlike a SQL database, there are no tables or rows.
Instead, you store data in documents, which are organized into collections.
Each document contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.
All documents must be stored in collections. Documents can contain subcollections and nested objects, both of which can include primitive fields like strings or complex objects like lists.
Collections and documents are created implicitly in Cloud Firestore. Simply assign data to a document within a collection. If either the collection or document does not exist, Cloud Firestore creates it.
Documents
In Firestore, the unit of storage is the document. A document is a lightweight record that contains fields, which map to values. Each document is identified by a name.
A document representing a user alovelace might look like this:
📄 alovelace
first : "Ada"
last : "Lovelace"
born : 1815Firestore supports a variety of data types for values: boolean, number, string, geo point, binary blob, and timestamp. You can also use arrays or nested objects, called maps, to structure data within a document.
Complex, nested objects in a document are called maps.
For example, you could structure the user’s name from the example above with a map, like this:
📄 alovelace
name :
first : "Ada"
last : "Lovelace"
born : 1815You may notice that documents look a lot like JSON:
"name": "first": "Ada",
"last": "Lovelace"
},
"born": 1815
}In fact, they basically are. There are some differences (for example, documents support extra data types and are limited to the document size limit), but in general, you can treat documents as lightweight JSON records.
Collections
Documents live in collections, which are simply containers for documents.
For example, you could have a users collection to contain your various users, each represented by a document:
📁 users
📄 alovelace
first : "Ada"
last : "Lovelace"
born : 1815
📄 aturing
first : "Alan"
last : "Turing"
born : 1912Cloud Firestore is schemaless, so you have complete freedom over what fields you put in each document and what data types you store in those fields. Documents within the same collection can all contain different fields or store different types of data in those fields. However, it’s a good idea to use the same fields and data types across multiple documents, so that you can query the documents more easily.
A collection contains documents and nothing else. It can’t directly contain raw fields with values, and it can’t contain other collections. (See Hierarchical Data for an explanation of how to structure more complex data in Cloud Firestore.)
The names of documents within a collection are unique. You can provide your own keys, such as user IDs, or you can let Cloud Firestore create random IDs for you automatically.
You do not need to “create” or “delete” collections. After you create the first document in a collection, the collection exists. If you delete all of the documents in a collection, it no longer exists.
References
Every document in Firestore is uniquely identified by its location within the database.
The previous example showed a document alovelace within the collection users. To refer to this location in your code, you can create a reference to it.
Add the Firebase dependency in deno.json:
Edit main.ts:
;
;
const config = apiKey: "AIzaSyCM61mMr_iZnP1DzjT1PMB5vDGxfyWNM64",
authDomain: "firestore-snippets.firebaseapp.com",
projectId: "firestore-snippets"
};
const app = ;
const db = ;You can create references to collections:
const collectionRef = ;
Run the script:
A reference is a lightweight object that just points to a location in your database.
You can create a reference whether or not data exists there, and creating a reference does not perform any network operations:
const docRef = ;
Collection references and document references are two distinct types of references and let you perform different operations. For example, you could use a collection reference for querying the documents in the collection, and you could use a document reference to read or write an individual document.
For convenience, you can also create references by specifying the path to a document or collection as a string, with path components separated by a forward slash (/).
For example, to create a reference to the alovelace document:
const docRef = ;Hierarchical Data
To understand how hierarchical data structures work in Firestore, consider an example chat app with messages and chat rooms.
You can create a collection called rooms to store different chat rooms:
📁 rooms
📄 roomA
name : "my chat room"
📄 roomB
...Now that you have chat rooms, decide how to store your messages. You might not want to store them in the chat room’s document. Documents in Firestore should be lightweight, and a chat room could contain a large number of messages.
However, you can create additional collections within your chat room’s document, as subcollections.
Subcollections
The best way to store messages in this scenario is by using subcollections. A subcollection is a collection associated with a specific document.
You can create a subcollection called messages for every room document in your rooms collection:
📁 rooms
📄 roomA
name : "my chat room"
📁 messages
📄 message1
from : "alex"
msg : "Hello World!"
📄 message2
📄 roomB
...In this example, you would create a reference to a message in the subcollection with the following code:
const messageRef = ;Notice the alternating pattern of collections and documents. Your collections and documents must always follow this pattern. You cannot reference a collection in a collection or a document in a document.
Subcollections allow you to structure data hierarchically, making data easier to access. To get all messages in roomA, you can create a collection reference to the subcollection messages and interact with it like you would any other collection reference.
Documents in subcollections can contain subcollections as well, allowing you to further nest data. You can nest data up to 100 levels deep.
Warning: Deleting a document does not delete its subcollections! When you delete a document that has subcollections, those subcollections are not deleted. For example, there may be a document located at
coll/doc/subcoll/subdoceven though the documentcoll/docno longer exists. If you want to delete documents in subcollections when deleting a parent document, you must do so manually, as shown in Delete Collections.
Data Types
Manage Data
Configuration
Replace FIREBASE_CONFIGURATION with your web app’s firebaseConfig.
const firebaseConfig = FIREBASE_CONFIGURATION
};You can download the Firebase config file or Firebase config object for each of your project’s apps from the Firebase console’s Project settings page.

You need to update the Firestore security rules in the Firebase Console for project:
Firebase Console → Firestore Database → Rules
Change the rules to:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}Add data
To persist data when the device loses its connection, see the Enable Offline Data documentation.
Set a document
To create or overwrite a single document, use the following language-specific set() methods:
;
// Add a new document in collection "cities"
await name: "Los Angeles",
state: "CA",
country: "USA"
});If the document does not exist, it will be created.
If the document does exist, its contents will be overwritten with the newly provided data, unless you specify that the data should be merged into the existing document, as follows:
;
const cityRef = ;
;If you’re not sure whether the document exists, pass the option to merge the new data with any existing document to avoid overwriting entire documents.
For documents that contain maps, if you specify a set with a field that contains an empty map, the map field of the target document is overwritten.
Data types
Cloud Firestore lets you write a variety of data types inside a document, including strings, booleans, numbers, dates, null, and nested arrays and objects. Cloud Firestore always stores numbers as doubles, regardless of what type of number you use in your code.
Query Data
Delete Data
Export and import data
Writing Data
Add a document (auto-generated ID)
Use addDoc to insert a new document and let Firestore assign an ID automatically.
;
;
const docRef = await name: "Alice",
age: 30,
email: "alice@example.com",
});
;Set a document (custom ID)
Use setDoc when you want to choose the document ID yourself.
;
;
await name: "Alice",
age: 30,
email: "alice@example.com",
});Calling setDoc replaces the entire document. To only update specific fields without erasing the rest, use updateDoc or pass { merge: true } as a third argument to setDoc.
Update specific fields
updateDoc only modifies the fields you specify — all other fields remain untouched.
;
;
await age: 31,
});Delete a document
;
;
await ;Create a products collection and add a document with the following fields:
name— a product name of your choiceprice— a numberinStock— a boolean
Log the auto-generated document ID to the console.
Show solution
;
;
const docRef = await name: "Mechanical Keyboard",
price: 129.99,
inStock: true,
});
;Reading Data
Read a single document
;
;
const docRef = ;
const docSnap = await ;
;
} ;
}Always check
docSnap.exists()before calling.data(). If the document does not exist,.data()returnsundefinedand your app will silently break.
Read all documents in a collection
;
;
const querySnapshot = await ;
querySnapshot. ;
});Filter and sort with queries
Use query, where, and orderBy to narrow down results.
;
;
// Get users older than 25, sorted by age then name
const q = ,
,
,
);
const querySnapshot = await ;
querySnapshot. ;
});When you combine where and orderBy on different fields, Firestore may ask you to create a composite index. The error message in the console will include a direct link to create it automatically.
Fetch all documents from the products collection where price is less than 50, and print the name and price of each result to the console.
Show solution
;
;
const q = ,
);
const snapshot = await ;
snapshot. const = doc.;
;
});Real-time Listeners
Instead of fetching data once, onSnapshot subscribes to changes and calls your callback every time the data updates — instantly, without polling.
Listen to a single document
;
;
const unsub = ;
}
});
// Later — stop listening (e.g. when the component unmounts)
;Listen to a collection query
;
;
const q = ;
const unsub = snapshot.. ;
;
;
});
});
// Stop listening when done
;onSnapshot keeps an open WebSocket connection. If you don’t call the returned unsub() function when the listener is no longer needed (e.g., component unmount, user logout), you will leak memory and continue to receive unwanted updates. In React, call unsub() inside a useEffect cleanup function.
const unsub = ;
});
return ; // cleanup on unmount
}, );Add a visits field (number) to a document and build a listener that logs the current visits value every time it changes. Then manually update the field in the Firebase console and observe the log output.
Show solution
;
;
// 1. Listen for changes
const unsub = ;
}
});
// 2. Simulate a visit increment (or do it in the Firebase console)
await visits: 42,
});
// 3. Stop when done
;Subcollections
A document can contain its own collection, called a subcollection. This is useful for modeling one-to-many relationships (e.g., a user’s posts, a chat room’s messages).
users/
└── uid_abc123/
├── name: "Alice"
└── posts/ ← subcollection
├── post_1/
│ └── title: "Hello World"
└── post_2/
└── title: "My second post"Write to a subcollection
;
;
// Path: users/{userId}/posts
await title: "Hello World",
createdAt: ,
});Read from a subcollection
;
;
const postsSnap = await
);
postsSnap. ;
});Deleting a document does not delete its subcollections. You must delete subcollection documents separately, or use a Cloud Function to do it recursively.
Security Rules
By default Firestore blocks all reads and writes. Security rules let you control who can access what — they run on Google’s servers and cannot be bypassed by client code.
Rules are defined in the Firestore → Rules tab of the Firebase console, or in firestore.rules in your project.
Block everything (default / lockdown)
rules_version = '2';
service cloud. match /databases//documents match / allow read, write: false;
}
}
}Allow only authenticated users
rules_version = '2';
service cloud. match /databases//documents match / allow read, write: request. != null;
}
}
}Allow users to read/write only their own data
rules_version = '2';
service cloud. match /databases//documents match /users/ allow read, write: request. != null && request.. == userId;
}
}
}The rule allow read, write: if true; lets anyone read or delete all your data without authentication. Only use it for quick local tests and always replace it before deploying.
Final Challenge
Implement a simple guestbook using everything you learned:
- Create a
guestbookcollection. - Write a function
addEntry(name, message)that adds a document withname,message, and acreatedAttimestamp. - Write a function
listenToEntries(callback)that subscribes to theguestbookcollection ordered bycreatedAtascending and callscallbackwith the array of entries on every change. - Make sure to return the unsubscribe function from
listenToEntries.
Show solution
collection,
addDoc,
query,
orderBy,
onSnapshot,
serverTimestamp,
} from "firebase/firestore";
;
// Add a new guestbook entry
await name,
message,
createdAt: , // server-side timestamp, not the client clock
});
}
// Subscribe to all entries in chronological order
const q = ,
);
const unsub = const entries = snapshot.. id: doc.,
...doc.,
}));
;
});
return unsub; // caller is responsible for calling unsub()
}