Document reference
A document reference is a pointer to a specific record in a collection, used for reading, writing, and deleting individual documents.
Why Use Document References
You need to read, write, or delete a specific document in your database. A document reference gives you a direct pointer to a single record, letting you perform operations on it without querying the entire collection.
// Get a reference to a specific user and read their data
const userRef = squid.collection<User>('users').doc('user_123');
const user = await userRef.snapshot();
console.log(user?.name);
Overview
A document can refer to a row in a table in a relational database or a document in a NoSQL database. A document reference acts as a typed pointer to that specific record.
Document references can also point to non-existent documents, which is how you create new documents by calling insert on the reference.
To get a reference to a document in a collection, use the doc method with a document ID:
const userRef = squid.collection<User>('users').doc('user_123');
Quick Start
Step 1: Create a collection reference and get a document
interface User {
id: string;
name: string;
email: string;
}
const usersCollection = squid.collection<User>('users');
const userRef = usersCollection.doc('user_123');
Step 2: Read the document data
// Single snapshot (returns the data or undefined if the document doesn't exist)
const user = await userRef.snapshot();
if (user) {
console.log(user.name, user.email);
}
Step 3: Write data to the document
// Insert a new document
await usersCollection.doc('new_user').insert({
id: 'new_user',
name: 'Alice',
email: 'alice@example.com',
});
// Update an existing document
await userRef.update({ email: 'newemail@example.com' });
Core Concepts
snapshot vs snapshots
A document reference provides two ways to read data:
snapshot()returns aPromise<T | undefined>that resolves with the current document data, orundefinedif the document does not exist. Use this for one-time reads.snapshots()returns an RxJS Observable ofT | undefinedthat emits the latest data each time the document changes. Use this for real-time updates.
// One-time read (returns the data directly)
const user = await userRef.snapshot();
if (user) {
console.log(user.name); // Typed access to User fields
}
// Real-time subscription
userRef.snapshots().subscribe((user) => {
console.log('User updated:', user);
});
The data getter on query results
When querying multiple documents via query().snapshot(), results are returned as DocumentReference<T>[]. Each document reference has a data getter to access the typed data:
const users = await squid.collection<User>('users').query().snapshot();
for (const userRef of users) {
const name: string = userRef.data.name; // Type-safe access via .data
}
Alternatively, use dereference() to get the data directly without document references. See Queries for details.
Referencing non-existent documents
You can create a reference to a document that does not yet exist. This is the standard way to insert new documents:
// This document doesn't exist yet
const newUserRef = squid.collection<User>('users').doc('new_user_id');
// Create it by inserting data
await newUserRef.insert({
id: 'new_user_id',
name: 'Bob',
email: 'bob@example.com',
});
For auto-generated IDs, call doc() without arguments. See Document IDs for details.
Error Handling
| Error | Cause | Solution |
|---|---|---|
| Document not found | snapshot() returned null because the document does not exist | Check for null before accessing data |
| Invalid document ID | The ID format does not match the collection's key schema | Use a string ID for built-in database, or an object ID for external connectors |
| Security rule rejection | The mutation was blocked by security rules | Verify the user has permission for the operation |
Best Practices
-
Always check for undefined when reading a document, since the document may not exist:
Client codeconst user = await userRef.snapshot();
if (!user) {
console.log('User not found');
return;
}
console.log(user.name); -
Use
snapshots()for UI bindings where you need real-time updates, andsnapshot()for one-time reads like form pre-population. -
Prefer
update()overinsert()when modifying existing documents to send only the changed fields. See Adding data for details.
See Also
- Document IDs - ID formats for built-in and external databases
- Collection references - Access collections and tables
- Queries - Query documents with filters, sorting, and joins
- Adding data - Insert and update operations
- Deleting data - Delete operations