Skip to content

Add TanStack DB example with Rivet Actor SQLite integration#4427

Open
jog1t wants to merge 3 commits intomainfrom
claude/rivetkit-tanstack-poc-7xRx3
Open

Add TanStack DB example with Rivet Actor SQLite integration#4427
jog1t wants to merge 3 commits intomainfrom
claude/rivetkit-tanstack-poc-7xRx3

Conversation

@jog1t
Copy link
Contributor

@jog1t jog1t commented Mar 15, 2026

Description

This PR adds a new example demonstrating how to integrate TanStack DB with Rivet Actors for real-time collaborative applications. The example showcases a todo list application with SQLite persistence in the actor, real-time broadcast to all connected clients, and reactive live queries in React.

Key Features

  • SQLite persistence: Todos are durably stored in the actor's SQLite database via rivetkit/db, surviving server restarts and actor hibernation
  • Real-time broadcast: Every mutation (add/toggle/delete) is broadcast to all connected clients via a change event, eliminating the need for polling
  • Reactive live queries: TanStack DB's differential dataflow engine re-evaluates only affected rows, providing sub-millisecond query updates
  • Optimistic mutations: Local mutations update the UI instantly and automatically reconcile with server state when the actor broadcasts the confirmed change
  • Multi-tab sync: Open multiple browser tabs to see changes sync in real time across all clients

Implementation Details

Actor (src/actors.ts)

  • Defines a todoList actor with SQLite-backed state management
  • Implements four actions: getTodos, addTodo, toggleTodo, deleteTodo
  • Each write action broadcasts a typed TodoChange delta to all connected clients
  • Includes schema migration for the todos table

TanStack DB Bridge (frontend/collection.ts)

  • Creates a todoCollection with custom sync callbacks wired to the actor
  • initCollection() fetches all existing todos and seeds the collection on connect
  • applyChange() applies incremental deltas from actor broadcast events
  • Implements onInsert, onUpdate, onDelete handlers to sync local mutations back to the actor

React App (frontend/App.tsx)

  • Uses useActor hook to connect to the actor
  • Implements filter tabs (All/Active/Completed) with live query counts
  • Demonstrates optimistic mutations with immediate UI updates
  • Shows connection status badge and loading state

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

The example can be tested by:

  1. Running pnpm install && pnpm dev
  2. Opening http://localhost:5173 in a browser
  3. Adding, toggling, and deleting todos
  4. Opening a second tab to verify real-time sync across clients
  5. Refreshing the page to verify SQLite persistence

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (README.md included)
  • My changes generate no new warnings

https://claude.ai/code/session_01DJGfxUoy4Sy5B55bP8oXFj

Adds a new example that integrates TanStack DB with a RivetKit actor
backed by SQLite. Demonstrates:

- SQLite persistence via rivetkit/db with live migration
- Real-time change events broadcast to all connected clients
- Custom TanStack DB sync function seeded from actor's getTodos() action
- Incremental delta updates via begin/write/commit for each broadcast event
- Optimistic mutations (insert/toggle/delete) with automatic reconciliation
- Reactive live queries with sub-millisecond differential dataflow updates
- Filter tabs (All / Active / Completed) as live query examples

https://claude.ai/code/session_01DJGfxUoy4Sy5B55bP8oXFj
@railway-app
Copy link

railway-app bot commented Mar 15, 2026

🚅 Deployed to the rivet-pr-4427 environment in rivet-frontend

Service Status Web Updated (UTC)
website ❌ Build Failed (View Logs) Web Mar 15, 2026 at 6:39 pm
frontend-inspector ❌ Build Failed (View Logs) Web Mar 15, 2026 at 3:49 pm
frontend-cloud ❌ Build Failed (View Logs) Web Mar 15, 2026 at 3:48 pm
mcp-hub ✅ Success (View Logs) Web Mar 15, 2026 at 3:29 pm
ladle ❌ Build Failed (View Logs) Web Mar 15, 2026 at 3:28 pm

@claude
Copy link

claude bot commented Mar 15, 2026

Code Review

Nice work on the TanStack DB integration. The concept is solid and the example demonstrates the pattern well. Here are the issues I found:


Bugs / Correctness

Race condition during initial seeding (rivetkit-typescript/packages/tanstack-db-collection/src/mod.ts)

The onOpen handler is async with a void wrapper. If a changeEvent broadcast fires while getInitial() is awaiting, the change will be applied to TanStack DB before the initial seed is committed and markReady() is called, which can produce duplicate or out-of-order rows. A standard fix is to buffer incoming change events while seeding is in progress and replay them after markReady().

Silent error swallowing in onOpen

If getInitial() throws, the void (async () => {})() wrapper silently discards the error. The collection stays unready forever with no signal to the caller. The error should be surfaced via a callback or rethrown in a way TanStack DB can observe.

toggleTodo returns undefined if ID is missing (examples/tanstack-db/src/actors.ts)

Add a guard: if (!rows[0]) throw new Error("Todo not found"). Without it, the actor broadcasts undefined and returns undefined typed as Todo.

onUpdate always calls toggleTodo regardless of the actual change (examples/tanstack-db/frontend/collection.ts)

This works for the current UI but is fragile. Any future code path that calls todoCollection.update() for a non-toggle reason will silently toggle instead. Consider passing the target completed value explicitly or exposing a setCompleted action.


Missing Requirements (per CLAUDE.md)

No Vercel example generated - CLAUDE.md states that when adding new examples the vercel equivalent must also be updated. The package.json does not have skipVercel: true, so run ./scripts/vercel-examples/generate-vercel-examples.ts to generate it.

README missing required sections - The example template requires a Resources section with links to relevant docs and a License section. The Implementation section should also include GitHub source links to key files.


Code Quality

type AnyConn = any (mod.ts) - This alias bypasses type safety throughout the implementation. Given ActorConnRaw is already imported from rivetkit/client, TConn should be bounded more tightly. AnyConn should not be used as the default for the TConn generic.

write type cast (mod.ts) - write(change as Parameters<typeof write>[0]) suggests a structural mismatch between RivetChangeMessage and what TanStack DB's write expects. Resolve at the type level rather than casting.

package-lock.json committed - This repo uses pnpm (per CLAUDE.md). Committing an npm lockfile into a pnpm workspace is inconsistent. Remove it.

turbo.json missing build dependency - @rivetkit/tanstack-db-collection#build should be listed in dependsOn since the example depends on this package at build time.

Version numbers - "version": "2.0.21" for the example and "version": "2.1.6" for the new package look like auto-assigned workspace versions for brand-new packages. Confirm these are intentional.


Testing

No tests are included for @rivetkit/tanstack-db-collection. Given the race condition surface area in the sync lifecycle (seed vs. live deltas), a driver test covering the seed-then-delta path would be valuable before this ships as a published package.

Introduces `@rivetkit/tanstack-db-collection` — a collection options
creator for TanStack DB backed by Rivet Actors, modeled after
`electricCollectionOptions` and `trailBaseCollectionOptions`.

`rivetCollectionOptions()` encapsulates the full sync lifecycle:
- Establishes the actor connection via a typed `getHandle` factory.
- Seeds the TanStack DB collection from `getInitial` once connected.
- Applies real-time deltas from the actor's broadcast event.
- Routes optimistic mutations back to the actor via `onInsert`,
  `onUpdate`, and `onDelete` handlers that receive the live connection.

The `examples/tanstack-db` example is updated to use the new package,
removing all manual sync boilerplate (module-level globals, the
`initCollection`/`applyChange` helpers, `useActor` hook wiring, and
the manual `useEffect` for seeding). The `App.tsx` now only consumes
the collection via `useLiveQuery` and `todoCollection.status`.

https://claude.ai/code/session_01DJGfxUoy4Sy5B55bP8oXFj
Replaces inline React.CSSProperties style objects with Tailwind utility
classes. Uses @tailwindcss/vite for zero-config Vite integration and
defines custom theme tokens (--color-rivet, --color-surface, etc.) via
@theme in index.css to match the project's dark color palette.

https://claude.ai/code/session_01DJGfxUoy4Sy5B55bP8oXFj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants