Skip to content

Latest commit

 

History

History
63 lines (50 loc) · 3.03 KB

File metadata and controls

63 lines (50 loc) · 3.03 KB

chat example

The worked reference for the real layered firepact setup: Firestore documents and plain HTTP DTOs, generated into separate files with each shared enum defined exactly once. This one uses the decorator-on-model style; see ../realtime_app/ for the production style (firepact kept out of the model source, snake_case wire).

datetime is not one TypeScript type

A Pydantic datetime maps to a different TS type depending on where the value lives. This example shows all three:

Field Annotation read view write view
Message.createdAt datetime + FirestoreServerTimestamp() Timestamp | null FieldValue (serverTimestamp())
Message.editedAt plain datetime (Firestore field) Timestamp Timestamp | Date
SendMessageRequest.clientTime plain datetime (HTTP DTO, --plain) string string

So on a Firestore read you get a real Timestamp (data.editedAt.toDate()), while an HTTP payload gives you a string (new Date(clientTime)) -- the two are never confused because they are different generated types.

Inputs (Pydantic)

  • models.py - the Firestore document models. Message is a @firestore_realtime root; Profile/Reaction/Attachment are its transitive closure. Wire keys are camelCase (by_alias=True + to_camel), matching the backend's write serialization.
  • dtos.py - plain HTTP request/response DTOs (SendMessageRequest, SendMessageResponse). Their datetime is an ISO string over HTTP, not a Firestore Timestamp. MessageKind is shared with Message and is defined here (the plain layer is the single source).

Outputs

  • dtos.ts - generated with firepact-gen --plain: one plain interface per model, datetime -> string, strict enums. Defines MessageKind.
  • generated.ts - the Firestore contract (TypeScript): read/write/update views, the read converter (doc-id injection), open string enums, a typed path helper. It imports MessageKind from ./dtos (via --shared-from) instead of redefining it, so every type is defined exactly once across the two files.
  • bundle.json - the deterministic contract bundle (--bundle-out): the enriched JSON Schema that both the TypeScript above and the compatibility gate consume. It is the artifact a project freezes as schema history (see ../../compat/).

Regenerate

just example
# which runs:
firepact-gen --plain --module examples.gen.chat.dtos --output examples/gen/chat/dtos.ts
firepact-gen --module examples.gen.chat.models --output examples/gen/chat/generated.ts \
  --shared ./dtos --shared-from examples.gen.chat.dtos

--shared-from examples.gen.chat.dtos derives the shared names from the dtos module's own output (the types it defines), so there is no hand-maintained list and the imported names are guaranteed to exist in dtos.ts; firepact imports only the ones the Firestore docs actually reference.

tests/integration/test_emit_chain.py keeps both outputs from drifting.