ID Strategies
The Firestore document name is your document’s identity (see Document Identity). Choosing how that name is assigned is a modeling decision. This page covers the three common strategies and how to write each.
Auto-generated ids (the default)
Section titled “Auto-generated ids (the default)”Let Firestore assign a random id. Use create(data) — it generates the id and returns { id } (or
the document with { returnDoc: true }). This is the right default for entities with no natural key
(users created by sign-up, orders, events):
const { id } = await orderRepo.create({ total: 42, status: 'pending' });If you need the id before writing — e.g. to reference it from another document in the same
request — mint one with repo.newId() and write under it with upsert:
const orderId = orderRepo.newId();await invoiceRepo.upsert(invoiceId, { orderId }); // reference it firstawait orderRepo.upsert(orderId, { total: 42, status: 'pending' });Deterministic / static ids
Section titled “Deterministic / static ids”When a document has a natural key — a slug, an external system’s id, a singleton config document — use that value as the id. Choose the write method by what a re-run should do:
createWithId(id, data)— create-only. Claims the id exactly once; a second write (or a concurrent claim) raisesConflictErrorand leaves the stored document untouched. Use this when the id must be claimed (an idempotency key, an upstream record id, an email hash).upsert(id, data)— create-or-overwrite. Re-running is idempotent and overwrites. Use this when the latest write should win (a singleton config, a mirrored external record).
import { ConflictError } from 'flintfire';
// Claim an externally-derived id exactly oncetry { await productRepo.createWithId(`sku-${sku}`, { name, price });} catch (error) { if (error instanceof ConflictError) { // Already claimed — do not overwrite. }}
// A singleton config document at a fixed id (re-runs overwrite)await configRepo.upsert('app-config', { featureFlags: { darkMode: true } });
// A record keyed by an external id where the latest snapshot should winawait productRepo.upsert(`sku-${sku}`, { name, price });Validate any externally-sourced id at the boundary with repo.id(rawKey) before using it as a
document name — see Document Identity. For the
full create-only / precondition surface, see
Conditional writes.
Shared ids across collections
Section titled “Shared ids across collections”To model a 1:1 relationship, store two documents under the same id in different collections (a
users/{uid} profile and a userSettings/{uid} document). Reads become direct getById(uid)
lookups on either collection — no query, no join. Write the secondary document with upsert(uid, …)
so it lands at the shared id.
Choosing
Section titled “Choosing”| Strategy | Write method | Use when |
|---|---|---|
| Auto-generated | create(data) |
No natural key; the common case |
| Claim once | createWithId(id, …) |
A natural key that must not be overwritten on collision |
| Deterministic | upsert(id, data) |
A natural key / slug / external id; re-runs overwrite |
| Singleton | upsert(id, data) |
Exactly one document (config, counters) |
| Shared (1:1) | upsert(id, data) |
A parallel document keyed by another collection’s id |
See CRUD Operations for the full write surface and FirestoreRepository for signatures.