Skip to content

Upgrade from 0.2.x to 0.3.x

Summary: Mondel 0.3 keeps the public API surface used in 0.2 (method names, argument order, createClient overloads, validation modes, schema builders).
It is a minor release: additive features plus intentional behavior fixes. It is not a bit-for-bit behavioral freeze of every 0.2 edge case.

ClaimAccurate?
Stable public API (no removed methods / renames)Yes
Safe for most apps that write valid dataYes
Zero behavior change in every scenarioNo
“100% compatible bit-for-bit with 0.2”No — do not promise that

Peers unchanged: mongodb ^6 \|\| ^7, zod ^3.24 \|\| ^4, Node >=18.


What stays the same

  • defineSchema / schema / s.* chains
  • createClient serverless factory vs node uri mode
  • Collection methods: find*, create*, update*, delete*, count, exists, aggregate, getCollection
  • Validation modes: "strict" | "loose" | "off"
  • Hard delete when soft is omitted
  • Native MongoDB filters and non-$set operators ($inc, $push, …)
  • CLI pull / push commands and config shape

Additive only (opt-in): findOneAndUpdate, bulkWrite, richer find option passthrough (timeoutMS, …).


Behavior differences (review before upgrading)

1. Field .default() on create

0.2.x0.3.x
create / createMany with validation strict or looseDefault often not written to the documentDefault is applied (all nesting levels)
validation: "off"No defaultsNo defaults (unchanged)
UpdatesShould not inject defaultsDo not inject defaults (including nested)

Who cares: schemas that use .default(...) and omit the field on create.
Action: confirm stored documents should include those defaults (usually yes — matches schema docs).

2. $set / $setOnInsert validation (strict)

0.2.x0.3.x
Plain update { name: "x" }ValidatedValidated
{ $set: { age: -1 } } with min constraint, strictOften acceptedZodError
{ $inc: { age: 1 } }Not validatedNot validated

Who cares: code that pushed invalid shapes only via $set under validation: "strict".
Action: fix payloads, or temporarily use "loose" / "off" during migration.

3. timestamps: false on updates

0.2.x0.3.x
updateOne(..., { timestamps: false })Could still set updatedAt (bug)Does not set timestamp fields

Who cares: callers that passed timestamps: false and relied on timestamps still appearing.
Action: omit the flag if you want automatic updatedAt.

4. { soft: true } on delete

0.2.x0.3.x
deleteOne(filter)Hard deleteHard delete (unchanged)
deleteOne(filter, { soft: true })Flag ignored → hard deleteSets deletedAt (+ updatedAt if timestamps enabled)

Who cares: only apps that already passed soft: true.
Action: if you meant hard delete, remove { soft: true }. If you want soft delete, filter reads with deletedAt: { $exists: false }.
Soft-delete deletedCount reflects matched documents.

5. TypeScript type of create result

0.2.x types0.3.x typesRuntime (both)
create()Incorrectly looked like full document + insertedIdInsertOneResultDriver insert result (insertedId, …)

Who cares: TypeScript code that read result.email (etc.) after create.
Action: use result.insertedId; fetch the doc with findById if you need fields. Runtime was never a full document.

6. createMany([])

0.2.x0.3.x
Empty arrayCould error in the driverNo-op, insertedCount: 0

Safer; unlikely to break real apps.

7. s.objectId() accepts only real ObjectId values

0.2.x0.3.x
s.objectId() string inputAny string passedMust be a 24-character hex string

0.2.x built this field as z.union([z.string(), …]), so the first branch accepted every string and the check was effectively dead. 0.3.x requires an ObjectId instance or a 24-char hex string.

Who cares: collections whose objectId fields hold legacy or synthetic ids ("", numeric strings, external keys).
Action: grep your schemas for s.objectId() and confirm the data really is ObjectId-shaped. If a field holds arbitrary identifiers, model it as s.string().

typescript
// legacy/external identifiers — not ObjectIds
brandRef: s.string(),

Pre-upgrade checklist

  1. [ ] npm install mondel@^0.3.1 (keep existing mongodb / zod peers)
  2. [ ] Run tsc — fix any code treating create result as a full document
  3. [ ] Grep schemas for .default( — confirm create paths that omit those fields
  4. [ ] Grep for $set under validation: "strict" — ensure values pass schema rules
  5. [ ] Grep schemas for s.objectId() — confirm the stored values are ObjectId-shaped
  6. [ ] Grep for timestamps: false on updates — confirm intent
  7. [ ] Grep for soft: true — confirm soft-delete is desired
  8. [ ] Map ZodError to a 400 at your API boundary — strict mode now rejects payloads that 0.2.x silently accepted, and the default Error handler will report them as 500s
  9. [ ] Compare enum fields against real data (db.col.distinct("status")) — a value in the collection that is missing from s.enum([...]) now fails $set
  10. [ ] Smoke-test create / update / delete in a staging DB
bash
# Optional: temporarily relax validation while fixing write paths
# validation: "loose"  // warn instead of throw, defaults still applied

Prefer "loose" over "off" for that: off also disables field defaults, so it changes what gets written as well as what gets rejected.


Install

bash
npm install mondel@^0.3.1
# peers unchanged
npm install mongodb@^6 zod@^3   # or mongodb@^7 + Node 20+, zod@^4

Go straight to 0.3.1 — it fixes a payload-mutation bug and makes defaults deterministic in loose mode. See the CHANGELOG.


Released under the MIT License. · llms.txt