Skip to content

Conversation

KyleAMathews
Copy link
Collaborator

Summary

Context

Manual transactions with local-only and local-storage collections were losing changes because these collections rely on their mutation handlers (onInsert/onUpdate/onDelete) being called to persist data. When operations occur inside tx.mutate(), handlers aren't automatically called - only the outer transaction's mutationFn runs.

Solution

Following the pattern from @tanstack/query-db-collection, both collection types now expose a utils.acceptMutations(transaction, collection) method that users explicitly call in their transaction's mutationFn to persist local mutations:

const localData = createCollection(
  localOnlyCollectionOptions({
    getKey: (item) => item.id,
  })
)

const tx = createTransaction({
  mutationFn: async ({ transaction }) => {
    // Persist local-only mutations
    localData.utils.acceptMutations(transaction, localData)
    
    // Then make API call
    await api.save(...)
  }
})

tx.mutate(() => {
  localData.insert({ id: 1, data: 'metadata' })
  apiCollection.insert({ id: 2, data: 'main data' })
})

await tx.commit()

Changes

  • Added acceptMutations to LocalOnlyCollectionUtils interface
  • Added acceptMutations to LocalStorageCollectionUtils interface
  • Implemented mutation filtering and persistence logic
  • Included JSON serialization validation for local-storage
  • Updated JSDoc with manual transaction examples

Test plan

  • Test manual transactions with local-only collections
  • Test manual transactions with local-storage collections
  • Verify mutations are persisted correctly
  • Verify JSON serialization validation works
  • Verify existing direct operations still work

🤖 Generated with Claude Code

Fixes #446

Local-only and local-storage collections now expose `utils.acceptMutations(transaction, collection)`
that must be called in manual transaction `mutationFn` to persist mutations. This provides explicit
control over when local mutations are persisted, following the pattern established by query-db-collection.

Changes:
- Add acceptMutations to LocalOnlyCollectionUtils interface
- Add acceptMutations to LocalStorageCollectionUtils interface
- Include JSON serialization validation in local-storage acceptMutations
- Update documentation with manual transaction usage examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

changeset-bot bot commented Oct 3, 2025

🦋 Changeset detected

Latest commit: dbfe4c8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
@tanstack/db Patch
@tanstack/angular-db Patch
@tanstack/electric-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch
todos Patch
@tanstack/db-example-react-todo Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

KyleAMathews and others added 3 commits October 3, 2025 17:07
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Replace `unknown` with `Record<string, unknown>` in PendingMutation type
parameters and related generics to satisfy the `T extends object` constraint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link

pkg-pr-new bot commented Oct 6, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@638

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@638

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@638

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@638

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@638

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@638

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@638

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@638

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@638

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@638

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@638

commit: dbfe4c8

Copy link
Contributor

github-actions bot commented Oct 6, 2025

Size Change: +242 B (+0.32%)

Total Size: 75.4 kB

Filename Size Change
./packages/db/dist/esm/local-only.js 891 B +64 B (+7.74%) 🔍
./packages/db/dist/esm/local-storage.js 2.2 kB +178 B (+8.81%) 🔍
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 943 B
./packages/db/dist/esm/collection/changes.js 1.01 kB
./packages/db/dist/esm/collection/events.js 660 B
./packages/db/dist/esm/collection/index.js 3.18 kB
./packages/db/dist/esm/collection/indexes.js 1.16 kB
./packages/db/dist/esm/collection/lifecycle.js 1.8 kB
./packages/db/dist/esm/collection/mutations.js 2.5 kB
./packages/db/dist/esm/collection/state.js 3.82 kB
./packages/db/dist/esm/collection/subscription.js 1.65 kB
./packages/db/dist/esm/collection/sync.js 1.42 kB
./packages/db/dist/esm/deferred.js 230 B
./packages/db/dist/esm/errors.js 3.1 kB
./packages/db/dist/esm/index.js 1.56 kB
./packages/db/dist/esm/indexes/auto-index.js 806 B
./packages/db/dist/esm/indexes/base-index.js 835 B
./packages/db/dist/esm/indexes/btree-index.js 2 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.21 kB
./packages/db/dist/esm/indexes/reverse-index.js 577 B
./packages/db/dist/esm/optimistic-action.js 294 B
./packages/db/dist/esm/proxy.js 3.86 kB
./packages/db/dist/esm/query/builder/functions.js 615 B
./packages/db/dist/esm/query/builder/index.js 4.04 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 938 B
./packages/db/dist/esm/query/compiler/evaluators.js 1.55 kB
./packages/db/dist/esm/query/compiler/expressions.js 631 B
./packages/db/dist/esm/query/compiler/group-by.js 2.04 kB
./packages/db/dist/esm/query/compiler/index.js 2.04 kB
./packages/db/dist/esm/query/compiler/joins.js 2.52 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.21 kB
./packages/db/dist/esm/query/compiler/select.js 1.28 kB
./packages/db/dist/esm/query/ir.js 785 B
./packages/db/dist/esm/query/live-query-collection.js 340 B
./packages/db/dist/esm/query/live/collection-config-builder.js 2.69 kB
./packages/db/dist/esm/query/live/collection-subscriber.js 1.86 kB
./packages/db/dist/esm/query/optimizer.js 3.08 kB
./packages/db/dist/esm/SortedMap.js 1.24 kB
./packages/db/dist/esm/transactions.js 3 kB
./packages/db/dist/esm/utils.js 1.01 kB
./packages/db/dist/esm/utils/browser-polyfills.js 365 B
./packages/db/dist/esm/utils/btree.js 6.01 kB
./packages/db/dist/esm/utils/comparison.js 754 B
./packages/db/dist/esm/utils/index-optimization.js 1.73 kB

compressed-size-action::db-package-size

Copy link
Contributor

github-actions bot commented Oct 6, 2025

Size Change: 0 B

Total Size: 1.47 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 152 B
./packages/react-db/dist/esm/useLiveQuery.js 1.32 kB

compressed-size-action::react-db-package-size

KyleAMathews and others added 2 commits October 6, 2025 16:02
Add LocalOnlyCollectionUtils type parameter to createCollection call to
satisfy type constraints after merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Collaborator

@samwillis samwillis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Conceptional I think this is good, however if this is becoming a generic pattern it shouldn't be on the utils namespace, but on the top level.

Could we just have a collection.acceptMutations(transaction) method that forwards to the relevant onInsert/Update/Delete method when available?

That would work with all collections then.

*/
acceptMutations: (
transaction: { mutations: Array<PendingMutation<Record<string, unknown>>> },
collection: unknown
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you have to pass the collection in here, that seems unnecessary.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I thought I'd gotten rid of all of those. Will remove

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There really should be tests for this new functionality

@KyleAMathews
Copy link
Collaborator Author

however if this is becoming a generic pattern it shouldn't be on the utils namespace, but on the top level.

It's not — this is only intended for collections with no backend. It'd be very odd to do this e.g. for the query/electric collections as then nothing would be written to the backend.

@samwillis
Copy link
Collaborator

samwillis commented Oct 7, 2025

It's not — this is only intended for collections with no backend

But any collection with onInsert/onUpdate/onDelete does have a "backend". So if you have configured those, and now want to use this collection in a transaction with another, if collection.acceptMutations(transaction) was standard they could just do that?

if you try and use it on a collection that does not have the appropriate onInsert/onUpdate/onDelete then it would throw.

@KyleAMathews
Copy link
Collaborator Author

Ok, I see where you're going — letting people rely on their onInsert/onUpdate/onDelete logic even for actions/custom transactions — I'm not a fan of that tbh — the great thing about collection handlers is the logic is very focused/simple as you know it's just coming from collection mutators. So keeping a clean separation between collection direct state changes & everything else is worthwhile. People commonly use api clients & helpers to abstract out any shared logic.

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.

Bug: Transactions do not work when using localOnlyCollection
2 participants