Skip to content

feat: sequential transcation support#30

Merged
jiashengguo merged 2 commits intodevfrom
jiasheng-dev
Mar 29, 2026
Merged

feat: sequential transcation support#30
jiashengguo merged 2 commits intodevfrom
jiasheng-dev

Conversation

@jiashengguo
Copy link
Copy Markdown
Member

@jiashengguo jiashengguo commented Mar 29, 2026

Summary by CodeRabbit

  • New Features

    • Execute sequential batch operations with robust validation and safer handling
    • Automatic model verification and argument validation to reduce errors
    • Improved serialization for more reliable request/response payloads
  • Dependencies

    • Added superjson for advanced data serialization support
  • Chores

    • Package version bumped to 0.4.0

Copilot AI review requested due to automatic review settings March 29, 2026 03:18
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

📝 Walkthrough

Walkthrough

Package version bumped to 0.4.0 and superjson added. New Express POST endpoint /api/model/$transaction/sequential in src/server.ts validates and executes arrays of Prisma operations sequentially inside a transaction, with optional SuperJSON (de)serialization and structured error handling.

Changes

Cohort / File(s) Summary
Package metadata
package.json
Version changed 0.3.00.4.0; added dependency superjson@^2.2.6.
Server: transaction endpoint & helpers
src/server.ts
Added POST /api/model/$transaction/sequential. Validates request body (non-empty array, object items, required model and op, op in VALID_OPS, known model), enforces args shape, lowercases model name, conditionally SuperJSON-deserializes args per model metadata, executes operations sequentially in client.$transaction, returns SuperJSON-serialized results with metadata, logs and returns errors; includes helper utilities for error responses and payload processing.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I nibble on a version bump, so neat,
I plant SuperJSON at my feet,
I line up ops in tidy rows,
They run in order — watch them go!
A hop, a log, the transaction's done. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title mentions 'sequential transaction support' which directly aligns with the main change: a new POST endpoint for sequential transaction processing with SuperJSON integration.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jiasheng-dev

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new HTTP endpoint to execute a list of Prisma model operations sequentially within a single $transaction, with SuperJSON-based payload/response serialization support.

Changes:

  • Added /api/model/$transaction/sequential endpoint to run sequential operations in a Prisma transaction.
  • Introduced SuperJSON (de)serialization support for transaction payloads and responses.
  • Bumped package version and added superjson dependency (plus lockfile updates).

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 5 comments.

File Description
src/server.ts Implements sequential transaction handler, request validation, and new Express route; adds SuperJSON support.
package.json Bumps version to 0.4.0 and adds superjson dependency.
pnpm-lock.yaml Updates lockfile to include superjson and transitive dependencies.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/server.ts (3)

286-293: Consider adding a transaction timeout.

The $transaction call has no timeout configured. Long-running sequential operations could block the connection indefinitely. Prisma supports a timeout option for interactive transactions.

⏱️ Suggested improvement to add timeout
     const clientResult = await client.$transaction(async (tx: any) => {
       const result: any[] = []
       for (const { model, op, args } of processedOps) {
         result.push(await (tx as any)[model][op](args))
       }
       return result
-    })
+    }, { timeout: 30000 }) // 30 second timeout, adjust as needed
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server.ts` around lines 286 - 293, The transaction started with
client.$transaction(async (tx: any) => { ... }) has no timeout; update the call
to use Prisma's interactive transaction options by passing a timeout
(milliseconds) as the second argument (e.g., { timeout: 60000 }) to
client.$transaction so the interactive transaction (the async tx callback that
processes processedOps and invokes (tx as any)[model][op](args)) will abort
after the configured duration and avoid indefinitely blocking the connection.

250-252: Consider limiting the number of operations.

There's no upper bound on the array length. A malicious client could send thousands of operations, potentially causing resource exhaustion. Consider adding a reasonable limit.

🛡️ Suggested improvement
+const MAX_TRANSACTION_OPERATIONS = 100
+
 async function handleTransaction(modelMeta: any, client: any, requestBody: unknown) {
   const processedOps: Array<{ model: string; op: string; args: unknown }> = []
   if (!requestBody || !Array.isArray(requestBody) || requestBody.length === 0) {
     return makeError('request body must be a non-empty array of operations')
   }
+  if (requestBody.length > MAX_TRANSACTION_OPERATIONS) {
+    return makeError(`transaction exceeds maximum of ${MAX_TRANSACTION_OPERATIONS} operations`)
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server.ts` around lines 250 - 252, The request body validation currently
checks only for presence and emptiness but lacks an upper bound; add a constant
MAX_OPERATIONS (e.g., 1000 or a lower value you deem safe) and update the check
that uses requestBody to also validate requestBody.length <= MAX_OPERATIONS,
returning makeError('request body must be a non-empty array of operations and no
more than ' + MAX_OPERATIONS + ' operations') when exceeded; apply this change
where requestBody is validated so callers of the same handler (and any logic
using requestBody later) get the capped input enforced.

345-358: Minor: _req is actually used.

The underscore prefix convention indicates an unused parameter, but _req.body is used on line 355. Rename to req for clarity.

✏️ Suggested rename
-  app.post('/api/model/\\$transaction/sequential', async (_req, res) => {
+  app.post('/api/model/\\$transaction/sequential', async (req, res) => {
     const response = await handleTransaction(
       modelMeta,
       enhanceFunc(
         prisma,
         {},
         {
           kinds: Enhancements,
         }
       ),
-      _req.body
+      req.body
     )
     res.status(response.status).json(response.body)
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server.ts` around lines 345 - 358, The route handler for
app.post('/api/model/\\$transaction/sequential') declares the request parameter
as _req but uses _req.body; rename the parameter to req in the handler signature
and update any uses (e.g., _req.body) to req.body so the code reflects that the
request is used; this affects the anonymous async function parameter and its
reference when calling handleTransaction with req.body and keeps names
consistent for functions like handleTransaction and enhanceFunc calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server.ts`:
- Around line 238-246: processRequestPayload currently destructures its args
(const { meta, ...rest } = args) which will throw if args is null or undefined;
update processRequestPayload to early-return args when it is nullish (undefined
or null) before destructuring, otherwise proceed to pull meta and rest and
perform SuperJSON.deserialize when meta?.serialization is present; ensure the
function signature and callers still accept any | null so the guard prevents the
TypeError and returns the original null/undefined unchanged.

---

Nitpick comments:
In `@src/server.ts`:
- Around line 286-293: The transaction started with client.$transaction(async
(tx: any) => { ... }) has no timeout; update the call to use Prisma's
interactive transaction options by passing a timeout (milliseconds) as the
second argument (e.g., { timeout: 60000 }) to client.$transaction so the
interactive transaction (the async tx callback that processes processedOps and
invokes (tx as any)[model][op](args)) will abort after the configured duration
and avoid indefinitely blocking the connection.
- Around line 250-252: The request body validation currently checks only for
presence and emptiness but lacks an upper bound; add a constant MAX_OPERATIONS
(e.g., 1000 or a lower value you deem safe) and update the check that uses
requestBody to also validate requestBody.length <= MAX_OPERATIONS, returning
makeError('request body must be a non-empty array of operations and no more than
' + MAX_OPERATIONS + ' operations') when exceeded; apply this change where
requestBody is validated so callers of the same handler (and any logic using
requestBody later) get the capped input enforced.
- Around line 345-358: The route handler for
app.post('/api/model/\\$transaction/sequential') declares the request parameter
as _req but uses _req.body; rename the parameter to req in the handler signature
and update any uses (e.g., _req.body) to req.body so the code reflects that the
request is used; this affects the anonymous async function parameter and its
reference when calling handleTransaction with req.body and keeps names
consistent for functions like handleTransaction and enhanceFunc calls.
🪄 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: 9918e838-b7b9-42f7-858a-a983770f8129

📥 Commits

Reviewing files that changed from the base of the PR and between de8e516 and 7a29f10.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (2)
  • package.json
  • src/server.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server.ts`:
- Around line 234-236: The current isValidModel + VALID_OPS checks are
insufficient because inherited keys (e.g. "constructor") can pass and later
cause tx[model][op] to throw; update validation to ensure the model exists as an
own property on modelMeta.models (use
Object.prototype.hasOwnProperty.call(modelMeta.models,
lowerCaseFirst(modelName))) and additionally verify the delegate and operation
are real callable methods by checking that tx[lowerCaseFirst(modelName)] exists
and typeof tx[lowerCaseFirst(modelName)][op] === "function" (and that op is one
of VALID_OPS). Apply these stricter guards wherever tx[model][op] is invoked so
invalid (model, op) pairs return a 400/validation error instead of reaching the
dynamic call and producing a 500.
- Around line 307-311: The catch block handling the "$transaction" request in
src/server.ts currently returns makeError with err.message which can leak
DB/schema details; instead keep the full err (err) in server logs via
console.error/processLogger but return a generic client-facing message (e.g.
"Transaction failed") or map known Prisma error classes
(Prisma.PrismaClientKnownRequestError / error.code like P2002) to curated
messages before calling makeError. Update the catch for the "$transaction"
handler so makeError never includes raw err.message — log the full error and
return only sanitized or mapped messages.
- Around line 274-285: The validation currently allows args === null to pass
through and later crash Prisma; update the check around itemArgs (the branch
that calls makeError) to treat null as invalid so requests with args === null
return a 400. Modify the predicate that inspects itemArgs (used before
processedOps.push and before calling processRequestPayload) to reject null
(e.g., include itemArgs === null or remove the explicit null-allowance) so that
makeError(...) is returned for null/ non-object/array args; keep the rest of the
flow (lowerCaseFirst(itemModel), itemOp, processRequestPayload(itemArgs))
unchanged.
🪄 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: ffef6b58-a308-419e-98b5-c4cf14fe443c

📥 Commits

Reviewing files that changed from the base of the PR and between 7a29f10 and b90551a.

📒 Files selected for processing (1)
  • src/server.ts

@jiashengguo jiashengguo merged commit d4841a3 into dev Mar 29, 2026
3 checks passed
@jiashengguo jiashengguo deleted the jiasheng-dev branch March 29, 2026 04:12
jiashengguo added a commit that referenced this pull request Mar 29, 2026
feat: sequential transcation support (#30)
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