Use `TransferState` to hand server-rendered Firestore data to the browser, instead of reading every document twice
@armando-navarro がすでに取り組んでいます。
2026年8月31日 から。
評価
この issue はまだ評価されていません。
説明
Summary
An Angular app rendering on the server fetches Firestore data, renders the page with it, sends that HTML to the browser, and then fetches the same documents a second time when the app starts.
- Angular already solves this for its own HTTP client. The server's responses go into the page and the browser reads them back instead of repeating the request.
- Nothing does that for Firestore.
TransferStateappears insrc/only in the legacy compat storage pipe. - Apps therefore write the handoff themselves, once per component, or accept the second read.
This proposes an opt-in transfer layer for Firestore reads.
What users hit today
- The server-rendered content disappears for 319 to 506 ms after the app starts, until the browser's own read returns.
- Every document read on the server is read again in the browser, so a page costs two reads per document rather than one.
- The workaround is to write it by hand per component. The sample app in this repo does exactly that in five components, two of them Firestore reads, and #3323 is eight comments of people doing the same thing with mixed results.
What is proposed
An app adds provideFirestoreTransfer() and marks the reads it wants transferred:
docData(ref, { transfer: true })stores the document's values on the server and emits them in the browser as the read's first value, so the rendered content stays on screen.collectionData(query, { transfer: true })stores the query's serialized snapshot (QuerySnapshot.toJSON()), which carries the query itself, and the browser rebuilds a real snapshot from it withquerySnapshotFromJSON.- Reads without the option, and every read in an app that does not add the provider, behave exactly as they do now.
For a query with no limit clause, the browser then attaches its listener with onSnapshotResume, which asks the backend for changes since the server's read rather than for the documents again. That is what removes the second read.
What this will not do
- A query with a
limitclause keeps its second read. Resuming a limited query makes the backend re-send the whole result set, which measured worse than not transferring at all, so the library will transfer the data for the first paint and attach an ordinary listener. Whether resume can support limited queries is a question for the Firestore team. - Aggregate reads such as
getCountFromServercannot participate, because their results have no public serialized form. - A read of a document that does not exist is skipped. There is no value to put in the page, and Firestore's serialized form for a missing document throws when the browser tries to rebuild it.
- A
limitToLastquery is skipped. The serialized query does not record that the limit counted from the end, so the browser would rebuild a query that no longer matches the one the app is running, and the documents would come back in the wrong order. - Data read during a server render that is signed in as the visitor belongs to that visitor, and putting it in the HTML is a hazard wherever that HTML gets cached. The library will warn in development when this happens, and the documentation will cover it.
Earlier attempts in this repo
- #1225 asked for this in 2017.
- #2018 built an experimental version in 2019 and was closed in favor of exploring Firestore bundles, which is the serialized form the query half of this proposal now uses.
- #1547 was the same idea for the Realtime Database and was also left unfinished.
The same gap exists outside Firestore
The gap is library-wide rather than Firestore-only. Firestore is proposed first for three reasons:
- Its values include types that plain JSON loses, such as
TimestampandDocumentReference. - Its queries carry no obvious identity for the browser to match a transferred value against.
- Its reads are billed per document, so the second read has a direct cost.
Realtime Database and Storage are simpler on all three counts, since their values are already JSON or a plain string and the path identifies the read. Both should be able to reuse whatever this builds, and each is worth its own PR rather than being folded into this one.
If you are hitting this today, saying which reads and whether your queries are paginated would help, since the paginated case is the one with an open constraint above.
- 主要言語
- TypeScript
- スター
- 7.8k
- フォーク
- 2.2k
- 平均マージ
- 22時間 34分
- マージ済み PR(30日)
- 4
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
angular/angularfire のほかの issue
-
Six `firebase` entry points have no `@angular/fire` equivalent, so their exports are unreachable オープンcomp: core type: feature
angular/angularfire#3755 · 担当者 1 名 ·
-
comp: auth comp: ssr comp: zones type: bug
難易度 3/5 1〜2日 初心者へのやさしさ 76/100
angular/angularfire#3748 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 35/100
angular/angularfire#3737 · コメント 1 件 · リアクション 22 件 ·
-
難易度 4/5 3〜5日 初心者へのやさしさ 52/100
angular/angularfire#3715 ·
-
comp: build/pipeline type: RFC / discussion / question
難易度 5/5 1週間以上 初心者へのやさしさ 25/100
angular/angularfire#3699 · コメント 13 件 · リアクション 4 件 ·
angular/angularfire の issue をすべて見る
似ている issue
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
dennys-bd/agent-hive#184 ·
-
Add: hunch オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
AbdelStark/awesome-typesafe#104 ·
-
ai-observability bug team/ai-observability
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
vicharanashala/fln#563 ·