Conversation
|
More templates
@tanstack/angular-db
@tanstack/db
@tanstack/db-ivm
@tanstack/electric-db-collection
@tanstack/offline-transactions
@tanstack/powersync-db-collection
@tanstack/query-db-collection
@tanstack/react-db
@tanstack/rxdb-db-collection
@tanstack/solid-db
@tanstack/svelte-db
@tanstack/trailbase-db-collection
@tanstack/vue-db
commit: |
Contributor
|
Size Change: 0 B Total Size: 92.1 kB ℹ️ View Unchanged
|
Contributor
|
Size Change: 0 B Total Size: 3.7 kB ℹ️ View Unchanged
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains two draft designs for SSR, along with examples of how they work.
SSR_DESIGN.mdwas the first based on the direction from the original issue #545 and draft PR #709.SSR_ALT_DESIGN.mdis taking a step back, noting that live queries are collections, and we need serialisation and scoping for both. It unifies the system.SSR_DESIGN.md -- The Extension Approach
Origin: Extends the direction proposed in issue #545 and draft PR #709, which introduced
prefetchLiveQuery,createServerContext,dehydrate, andHydrationBoundary. The original issue focused on hydratinguseLiveQueryresults by query id, similar to how TanStack Query handles SSR. This design adds request-scoped collections on top of that foundation to solve the data-leakage problem.Core idea: Every server collection declares its scope as
request(default, isolated per request) orprocess(shared across requests for cacheable data). ADbSharedEnvironmentmanages process-scoped singletons viagetOrCreate. ADbRequestScopeholds named collections with declared scopes and tracks prefetched queries. Dehydration is a separate step (dehydrateDbScope) with fine-grained options for which collections and queries to include.API surface: ~12+ primitives --
createDbSharedEnvironment,createDbRequestScope,DbSharedEnvironment,DbRequestScope,prefetchDbQuery,dehydrateDbScope,CollectionSelector,DehydrateDbScopeOptions,CollectionSnapshot,exportSnapshot,importSnapshot,SyncConfig,HydrationBoundary,useHydratedQuery,useHydrateCollections, plus the collection factory pattern (createServerCollections).Serialization model: Two separate systems -- query result dehydration (by query id) and collection snapshot dehydration (opt-in allowlist).
ssr.explicitlySerializedon a query skips auto-marking its source collections as "used," avoiding duplicated payloads. Collection snapshots have their ownexportSnapshot/importSnapshotAPI with sync resume policies (resume-if-possible,truncate,throw,ignore).Strengths: Comprehensive sync resume semantics, detailed security/payload controls, explicit
request/processscope declarations, phased rollout plan with backwards compatibility.Weaknesses: High ceremony -- the collection factory pattern, shared environment setup, scope declaration map, and separate dehydration options add significant boilerplate. Two distinct serialization paths (queries vs collections) with different APIs and different opt-in mechanisms. The developer must understand which path applies when.
SSR_ALT_DESIGN.md -- The Redesign
Origin: A step back from the extension approach, rethinking the problem from first principles. Aims for a smaller, more consistent API where collections and live queries are treated uniformly through the same getter/scope pattern.
Core idea: Two new definition helpers (
defineCollection,defineLiveQuery) produce getter functions that accept an optionalscopeargument. Passingscopebinds the instance to a request lifecycle; omitting it falls back to global/process memoization. Serialization intent is expressed throughscope.include(collection)for collection snapshots andssr: { serializes: true }for live query results. A singlescope.serialize()call produces the complete dehydrated payload, with an automatic pruning step that skips live query payloads when their source collections are already included.API surface: 5 core primitives --
createDbScope,ProvideDbScope,useDbScope,defineCollection,defineLiveQuery. The scope object has 3 methods:include,serialize,cleanup.Serialization model: One unified system. Both collection snapshots and live query results go into a single
DehydratedDbStateV1payload. The pruning logic atserialize()time automatically avoids duplicate data: if a live query's source collections are all included as snapshots, the live query result is omitted (the client re-derives it from the hydrated collections).Scope placement: Two strategies depending on framework. Single root scope (preferred, used in TanStack Start where
createRouter()runs per request) or per-loader scope with nestedProvideDbScopemerge (used in React Router/Remix and Next.js where framework constraints prevent a shared scope). Merge uses timestamp-based freshness to resolve conflicts.Sync resume: Optional
exportSyncMeta/importSyncMetahooks onSyncConfigallow sync layers to export and restore resume metadata via themetafield inDehydratedDbStateV1. Query collections populate theQueryClientcache on import sostaleTimeprevents immediate refetch (always resumable). Electric collections exportseenTxids/seenSnapshotsand a shape offset when available; full resume is blocked in v1 becauseShapeStreamdoes not expose its running offset, but adding agetCurrentOffset()accessor upstream would unblock it with no design change needed here. Live queries always restart from scratch -- the D2 graph has internal state (multiplicity tracking, join buffers) that cannot be serialized, so it must be rebuilt from current source rows. Source resumability affects transition quality: if all sources resume, the D2 first run matches hydrated data seamlessly; if any source restarts, the imported live query result provides stable UI while that source re-syncs. Fallback is always safe: hydrated rows remain as placeholders until fresh sync data overwrites them.Strengths: Small API surface, consistent treatment of collections and live queries, the getter pattern preserves existing global-import usage for non-SSR code, scope is optional so adoption is incremental, automatic payload pruning reduces developer decisions about what to serialize, sync resume is opt-in per sync layer with a safe fallback.
Weaknesses: Scope threading is manual in client components (
useDbScope()+ pass to every getter). Per-loader scope strategy means duplicate server-side fetches when multiple loaders use the same collection. Full Electric resume depends on an upstream@electric-sql/clientAPI addition.Key Differences at a Glance
request/processmap per collectionscope= request, omit = globalcreateServerCollections)defineCollection/defineLiveQuerygettersscope.serialize()with automatic pruningDbSharedEnvironment+getOrCreatescopefrom getter calldehydrateDbScopeoptions +ssr.explicitlySerializedscope.include(collection)+ssr: { serializes: true }resume-if-possible,truncate,throw,ignore)exportSyncMeta/importSyncMetahooks; Query always resumes, Electric best-effort (upstream blocker), live queries always restart D2 from scratchProvideDbScopemerge with timestamp-based freshness