Skip to content

Support for Repository.set / upsert by known ID (and FirestoreService.create) #62

Description

@fwal

The repository layer currently has exactly one way to write a new document: add(data), which encodes through Model.insert and lets Firestore pick an auto-ID. FirestoreService already has a set(path, data) method, but it isn't surfaced through the repository at all.

Motivation

A lot of Firestore data is keyed by an ID the caller already knows: documents keyed by user UID, integration configs keyed by provider name, join documents keyed by ${userId}_${orgId}, idempotent writes keyed by an external event ID. For all of those, add doesn't apply, and every consumer ends up hand-rolling the same block:

const firestore = yield* FirestoreService;
const encoded = yield* Schema.encodeEffect(MyModel.insert)(data);
yield* firestore.set(`${collectionPath}/${id}`, encoded);

In one real-world codebase this exact block is copy-pasted across six-plus repositories, and one grew into a full hand-rolled upsert (getById + catch-not-found + branch). The encode/path/span plumbing that makeRepository is meant to centralize leaks back into every consumer.

Suggested shape

readonly set: (
  id: IdSchema['Type'],
  data: S['insert']['Type']
) => Effect.Effect<void, ModelError, ...>;

This is nearly free to implement — the existing add wiring pointed at firestore.set(${collectionPath}/${id}) instead of firestore.add.

Related: create (insert-if-absent)

Firestore distinguishes three write modes at a known ID: set (overwrite/upsert), set with merge: true (partial upsert), and create (fail if the document already exists). The library currently exposes none of the create semantics. The admin SDK's .doc(id).create(data) is the natural primitive for "claim this ID atomically" (auth codes, idempotency guards, uniqueness by document ID). Supporting it would need a new FirestoreService.create method plus a distinguishable already-exists error so callers can branch on it.

Open questions

  • Should set encode through Model.insert so DateTimeInsert fields get stamped? (That's what the hand-rolled workarounds all do, so probably yes.)
  • Since Version 1.0 and Effect v4 support #30 notes the v1 method renames (addinsert etc.) aren't final yet, this is the cheapest moment to slot set/upsert/create into the naming scheme coherently.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions