SkillAgentSearch skills...

Mongolayer

MongoDB model layer with validation and hooks, similar to Mongoose but thinner.

Install / Use

/learn @simpleviewinc/Mongolayer
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

mongolayer

npm install mongolayer

Mongolayer is a rich document system similar to Mongoose, but thinner, more type-strict, and with more developer flexibility.

This module is an attempt at providing the vision of mongoose (validation, hooks, relationships) but with less eccentricities, less magic under the hood providing developers with more consistent behaviors.

Changelog

12/5/2023 - 2.0.1

  • Updates to mongodb 3.7.4
  • Test db now based on 5.0

3/11/2020 - 2.0.0

  • Adds support for Model based on a mongodb view.
  • createIndexes parameter of connection.add is deprecated, please use sync instead.

9/3/2019 - 1.5.8

  • Adds options.context for passing state to all descendent hooks. Needed for handling conditions where variables need to be available in the top level hook, and in the nested relationship hooks.

7/19/2019 - 1.5.7

  • Adds Connection.promises.add, mongolayer.promises.connect, mongolayer.promises.connectCached.

5/16/2019 - 1.5.2

  • Adds back the promises object to maintain consistency with the Node core strategy of exposing promises object. Adds support for all async methods on the Model.

5/14/2019 - 1.5

  • Updates to mongodb 3.2.4
  • find, findById, aggregate updated so that if you execute them without passing a callback they will now return a promise, allowing them to executed via await.
  • Removes the promises object, it's better when we can utilize the methods without the verbosity of having to go to promises.method.

7/21/2017 - 1.4

  • Virtuals have become a whole smarter. You can now specify a virtual field as having requiredFields and requiredHooks. If you reference that field in a find() fields obj, it will automatically include the requiredFields and hooks. This makes working with relationships and virtuals much simpler.
  • Relationships can now be executed simply by adding the field to your fields obj, without needing to ask for dependent keys and the hook. See Populating Relationships for more info.
  • find() options.castDocs === false behavior has changed. If it's specified, and a truthy fields obj is passed, it will only return the specified fields. This differs from native mongodb, which will continue to return _id even if it's not asked for. This makes the downstream from queries simpler to work with as you only receive what you ask for. Nested empty {} and [] will always be trimmed from the final result. See castDocs in find() for more information.
  • find() options.castDocs === false and passing fields is the recommended default behavior for all queries where performance matters as it forces you to specify only the fields you want.
  • BREAKING - Virtuals can no longer be referenced in hooks. This is for capability when working with castDocs === false and castDocs === true. In general if you need data created via a virtual inside a hook, you should be creating that data with a hook instead.
  • Aggregate now supports hooks, beforeAggregate and afterAggregate
  • Aggregate now supports options.castDocs and options.virtuals for utilizing virtuals when returning data via aggregate
  • Fixed potential RSS memory expansion issue due to usage of WeakMap().
  • BREAKING - When specifying a fields object you will only receive those fields. Previously if you requested { "relationship.foo" : 1 }, you would still get all keys on the root (since foo is on a relationship). This is no longer the case. This change was made so it more closely mimics native MongoDB which only returns the fields that are requested.
  • find() options.castDocs === false is now recursive through relationships. When specified all relationships will also be pulled with castDocs === false. This means if you need to access virtuals on them, you should specify them via the fields obj.

Documentation

Features

  1. Supports the basic queries: find, findById, save, update, count, and remove.
  2. Infinitely recursive field validation on insert, save, and update. Allowing you to validate arrays, objects, and any recursive combination of them.
  3. Enforce required fields and field defaults on insert, save and update.
  4. Robust hook system to run async code before and after any query. You can set default hooks, required hooks, and specify which hooks run at query time.
  5. Declare relationships between models allowing infinite recursive population of related records. Related records will be pulled with virtuals and methods.
  6. Getter and setter virtuals on the document level.
  7. Methods on the document level.
  8. Methods on the model level.

Why not just use Mongoose?

mongoose is a powerful package to which this module owes great homage, but after using mongoose for a while I became frustrated with some of it's eccentricities. In attempting to contribute to the module it became clear that the codebase was quite burdened by legacy.

Here are some examples of frustrations I personally came across using mongoose.

  1. If a record in the DB does not have a value in a field, it will still fill that field with a default value when you pull it out of the database. This gives the illusion a value exists in the db, when it doesn't.
  2. Unable to recursive populate recursive records (populate in a populate). You can only populate one level deep.
  3. When records are populated, they are plain objects, lacking virtuals and methods that they would have if acquired with a normal query.
  4. Unable to run post-query hooks with async code that gets flow control.
  5. Too many differences from the way that node-mongodb-native and mongodb function out of the box. In example, mongoose wraps update with $set causing queries that 'look' correct in mongodb shell and node-mongodb-native to perform entirelly different. Mongoose calls it create while node-mongodb-native and mongodb shell call it insert.
  6. Update method doesn't run hooks, or validation.
  7. save method not implemented with unless using the new doc() syntax. So find, create, all use one syntax, but save uses an entirely different syntax.
  8. Each document in mongoose is an instance of the Schema. That just doesn't make sense to me. The fields on my Document should only be the fields I add, nothing more, nothing less.

Getting Started

npm install mongolayer

Mongolayer has three basic constructs, Models, Connections and Documents.

  • mongolayer.Connection - Manage the connection pool and the raw connection to MongoDB. The connection is aware of all of the Models that are attached to it.
  • mongolayer.Model - The bread-and-butter of mongolayer, your queries are executed on Models and they have fields, methods, and a ton of other features. These are attached to Connections.
  • mongolayer.Document - Base document class for all documents.
  • model.Document - After running a query, each row from the database is returned as an instanceof the model.Document class specific to the model.

Basic application boot-up

  1. Create connection.
  2. Create models.
  3. Attach models to connection.
  4. Run queries, and return documents.
var mongolayer = require("mongolayer");

// create a model
var postModel = new mongolayer.Model({
	collection : "posts",
	fields : [
		{ name : "title", validation : { type : "string" }, required : true },
		{ name : "description", validation : { type : "string" }, required : true }
	]
});

// get a mongolayer connection
mongolayer.connectCached({ connectionString : "mongodb://127.0.0.1:27017/mongoLayer" }, function(err, conn) {
	// attach a model to a connection
	conn.add({ model : postModel }, function(err) {
		// once a model is attached to a connection, assuming no errors, you can then run queries on the model
		
		// you can run queries by using the model reference
		postModel.find({}, function(err, docs) {
			// do something
		});
		
		// you can run queries by using the connection reference
		conn.models.posts.find({}, function(err, docs) {
			// do something
		});
		
		// whether you pass around the model references or the connection is totally up to your application architecture!
	});
});

Views

Creates a view and exposes it via the model. viewOn and pipeline pass directly to https://docs.mongodb.com/manual/reference/command/create/.

const model = new mongolayer.Model({
	collection : "testing",
	viewOn : "name_of_model",
	pipeline : []
});

Once a view is added to a connection you can query on it with find, aggregate, count and all of the various content querying methods.

Hooks

Hooks are the powerful underpining that makes relationship management possible, in addition they provide an entry point for developers to run async code before or after all major qureies, allowing them to maintain a coherent object model. Every major query function invokes a number of different hooks.

Here are some common uses for hooks:

  • Specify a beforeFilter hook which will run prior to count, remove, find allowing you to transform the filter, such as casting a string to a mongolayer.ObjectId.
  • Specify an afterFind hook which would log some information to a log file based on the number of records returned.
  • Specify a afterRemove hook to remove related orphan records.
  • Specify an afterFind hook to pull in other data which is not stored in mongoDB and can't be managed through relationships such as Facebook posts, SQL data, file system data, or memcache

Related Skills

View on GitHub
GitHub Stars22
CategoryDevelopment
Updated1mo ago
Forks7

Languages

JavaScript

Security Score

90/100

Audited on Feb 9, 2026

No findings