swyxio / swyxdotio

This is the repo for swyx's blog - Blog content is created in github issues, then posted on swyx.io as blog pages! Comment/watch to follow along my blog within GitHub
https://swyx.io
MIT License
336 stars 45 forks source link

4 Things I Learned from Mastering Mongoose.js #339

Closed swyxio closed 2 years ago

swyxio commented 2 years ago

source: devto devToUrl: "https://dev.to/swyx/5-things-i-learned-from-mastering-mongoose-js-286f" devToReactions: 0 devToReadingTime: 3 devToPublishedAt: "2020-07-26T09:51:10.225Z" devToViewsCount: 2 title: 4 Things I Learned from Mastering Mongoose.js published: false description: A quick book review of the new Mongoose.js book from Val Karpov tags: Reflections cover_image: https://mastering-mongoose.netlify.app/images/mastering-mongoose.jpg

I first encountered MongoDB as part of the FreeCodeCamp curriculum, then as part of the MeteorJS stack, finally attending a talk at my bootcamp given by it's lead developer, Val Karpov (who, by the way, also coined the MEAN stack!).

All of these sources kind of treat Mongoose as a thin interface to MongoDB, which is a shame given how powerful it really is and how central your mastery of your database stack can be for an app's speed and scale. So it was of course exciting to hear that Val has finally written the definitive guide on Mongoose: Mastering Mongoose!

I'm going to jot down 5 things I learned. Note - I haven't used Mongoose in about 3 years, so this is mostly very introductory level.

1. Change Tracking for Minimal Updates

When you load a document from the database using a query and then modify it after, change tracking means Mongoose can determine the minimal update to send to MongoDB and avoid wasting network bandwidth.

// Mongoose loads the document from MongoDB and then _hydrates_ it
// into a full Mongoose document.
const doc = await MyModel.findOne();
doc.name; // "Jean Valjean"
doc.name = 'Monsieur Leblanc';
doc.modifiedPaths(); // ['name']
// `save()` only sends updated paths to MongoDB. Mongoose doesn't
// send `age`.
await doc.save();

This is very nice out of the box for performance and I don't have to do a thing!

2. Multiple Connections

Most apps only need one connection to MongoDB. However, Mongoose supports multiple connections:

const mongoose = require('mongoose');
const conn1 = mongoose.createConnection('mongodb://localhost:27017/db1',
 { useNewUrlParser: true });
const conn2 = mongoose.createConnection('mongodb://localhost:27017/db2',
 { useNewUrlParser: true });
// Will store data in the 'db1' database's 'tests' collection
const Model1 = conn1.model('Test', mongoose.Schema({ name: String }));
// Will store data in the 'db2' database's 'tests' collection
const Model2 = conn2.model('Test', mongoose.Schema({ name: String }));

This is helpful when:

3. Mongoose uses its own Middleware

In Mongoose, middleware lets you attach your own custom logic to built-in Mongoose functions. You can run pre and post any function:

This is a powerful pluggable system that reminds me of similar things in the npm scripts and Netlify Build system.

Mongoose uses its own system internally - Mongoose attaches a pre('save') middleware to all models that calls validate(). That means save() triggers validate() middleware, which is why save() works the way it does.

4. Principle of Least Cardinality

With relational databases it is best to normalise data per schema. The third normal form is something like:

"Every non-key must provide a fact about the key, the whole key, and nothing but the key."

You can do this in Mongoose with populate(), but that goes against the grain of how NoSQL schemas should be setup. The common recommendation is to denormalize - a document should store all the properties you want to query by ("The Princple of Denormalization"), and, data that is referenced together, belongs together ("The Principle of Data Locality"). However, taken literally, this can lead to monster documents that can take forever to load.

The Principle of Least Cardinality states:

Store relationships in a way that minimizes the size of individual documents.

This helps make the correct tradeoff as far as Val is concerned. The other very good reason to do this is the fact that MongoDB limits documents to 16MB in size, of course - but primarily, the Principle of Least Cardinality is about conserving bandwidth when loading documents.

Conclusion

The book offers 4 sample apps built with Websockets, React, and Vue, and even ends with a great discussion about recommended app structure at the directory and app level! These polishing touches and the accessible and well thought through examples on every page make this a great reference point for anyone using Mongoose.js.