fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(orm): make orderBy nulls optional - #2670

Merged
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional
May 13, 2026
Merged

fix(orm): make orderBy nulls optional#2670
ymc9 merged 3 commits into
devfrom
fix/orderby-nulls-optional

Conversation

@ymc9

@ymc9ymc9 commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Prisma's SortOrderInput defines nulls as optional, but ZenStack's Zod schema required it, so valid inputs like orderBy: { name: { sort: 'asc' } } were rejected by validation before reaching the dialect.
  • Make nulls optional in the orderBy Zod schema.
  • Update applyScalarOrderBy in the base dialect to handle the nulls-less object form by falling back to query.orderBy(fieldRef, sort).

Test plan

  • Added a regression case in tests/e2e/orm/client-api/find.test.ts covering orderBy: { name: { sort: 'desc' } }.
  • pnpm vitest run orm/client-api/find.test.ts — all 18 tests pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • orderBy now allows omitting the nulls option for optional scalar fields.
  • Chores

    • Filter options updated: threshold is now optional and unaccent is required.
    • OpenAPI baseline updated to reflect adjusted ordering/nulls schema branches.
  • Tests

    • Added test asserting sorting behavior when nulls is omitted.

Review Change Stack

Prisma's `SortOrderInput` defines `nulls` as optional, but ZenStack's
Zod schema required it, causing valid inputs like
`orderBy: { name: { sort: 'asc' } }` to be rejected by validation.
- Make `nulls` optional in the orderBy Zod schema
- Handle the `nulls`-less object form in `applyScalarOrderBy`
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitaiBot commented May 13, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Makes nulls optional for scalar orderBy objects in the zod schema and dialect: the schema omits nulls, the dialect falls back to field ordering when nulls is absent, updates FuzzyFilterOptions, adds an e2e test case, and reformats OpenAPI baseline ordering sections.

Changes

Optional nulls in orderBy

Layer / File(s)Summary
Schema generation with optional nulls
packages/orm/src/client/zod/factory.ts
makeOrderBySchema now emits orderBy objects where nulls is optional for scalar fields.
Dialect implementation for optional nulls
packages/orm/src/client/crud/dialects/base-dialect.ts
applyScalarOrderBy keys on value.sort and applies NULLS FIRST/LAST only when value.nulls is exactly 'first' or 'last'; otherwise it orders by the field using the computed sort.
Type updates and test verification
packages/orm/src/client/crud/dialects/base-dialect.ts, tests/e2e/orm/client-api/find.test.ts
FuzzyFilterOptions now has threshold?: number and unaccent: boolean; e2e test adds a case asserting orderBy: { name: { sort: 'desc' } } returns a defined result.
OpenAPI baseline reformatting
packages/server/test/openapi/baseline/rpc.baseline.yaml
Reformats multiple nulls/required sections in User/Post orderBy input schema blocks (placement of const: last and required: - sort).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through schemas, tidy and spry,
Took nulls off the path and let sorts fly,
Tests gave a nod, the dialect agreed,
OpenAPI tidied each ordering seed,
A rabbit's small fix — quick, neat, and spry.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title 'fix(orm): make orderBy nulls optional' accurately describes the primary change: making the nulls field optional in orderBy schemas, which aligns with the Prisma SortOrderInput definition and the main code changes throughout the PR.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.

✏️ 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 fix/orderby-nulls-optional

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.

@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1525: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1525)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. The changes are exactly what's needed - no more, no less.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1240-1246: The conditional that checks sort on the incoming value
can throw when value is null; update the if-condition in the method in
BaseDialect to guard against null by adding value !== null before using 'in' and
property accesses (so the branch that calls this.negateSort(value.sort, negated)
and either this.buildOrderByField(query, fieldRef, sort, value.nulls) or
query.orderBy(fieldRef, sort) only runs when value is not null). Locate the
block that references negateSort, buildOrderByField, and query.orderBy and
modify the predicate to include value !== null while preserving existing checks
for value.sort and value.nulls.
🪄 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: a29adcc7-1247-43d3-8f21-e9b2be235c8d

📥 Commits

Reviewing files that changed from the base of the PR and between e492c93 and 5bbfbba.

📒 Files selected for processing (3)
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • tests/e2e/orm/client-api/find.test.ts

Comment threadpackages/orm/src/client/crud/dialects/base-dialect.ts
@ymc9ymc9 changed the title fix(orm): make orderBy nulls optional to match Prisma's SortOrderInputfix(orm): make orderBy nulls optionalMay 13, 2026
@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toMatchObject({email: 'u2@test.com'});
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 177, 313, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@claude

claudeBot commented May 13, 2026

Copy link
Copy Markdown
Contributor

Claude finished @ymc9's task —— View job


Code Review: Make orderBy nulls optional

Tasks:

  • Read and analyze changed files
  • Review Zod schema changes in factory.ts
  • Review dialect implementation in base-dialect.ts
  • Review test coverage in find.test.ts
  • Check for potential bugs or edge cases
  • Provide overall feedback and recommendations

Analysis

This PR successfully addresses a validation mismatch between ZenStack and Prisma's SortOrderInput type. The changes are well-targeted and maintain backward compatibility.

Strengths

  1. Correct Problem Identification: The issue correctly identifies that Prisma's SortOrderInput makes nulls optional, but ZenStack's Zod validation was requiring it.

  2. Minimal, Focused Changes:

    • factory.ts:1363: Simply adds .optional() to the nulls field in the Zod schema
    • base-dialect.ts:1244-1246: Properly handles the case where nulls is undefined by falling back to basic query.orderBy(fieldRef, sort)
  3. Backward Compatibility: Existing code using { sort: 'asc', nulls: 'first' } continues to work unchanged.

  4. Test Coverage: Added regression test in find.test.ts:150-155 covering the { sort: 'desc' } syntax without nulls.

🔍 Code Quality Assessment

factory.ts (packages/orm/src/client/zod/factory.ts:1363)

nulls: z.union([z.literal('first'),z.literal('last')]).optional()
  • Clean, straightforward change that aligns with Prisma's type definition
  • Maintains type safety while allowing optional nulls

base-dialect.ts (packages/orm/src/client/crud/dialects/base-dialect.ts:1244-1246)

if(value.nulls==='first'||value.nulls==='last'){returnthis.buildOrderByField(query,fieldRef,sort,value.nulls);}else{returnquery.orderBy(fieldRef,sort);}
  • Proper defensive programming - explicitly checks for nulls presence
  • Graceful fallback to standard ordering when nulls is undefined
  • Uses existing, well-tested code paths

find.test.ts (tests/e2e/orm/client-api/find.test.ts:150-155)

// object sort without nullsawaitexpect(client.user.findFirst({orderBy: {name: {sort: 'desc'}},}),).resolves.toBeDefined();
  • Tests the exact use case mentioned in the issue
  • Integration test verifies end-to-end functionality

🚀 Performance Considerations

  • No impact: Changes are purely structural/validation-related
  • Falls back to standard Kysely orderBy() method when nulls is omitted
  • No additional overhead introduced

🔒 Security Assessment

  • Low risk: Changes are limited to schema validation relaxation
  • No user input handling changes
  • Maintains existing validation for when nulls is provided

⚠️Potential Issue Found

Critical Bug in base-dialect.ts:1240: The conditional check has a potential null pointer exception:

if(typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

Problem: typeof null === 'object' returns true, but the in operator throws a TypeError when used on null. This pattern appears throughout the codebase with proper null guards (see lines 313, 1399, 1408, 1809), but line 1240 is missing the null check.

Fix needed: Add value !== null to the condition:

if(value!==null&&typeofvalue==='object'&&'sort'invalue&&(value.sort==='asc'||value.sort==='desc')){

🎯 Additional Considerations

  1. Database-Specific Behavior: When nulls is omitted, ordering behavior becomes database-dependent:

    • PostgreSQL: NULLs typically sort after non-NULLs in ASC, before in DESC
    • SQLite: NULLs sort before non-NULLs regardless of sort direction
    • This matches Prisma's behavior, so it's correct, but users should be aware
  2. Documentation: Consider updating documentation to clarify that nulls is optional and explain default behavior across databases.

📊 Test Coverage Assessment

Excellent coverage for the core functionality:

  • ✅ Basic { sort: 'desc' } syntax (new test)
  • ✅ Full { sort: 'asc', nulls: 'first' } syntax (existing tests at lines 137-148)
  • ✅ String syntax 'asc'/'desc' (existing tests)

Could be enhanced with:

  • Cross-database behavior verification for null ordering
  • Edge cases with computed/virtual fields

🎯 Recommendation: APPROVE with Minor Fix

This is a well-implemented fix that:

  • ✅ Solves the immediate validation issue
  • ✅ Maintains full backward compatibility
  • ✅ Follows established patterns in the codebase
  • ✅ Includes appropriate test coverage
  • ✅ Aligns ZenStack behavior with Prisma's specification

The implementation is clean, minimal, and follows defensive programming principles. Please address the null check issue in base-dialect.ts:1240 before merging.


@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/orm/client-api/find.test.ts`:
- Around line 150-155: The test currently asserts that
client.user.findFirst({...}).resolves.toBeDefined(), which would incorrectly
pass if the result is null; replace the assertion with the stronger custom
matcher toResolveTruthy() so the promise must resolve to a truthy user. Locate
the call to client.user.findFirst with orderBy: { name: { sort: 'desc' } } and
change the expectation from .resolves.toBeDefined() to .toResolveTruthy() to
match the suite's pattern.
🪄 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: af073876-324a-4f95-a928-a226553258bd

📥 Commits

Reviewing files that changed from the base of the PR and between 7e1a9f6 and e275f62.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/find.test.ts

Comment threadtests/e2e/orm/client-api/find.test.ts
@ymc9
ymc9 merged commit 026450b into devMay 13, 2026
10 checks passed
@ymc9
ymc9 deleted the fix/orderby-nulls-optional branch May 13, 2026 19:13
Sign up for freeto 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.

1 participant

@ymc9