Building a smooth, lovely experience isn’t just about rich text rendering—it’s about ensuring the database layer under the hood is as fast, flexible, and bulletproof as possible.
Here is a full breakdown of the core backend updates implemented in this release, covering everything from atomic database operations to user-customizable UI settings.
1. Migrating from Manual Rollbacks to Native MongoDB Transactions
The Problem
Previously, deleting a save relied on sequential async operations:
- Delete the save document from the database.
- Decrement the user’s
savesCountcounter by 1. - Remove the associated share link if one existed.
This execution flow introduced serious data integrity flaws:
- Unexpected Crashes: If the Node.js process or server crashed mid-execution (for example, right after step 1), orphan data was left behind, permanently corrupting database consistency.
-
State Mismatches: If a lookup for a save returned
null, the system would still blindly decrementsavesCount, leading to negative or incorrect counter states.
The Solution
Instead of attempting fragile, manual rollbacks inside application-level try/catch blocks, it now relies entirely on native MongoDB transactions via mongoose.startSession() and session.withTransaction().
By wrapping our operations—deleting from schemas.Saves, updating schemas.Users, and removing linked records from schemas.Links—inside a single transaction block passing { session }, MongoDB guarantees absolute ACID compliance. If any step fails or an explicit error is thrown (like SAVE_DELETE_FAILED or USER_UPDATE_FAILED), MongoDB automatically aborts the transaction and rolls back every write operation atomically.
Key Architectural Decisions:
-
Atomic Safeguards: Adding
savesCount: { $gt: 0 }directly inside the atomic query condition acts as an absolute guardrail at the database layer, preventing a user’s save count from ever dipping below zero under any edge case. -
Error-Driven Rollbacks: Throwing inside
session.withTransaction()naturally passes control to the enclosing catch block, which immediately aborts the session without requiring manual flag tracking likefailed = true.
2. Dynamic Patching for Flexible Save Updates
The Problem
Updating a save was previously locked behind strict schema requirements. Users were forced to submit both title and content on every single edit request.
This pattern created zero flexibility: if a user wanted to quick-edit just the content body, the client application was forced to fetch and re-pass the existing title, introducing unnecessary network overhead and strict coupling.
The Solution
I shifted from rigid validation to a dynamic patch object pattern.
When a payload hits /api/v1/update/save/:id, the controller initializes an empty updateObject = {}. Each field (content, title, and color) is length-validated independently. If a field exists on req.body, it gets appended to updateObject; if it doesn’t, the execution flow simply moves on.
Finally, we execute a single schemas.Saves.updateOne() using the $set: updateObject operator. By pairing this with { runValidators: true }, Mongoose validates only the specific fields being modified while ignoring omitted keys entirely.
3. Schema-Enforced Color Selection
The Problem
Allowing users to customize the card background color for individual saves creates a great visual experience, but it opens an immediate security and design vector: How do you prevent malicious actors or API requests from injecting arbitrary strings or broken non-hex values into the UI?
Relying on manual validation checks like colorsArray.includes(color) can be tricky—if an attacker sends a string like "hello #f8f9fa", a loose string check might pass, persisting bad data to your storage engine.
The Solution
Instead of writing defensive JavaScript code before the database write, we shift schema integrity to the ORM level using Mongoose schema enum constraints.
By defining enum: ["#f8f9fa", "#c5d9ec", "#83e6b5", "#ee9595"] directly inside the Mongoose schema definition and enforcing { runValidators: true } during update queries, MongoDB guarantees exact matching. Any attempt to pass an unauthorized color string is rejected automatically before hitting the collection.
If you liked my 🎁, please consider giving it a star on GitHub! It means the 🌍 to me!