feat(tanstack-query): add useTransaction hook for sequential transactions#2637
Conversation
…ions Adds `$transaction.useSequential()` to the hooks returned by `useClientQueries()` across React, Vue, and Svelte. The hook POSTs an array of operations to the server's `$transaction/sequential` endpoint and automatically invalidates all queries affected by the operations on success. Shared mutation logic (URL building, per-op invalidation) is extracted into `src/common/transaction.ts` to avoid duplication across frameworks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdded typed sequential transaction support: new TransactionOperation type and TRANSACTION_ROUTE_PREFIX, transaction mutation/+onSuccess helpers, per-framework useSequential hooks (React/Svelte/Vue), endpoint normalization, model-name normalization for invalidation, small behavioral change in nested-write visitor, and React tests for transaction invalidation behavior. ChangesSequential Transaction Support
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/clients/tanstack-query/src/common/transaction.ts`:
- Around line 39-42: The transaction wrapper assumes args[1] is an array of
TransactionOperation and iterates it; guard against malformed calls by
validating args[1] before the loop in the returned async function: check if
Array.isArray(args[1]) and that each item matches the expected shape (or at
minimum default to an empty array / bail out so onSuccess still runs). Update
the code around the variables assignment and the for-loop that calls
createInvalidator(op.model, op.op, schema, invalidateFunc, logging) to set const
variables = Array.isArray(args[1]) ? args[1] as TransactionOperation[] : [] (or
return early), and add a lightweight per-item type check before using
op.model/op.op to avoid runtime exceptions.
- Line 14: The URL construction in the transaction code uses const reqUrl =
`${endpoint}/${TRANSACTION_ROUTE_PREFIX}/sequential` and will produce a double
slash if endpoint ends with '/', so normalize endpoint before appending the
transaction path: trim any trailing slashes from the endpoint variable (or use a
small helper like normalizeEndpoint) and then build reqUrl using the cleaned
value and TRANSACTION_ROUTE_PREFIX so you always get a single slash between
segments.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8c46c6c5-15cc-468e-adc5-97294df5f46b
📒 Files selected for processing (7)
packages/clients/tanstack-query/src/common/constants.tspackages/clients/tanstack-query/src/common/transaction.tspackages/clients/tanstack-query/src/common/types.tspackages/clients/tanstack-query/src/react.tspackages/clients/tanstack-query/src/svelte/index.svelte.tspackages/clients/tanstack-query/src/vue.tspackages/clients/tanstack-query/test/react-query.test.tsx
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
Wow, great feature, exactly what we need. We can avoid creating custom transactions on the backend with that. Do you think it’s manageable to pass results from one step to another? For example, passing a created ID to link a record or something similar? Could we even mix queries with mutations? |
Happy to know it's helpful to you. Unfortunately the hooks (and the underlying RPC server implementation) is a simple wrapper around the ORM client's sequential transaction feature - which doesn't support passing data between steps. Having that would require a more sophisticated representation. Mixing queries with mutations is supported, however its utility is very limited since you can't pass data between steps ... |
…alize model name lookup - Make `TransactionOperation<Schema>` a discriminated union over (model, op) pairs with schema-derived args types - Normalize incoming mutation model name via case-insensitive schema lookup so lowerCaseFirst names resolve - Guard transaction onSuccess against malformed variables and per-item shape - Move `normalizeEndpoint` from constants.ts to client.ts - Group react-query tests into CRUD/Optimistic/Sequential describe blocks Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/clients/tanstack-query/src/common/types.ts`:
- Around line 157-165: TransactionOperation currently makes args optional for
every operation which allows required payloads to be omitted; change the mapped
branch so operations with no args keep a two-field shape while operations that
require args require the args field. Update the inner mapping over
AllowedTransactionOps in TransactionOperation to use a conditional type on
CrudArgsMap<Schema, Model>[Op] — e.g. for each Op produce either { model: Model;
op: Op } when CrudArgsMap<...>[Op] extends undefined, or { model: Model; op: Op;
args: CrudArgsMap<Schema, Model>[Op] } when args are required — referencing
TransactionOperation, GetModels, AllowedTransactionOps and CrudArgsMap to locate
and modify the code.
In `@packages/clients/tanstack-query/src/svelte/index.svelte.ts`:
- Around line 725-733: The transaction invalidation currently passes every
step's op.op into createInvalidator, causing read-only TransactionOperation
types (findMany, count, aggregate, groupBy, exists) to be treated as mutations;
update makeTransactionOnSuccess to filter out read-only steps before calling
createInvalidator by checking each step's op.op against the read-only set (e.g.,
'findMany','count','aggregate','groupBy','exists') and skip invalidation for
those entries so only write ops reach createInvalidator; keep existing behavior
for write ops and preserve calling the original optionsValue?.onSuccess.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1a4a7a3d-a35c-4174-b53f-421ba4cd420e
📒 Files selected for processing (8)
packages/clients/client-helpers/src/invalidation.tspackages/clients/client-helpers/src/nested-write-visitor.tspackages/clients/tanstack-query/src/common/transaction.tspackages/clients/tanstack-query/src/common/types.tspackages/clients/tanstack-query/src/react.tspackages/clients/tanstack-query/src/svelte/index.svelte.tspackages/clients/tanstack-query/src/vue.tspackages/clients/tanstack-query/test/react-query.test.tsx
💤 Files with no reviewable changes (1)
- packages/clients/client-helpers/src/nested-write-visitor.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/clients/tanstack-query/src/common/transaction.ts
- packages/clients/tanstack-query/src/vue.ts
- packages/clients/tanstack-query/src/react.ts
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
…it react tests - Make `args` required on TransactionOperation only for ops whose args type has required fields; keep it optional for read ops with all-optional args - Skip read-only ops in transaction onSuccess so they don't reach createInvalidator - Split the 2k-line react-query test file into per-feature files under test/react/, adding shared helpers - Add typing tests for args optionality and delegate-model rejection in the sequential transaction suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Claude finished @ymc9's task —— View job Code Review Complete ✅I've thoroughly reviewed the Review Checklist
✅ Code Quality & Best PracticesExcellent architectural decisions:
Code organization is exemplary:
✅ Security & SafetyNo security concerns identified:
✅ Performance ConsiderationsWell-optimized implementation:
✅ Test CoverageComprehensive test coverage in
✅ Architecture & Type SafetyExcellent type definitions:
✅ Framework ConsistencyUniform implementation across frameworks:
Minor Observations
Verdict: APPROVED 🚀This is a high-quality implementation that:
The shared logic extraction is particularly well done, and the integration with existing invalidation mechanisms is seamless. No changes required. |
Summary
$transaction.useSequential()to theClientHookstype returned byuseClientQueries(), available for React, Vue, and Svelte{ model, op, args }operations to the server's existing$transaction/sequentialendpoint and automatically invalidates all queries affected by the operations on successsrc/common/transaction.tsto avoid duplication across frameworksUsage
Pass
{ invalidateQueries: false }to opt out of automatic invalidation.Test plan
works with sequential transaction and invalidation— verifies both affected models are refetched after the transactionworks with sequential transaction and no invalidation— verifies the cache is untouched wheninvalidateQueries: false🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests
Chores
Fixes #2405.