Conversation
📝 WalkthroughWalkthroughPackage version bumped to 0.4.0 and Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
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/sequentialendpoint to run sequential operations in a Prisma transaction. - Introduced SuperJSON (de)serialization support for transaction payloads and responses.
- Bumped package version and added
superjsondependency (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.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
src/server.ts (3)
286-293: Consider adding a transaction timeout.The
$transactioncall has no timeout configured. Long-running sequential operations could block the connection indefinitely. Prisma supports atimeoutoption 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:_reqis actually used.The underscore prefix convention indicates an unused parameter, but
_req.bodyis used on line 355. Rename toreqfor 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
package.jsonsrc/server.ts
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
src/server.ts
feat: sequential transcation support (#30)
Summary by CodeRabbit
New Features
Dependencies
Chores