Skip to content

fix: Strip undefined values in ParseServerRESTController to match HTTP mode#10269

Open
yog27ray wants to merge 4 commits intoparse-community:alphafrom
yog27ray:fix/directaccess-undefined-to-null
Open

fix: Strip undefined values in ParseServerRESTController to match HTTP mode#10269
yog27ray wants to merge 4 commits intoparse-community:alphafrom
yog27ray:fix/directaccess-undefined-to-null

Conversation

@yog27ray
Copy link
Contributor

@yog27ray yog27ray commented Mar 21, 2026

Issue

When directAccess: true is enabled, undefined values in update (PUT) payloads are converted to null and stored in the database. This breaks doesNotExist / exists queries and changes data semantics.

Root cause: ParseServerRESTController passes request data directly to Parse Server internals without JSON serialization. In HTTP mode, JSON.stringify() naturally strips undefined values from payloads. With directAccess, undefined values are preserved all the way to transformUpdate() in MongoTransform.js, which passes them into MongoDB's $set operator. The MongoDB BSON driver then converts undefined to null.

The CREATE path is unaffected because parseObjectToMongoObjectForCreate() already has an if (value !== undefined) check (MongoTransform.js line 476). The UPDATE path in transformUpdate() lacks this check (line 529).

Approach

Added a stripUndefined() helper in ParseServerRESTController.js that removes keys with undefined values from the request body before passing it to the router. This makes directAccess behave identically to HTTP mode, where JSON.stringify() strips undefined values.

Why fix here instead of transformUpdate(): Without directAccess, JSON.stringify strips undefined before data ever reaches transformUpdate() — so transformUpdate never sees undefined values in HTTP mode and works fine. The bug only exists because directAccess bypasses JSON serialization. Fixing at the ParseServerRESTController level — the component that's added when directAccess is enabled — addresses the root cause at the right layer.

Reproduction:

// With directAccess: true
const obj = new Parse.Object('MyClass');
await obj.save({ name: 'test' });

// Update with undefined value
await obj.save({
  name: 'updated',
  questionId: anotherObj.get('nonExistentField'), // returns undefined
});

// Expected (HTTP mode): questionId is not set, get() returns undefined
// Actual (directAccess): questionId is stored as null, get() returns null
// doesNotExist('questionId') query won't match this object

Tasks

  • Add tests
  • Add changes to documentation (guides, repository pages, code comments)
  • Add security check

Summary by CodeRabbit

  • Bug Fixes

    • REST API request handling updated so undefined fields in update payloads are preserved: fields set as undefined remain absent/undefined in subsequent responses instead of being converted to null, preventing unintended data changes.
  • Tests

    • Added an integration test that verifies undefined field values are correctly preserved across update and fetch operations via the REST API.

…TTP mode behavior

When `directAccess: true` is enabled, `ParseServerRESTController` passes
request data directly to Parse Server internals without JSON serialization.
In HTTP mode, `JSON.stringify()` naturally strips `undefined` values from
payloads. With directAccess, `undefined` values are preserved, passed to
MongoDB's `$set` operator, and converted to `null` by the BSON driver.

This causes fields that should be absent to be stored as `null`, breaking
`doesNotExist` queries and changing data semantics.

The fix adds a `stripUndefined()` helper that removes keys with `undefined`
values from the request body, making directAccess behave identically to
HTTP mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@parse-github-assistant
Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title fix: strip undefined values in ParseServerRESTController to match HTTP mode fix: Strip undefined values in ParseServerRESTController to match HTTP mode Mar 21, 2026
@parse-github-assistant
Copy link

parse-github-assistant bot commented Mar 21, 2026

🚀 Thanks for opening this pull request! We appreciate your effort in improving the project. Please let us know once your pull request is ready for review.

Tip

  • Keep pull requests small. Large PRs will be rejected. Break complex features into smaller, incremental PRs.
  • Use Test Driven Development. Write failing tests before implementing functionality. Ensure tests pass.
  • Group code into logical blocks. Add a short comment before each block to explain its purpose.
  • We offer conceptual guidance. Coding is up to you. PRs must be merge-ready for human review.
  • Our review focuses on concept, not quality. PRs with code issues will be rejected. Use an AI agent.
  • Human review time is precious. Avoid review ping-pong. Inspect and test your AI-generated code.

Note

Please respond to review comments from AI agents just like you would to comments from a human reviewer. Let the reviewer resolve their own comments, unless they have reviewed and accepted your commit, or agreed with your explanation for why the feedback was incorrect.

Caution

Pull requests must be written using an AI agent with human supervision. Pull requests written entirely by a human will likely be rejected, because of lower code quality, higher review effort and the higher risk of introducing bugs. Please note that AI review comments on this pull request alone do not satisfy this requirement.

@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

Added a recursive stripUndefined helper that omits object properties with undefined and converts undefined in arrays to null; used it to build outbound request.body in request handling. Added an integration test that updates an object with absentField: undefined and verifies the field remains omitted on GET.

Changes

Cohort / File(s) Summary
Test Coverage
spec/ParseServerRESTController.spec.js
Added integration test: create MyObject with presentField, PUT update including absentField: undefined, then GET to assert absentField is not present in the response.
REST Request Processing
src/ParseServerRESTController.js
Added stripUndefined(obj) helper that recursively omits object properties with undefined and converts undefined in arrays to null; replaced direct request body usage with stripUndefined(data) when constructing outbound request.body in handleRequest.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding a stripUndefined fix in ParseServerRESTController to align directAccess behavior with HTTP mode for undefined values.
Description check ✅ Passed The PR description is comprehensive, well-structured, and covers all required template sections with substantial detail about the issue, root cause, approach, and reproduction steps.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Mar 21, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
spec/ParseServerRESTController.spec.js (1)

679-695: Add one nested undefined regression case to close the parity gap.

This test is good for top-level fields. Please add a case like { nested: { absentField: undefined } } to ensure nested updates also match HTTP behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/ParseServerRESTController.spec.js` around lines 679 - 695, Add a
nested-undefined regression case to the existing test "should not convert
undefined values to null on update with directAccess" in
ParseServerRESTController.spec.js: after the current PUT that includes
absentField: undefined, also perform a PUT (or include in the same update) with
a nested object like { nested: { absentField: undefined } } and then GET the
object and assert that getRes.nested is defined, getRes.nested.absentField is
undefined, and that 'absentField' is not an own property of getRes.nested;
update expectations in the test to cover both top-level and nested undefined
behavior so nested updates mirror HTTP behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ParseServerRESTController.js`:
- Around line 32-43: The stripUndefined function currently only removes
undefined at the top level; update stripUndefined to recursively traverse
objects and arrays so its behavior matches JSON.stringify: for objects (function
stripUndefined) iterate keys and if a value is undefined omit the key, otherwise
set key to stripUndefined(value); for arrays return a new array mapping each
element to stripUndefined(element) but convert undefined elements to null (i.e.,
if element === undefined return null); preserve primitives and null as-is and
avoid mutating the original input. Ensure the function handles nested
objects/arrays and returns a fresh structure.

---

Nitpick comments:
In `@spec/ParseServerRESTController.spec.js`:
- Around line 679-695: Add a nested-undefined regression case to the existing
test "should not convert undefined values to null on update with directAccess"
in ParseServerRESTController.spec.js: after the current PUT that includes
absentField: undefined, also perform a PUT (or include in the same update) with
a nested object like { nested: { absentField: undefined } } and then GET the
object and assert that getRes.nested is defined, getRes.nested.absentField is
undefined, and that 'absentField' is not an own property of getRes.nested;
update expectations in the test to cover both top-level and nested undefined
behavior so nested updates mirror HTTP behavior.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 40b804d3-ed21-4a29-b323-4dff6e0f2cc2

📥 Commits

Reviewing files that changed from the base of the PR and between e8cc676 and a423f0c.

📒 Files selected for processing (2)
  • spec/ParseServerRESTController.spec.js
  • src/ParseServerRESTController.js

@codecov
Copy link

codecov bot commented Mar 21, 2026

Codecov Report

❌ Patch coverage is 88.88889% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 92.53%. Comparing base (875cf10) to head (4385494).
⚠️ Report is 1 commits behind head on alpha.

Files with missing lines Patch % Lines
src/ParseServerRESTController.js 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            alpha   #10269      +/-   ##
==========================================
- Coverage   92.53%   92.53%   -0.01%     
==========================================
  Files         192      192              
  Lines       16481    16490       +9     
  Branches      226      226              
==========================================
+ Hits        15251    15259       +8     
- Misses       1210     1211       +1     
  Partials       20       20              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

JSON.parse(JSON.stringify(data)) perfectly replicates HTTP mode's
serialization roundtrip — stripping undefined at all nesting levels,
not just top-level keys.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 21, 2026
…ingify())

JSON.parse(JSON.stringify()) is expensive. Replace with a recursive
stripUndefined that walks the object tree efficiently — strips undefined
keys from objects at all nesting levels and converts undefined array
elements to null, matching JSON.stringify behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

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

Can you add a test to compare that with behavior when directAccess is false? It should yield the same result.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants