Skip to main content

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.

Client code
// 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:

Client code
const userRef = squid.collection<User>('users').doc('user_123');

Quick Start​

Step 1: Create a collection reference and get a document​

Client code
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​

Client code
// 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​

Client code
// 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 a Promise<T | undefined> that resolves with the current document data, or undefined if the document does not exist. Use this for one-time reads.
  • snapshots() returns an RxJS Observable of T | undefined that emits the latest data each time the document changes. Use this for real-time updates.
Client code
// 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:

Client code
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:

Client code
// 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​

ErrorCauseSolution
Document not foundsnapshot() returned null because the document does not existCheck for null before accessing data
Invalid document IDThe ID format does not match the collection's key schemaUse a string ID for built-in database, or an object ID for external connectors
Security rule rejectionThe mutation was blocked by security rulesVerify the user has permission for the operation

Best Practices​

  1. Always check for undefined when reading a document, since the document may not exist:

    Client code
    const user = await userRef.snapshot();
    if (!user) {
    console.log('User not found');
    return;
    }
    console.log(user.name);
  2. Use snapshots() for UI bindings where you need real-time updates, and snapshot() for one-time reads like form pre-population.

  3. Prefer update() over insert() when modifying existing documents to send only the changed fields. See Adding data for details.

See Also​