Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Document and collection IDs

FirestoreDocumentId and FirestoreCollectionId are validated newtypes for IDs that arrive from outside your process - a request path segment, a JSON body, a runtime-chosen collection name. Construct one where the ID enters your system; a /, an empty string, or ./.. is rejected there instead of silently reaching Firestore or, in the case of a collection name, retargeting the operation at a different collection.

use firestore::*;
fn example() -> Result<(), Box<dyn std::error::Error>> {
let id = FirestoreDocumentId::new("user-42")?;
let collection = FirestoreCollectionId::new("users")?;
let _ = (id, collection);
Ok(())
}

A name known up front - the sort of thing you would today write as const NAME: &str = "..." - can be declared as a validated const or static with from_static. An invalid literal there is a compile error, not a runtime one:

use firestore::*;
const USERS: FirestoreCollectionId = FirestoreCollectionId::from_static("users");

Use from_static for a literal you control, checked at compile time with no Result to handle; use new for a value arriving at runtime, which returns a FirestoreResult.

Both implement AsRef<str>, so a reference drops straight into any call that already takes a document or collection ID or name, with no conversion:

use firestore::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
struct MyTestStructure { some_id: String }
async fn example(
    db: FirestoreDb,
    collection: FirestoreCollectionId,
    id: FirestoreDocumentId,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
db.fluent()
    .select()
    .by_id_in(&collection)
    .obj::<MyTestStructure>()
    .one(&id)
    .await?;
Ok(())
}

Store the validated type in your own structs instead of a bare String to carry the proof along with the value; it deserializes through the same validation, so a CreateSession built from an untrusted request body rejects a bad ID before it reaches Firestore:

use firestore::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
struct CreateSession {
    user_id: FirestoreDocumentId,
}

See examples/crud.rs and examples/nested_collections.rs for a document ID and a parent_path/at pair built from validated IDs, and examples/dynamic_doc_level_crud.rs / examples/caching_dynamic_collections.rs for a collection name chosen at runtime.