feat: Check required schema properties in upsert updates - #1104
Open
umeshravuru wants to merge 1 commit into
Open
umeshravuru wants to merge 1 commit into
umeshravuru wants to merge 1 commit into
Conversation
The update argument of Model.upsert is now type-checked to ensure the document created by the insert branch of the upsert operation contains all the required properties in the schema. The required properties can be provided by the filter equality fields (including inside top-level $and clauses), by the update operators which create missing fields ($set, $setOnInsert, $inc, $push, etc.), by the schema defaults or by the timestamps options. Any remaining required properties must be passed in the $setOnInsert operator. This matches the runtime behavior of the MongoDB JSON schema validation, which rejects upserted documents with missing required properties. Fixes plexinc#1019 BREAKING CHANGE: TypeScript compilation errors are now reported for `Model.upsert` calls which may insert documents with missing required properties. Previously such calls compiled without errors and failed at runtime with a MongoDB document validation error. Provide the missing properties in `$setOnInsert` (or `$set`) to fix the errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
umeshravuru
marked this pull request as ready for review
August 29, 2026 21:57
nunomarks
requested changes
Sep 9, 2026
nunomarks
left a comment
Contributor
There was a problem hiding this comment.
Thank you for the PR!
There were some regressions related to excess properties (and also bad names), and here are other findings: https://gist.github.com/nunomarks/bd0cc6d8c9bd776ca49bf3271e47a9ab
The false negatives might not be so easy to solve, so those are optional.
I'll take a closer look once these are addressed.
| * 'nestedObject.direct' -> 'nestedObject' | ||
| * 'foo' -> 'foo' | ||
| */ | ||
| type RootProperty<Property> = Property extends `${infer Root}.${string}` ? Root : Property; |
Contributor
There was a problem hiding this comment.
We already have this in src/DeepPick.ts (sort of) - HeadPaths. Please consolidate if possible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1019
Problem
Model.upsertaccepted update filters which, when the upsert inserted a new document, produced documents missing required schema properties. The compiler was silent and the operation only failed at runtime with a MongoDB document validation error:Solution
The
updateargument ofupsertis now checked against a newPaprUpsertUpdateFiltertype, which verifies that every required property in the schema is provided by at least one of the sources MongoDB and papr use to build the inserted document:$andclauses; dot-notation keys count for their root property);$set,$setOnInsert,$inc,$mul,$min,$max,$push,$addToSet,$currentDate,$bit);defaults(static and dynamic) and thetimestampsoptions (reusing the existingDocumentForInserttype).When required properties are missing, the type resolves to
{ $setOnInsert: { ...missing } }, so the compiler error names exactly the properties to add:Design notes and limitations
{ foo: { $gt: 1 } }) are treated as provided, because they cannot be reliably distinguished from direct object equality values at the type level. This keeps the check free of false positives, at the cost of not flagging some unsafe calls.$or/$norclauses are not treated as provided (MongoDB does not copy them into the inserted document).PaprFilter<TSchema>/PaprUpdateFilter<TSchema>) skip the check, since their keys cannot be known statically.updateManywithupsert: truein its options andbulkWriteupdate operations withupsert: truestill accept unchecked updates — happy to tackle those in a follow-up if this approach is accepted.Breaking change
Code which previously compiled but could insert invalid documents at runtime now fails to compile; the fix is to provide the missing properties in
$setOnInsert(or$set). Marked asBREAKING CHANGEin the commit footer, matching how the previous type-strictness changes were released (e.g. #430 in v15.0.0).Testing
pnpm test— 242 tests pass, including new tests inmodel.test.ts(call-site enforcement, including the exact No error when missing type withupsert#1019 repro schema) andmongodbTypes.test.ts(unit tests for the new type: per-operator behavior,$and/$orhandling, dot-notation keys, defaults).pnpm test:types,pnpm lintandpnpm pretty:ciare clean../tests/run.sh— the full integration suite passes against a real MongoDB 6.0.16, and the TS fixtures compile against the built package with TypeScript 5.6. A new end-to-end case verifies that the new type error and MongoDB's runtime JSON schema validation reject the same upsert call.node docs/build.js.