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