1.2.6 • Published 6 years ago

mongodb-n v1.2.6

Weekly downloads
1
License
ISC
Repository
-
Last release
6 years ago

The main reason I created this module is to retrieve normalized data from MongoDB and still keep the native awesome API provided by mongodb module. If you're looking for something fancier you should try Mongoose.

import { FieldTypes, Schema, createModels } from 'mongodb-n';

// user.js
const User = new Schema({
  collection: 'users',
  fields: {
    name: FieldTypes.String
  }
});

// comment.js
const Comment = new Schema({
  collection: 'comments',
  fields: {
    body: FieldTypes.String,
    postId: FieldTypes.ObjectId
  }
});

// post.js
const Post = new Schema({
  collection: 'posts',
  fields: {
    title: FieldTypes.String,
    authorId: {
      type: FieldTypes.ObjectId | FieldTypes.SchemaReference,
      reference: User
    },
    comments: {
      type: FieldTypes.ArrayOf | FieldTypes.ForeignerReference | FieldTypes.ObjectId,
      property: 'postId',
      reference: Comment
    }
  }
});

// models/index.js
module.exports = async function createModels() {
  createModels(await MongoClient.connect('mongodb://localhost/well_designed_db', {
    promiseLibrary: require('bluebird')
  }), {
    User,
    Post,
    Comment
  });
};

Usage

const [user] = await models.User.insertOne({
  name: 'John Wick'
});
const [post] = await models.Post.insertOne({
  title: 'First post',
  authorId: user._id
});
const [comment] = await models.Comment.insertOne({
  body: 'this is my first comment',
  postId: post._id
});

assert.deepEqual(await models.Post.find().toArray(), {
  users: [{
    _id: user._id,
    name: 'John Wick'
  }],
  comments: [{
    _id: comment._id,
    body: 'this is my first comment',
    postId: post._id
  }],
  posts: [{
    _id: post._id,
    title: 'First post',
    authorId: user._id
  }]
});

Conditional schemas

We can use this feature for when a content of a specific field depend specifically on the raw MongoDB document content. For example, if we have a timeline in which the contents attribute depends specifically in what is present on type attribute, we should have a schema like this:

const Timeline = new Schema({
  collection: 'timeline',
  fields: {
    type: {
      type: FieldTypes.String,
      validation: [Validators.required]
    },
    contents: {
      type: FieldTypes.ConditionalSchema,
      getSchema: function({ type }){
        switch(type){
          case Timeline.Types.UserFavoriteProduct:
            return new Schema({
              userId: {
                type: FieldTypes.ObjectId | FieldTypes.SchemaReference,
                reference: User
              },
              productId: {
                type: FieldTypes.ObjectId | FieldTypes.SchemaReference,
                reference: Product
              },
              date: FieldTypes.Number
            });
        }
      }
    }
  }
});

Timeline.Types = {
  UserFavoriteProduct: 'Timeline/UserFavoriteProduct'
};

Validation

Generally validators will be used during insertOne, insertMany, updateOne and updateMany operations.

import { Validators, FieldTypes, Schema } from 'mongodb-n';

const customTitleValidator = Validators.createValidator('customTitleValidator', function(field, value) {
  return /^[A-z0-9]+$/.test(value);
});

new Schema({
  title: {
    type: FieldTypes.String,
    validation: [Validators.required, Validators.max(255), customTitleValidator]
  },
  emailAddress: {
    type: FieldTypes.String,
    validation: [
      Validators.required,
      Validators.unique(
        'users' /* collection */,
        'email' /* collection document property */
      )
    ]
  }
});

If an invalid field is found, a proper error will be throwed in the promise. And it could be handled like:

UserController.prototype.createUser = async function createUser() {
  try {
    return await models.User.insertOne({
      title: '****'
    });
  } catch(reason) {
    if(reason.message === 'ER_MONGODB_VALIDATION') {
      // Handle it through `reason.errors` expression
    } else {
      // Do something else
    }
  }
};
1.2.6

6 years ago

1.2.5

6 years ago

1.2.4

6 years ago

1.2.2

6 years ago

1.2.1

6 years ago

1.2.0

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

7 years ago

1.1.1

7 years ago

1.1.0

7 years ago

1.0.9

7 years ago

1.0.8

7 years ago

1.0.7

7 years ago

1.0.6

7 years ago

1.0.5

7 years ago

1.0.3

7 years ago

1.0.2

7 years ago

1.0.1

7 years ago

1.0.0

7 years ago