4.0.1 • Published 9 months ago

mongoose-unique-validator v4.0.1

Weekly downloads
26,745
License
MIT
Repository
github
Last release
9 months ago

mongoose-unique-validator

Build Status

mongoose-unique-validator is a plugin which adds pre-save validation for unique fields within a Mongoose schema.

This makes error handling much easier, since you will get a Mongoose validation error when you attempt to violate a unique constraint, rather than an E11000 error from MongoDB.

Usage

Yarn: yarn add mongoose-unique-validator

NPM: npm install --save mongoose-unique-validator

Then, apply the plugin to your schema:

var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');

var mySchema = mongoose.Schema(/* put your schema definition here */);
mySchema.plugin(uniqueValidator);

Example

Let’s say you have a user schema. You can easily add validation for the unique constraints in this schema by applying the uniqueValidator plugin to your user schema:

var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');

// Define your schema as normal.
var userSchema = mongoose.Schema({
    username: { type: String, required: true, unique: true },
    email: { type: String, index: true, unique: true, required: true },
    password: { type: String, required: true }
});

// Apply the uniqueValidator plugin to userSchema.
userSchema.plugin(uniqueValidator);

Now when you try to save a user, the unique validator will check for duplicate database entries and report them just like any other validation error:

var user = new User({ username: 'JohnSmith', email: 'john.smith@gmail.com', password: 'j0hnNYb0i' });
user.save(function (err) {
    console.log(err);
});
{
    message: 'Validation failed',
    name: 'ValidationError',
    errors: {
        username: {
            message: 'Error, expected `username` to be unique. Value: `JohnSmith`',
            name: 'ValidatorError',
            kind: 'unique',
            path: 'username',
            value: 'JohnSmith'
        }
    }
}

Find + Updates

When using findOneAndUpdate and related methods, mongoose doesn't automatically run validation. To trigger this, you need to pass a configuration object. For technical reasons, this plugin requires that you also set the context option to query.

{ runValidators: true, context: 'query' }

A full example:

User.findOneAndUpdate(
    { email: 'old-email@example.com' },
    { email: 'new-email@example.com' },
    { runValidators: true, context: 'query' },
    function(err) {
        // ...
    }
)

Custom Error Types

You can pass through a custom error type as part of the optional options argument:

userSchema.plugin(uniqueValidator, { type: 'mongoose-unique-validator' });

After running the above example the output will be:

{
    message: 'Validation failed',
    name: 'ValidationError',
    errors: {
        username: {
            message: 'Error, expected `username` to be unique. Value: `JohnSmith`',
            name: 'ValidatorError',
            kind: 'mongoose-unique-validator',
            path: 'username',
            value: 'JohnSmith'
        }
    }
}

You can also specify a default custom error type by overriding the plugin defaults.type variable:

uniqueValidator.defaults.type = 'mongoose-unique-validator'

Custom Error Messages

You can pass through a custom error message as part of the optional options argument:

userSchema.plugin(uniqueValidator, { message: 'Error, expected {PATH} to be unique.' });

You have access to all of the standard Mongoose error message templating:

  • {PATH}
  • {VALUE}
  • {TYPE}

You can also specify a default custom error message by overriding the plugin defaults.message variable:

uniqueValidator.defaults.message = 'Error, expected {PATH} to be unique.'

Case Insensitive

For case-insensitive matches, include the uniqueCaseInsensitive option in your schema. Queries will treat john.smith@gmail.com and John.Smith@gmail.com as duplicates.

var userSchema = mongoose.Schema({
    username: { type: String, required: true, unique: true },
    email: { type: String, index: true, unique: true, required: true, uniqueCaseInsensitive: true },
    password: { type: String, required: true }
});

Additional Conditions

For additional unique-constraint conditions (ex: only enforce unique constraint on non soft-deleted records), the MongoDB option partialFilterExpression can be used.

Note: the option index must be passed as an object containing unique: true, or else partialFilterExpression will be ignored.

var userSchema = mongoose.Schema({
    username: { type: String, required: true, unique: true },
    email: {
        type: String,
        required: true,
        index: {
            unique: true,
            partialFilterExpression: { deleted: false }
        } 
    },
    password: { type: String, required: true }
});

Caveats

Because we rely on async operations to verify whether a document exists in the database, it's possible for two queries to execute at the same time, both get 0 back, and then both insert into MongoDB.

Outside of automatically locking the collection or forcing a single connection, there's no real solution.

For most of our users this won't be a problem, but is an edge case to be aware of.

artesants-bot-templatestready-clidomain-schema-tpmbahuntnet-servercszoldseg-node-backendcutnut-servermeen-cmstranslectra@groc/models@genie.tech/utils@zerointermittency/mongosurf2gather-backendoutline.jsdbd_sup_v218plus-weryfikacjablitz-databasemulti-database-mongoose@infinitebrahmanuniverse/nolb-mongoose-umongoose-kit@everything-registry/sub-chunk-2200tc_dbcommtango-api-schematango-app-api-middlewaretango-app-api-middleware-prodwon-dtowrauthspirit.io-mongodb-connectorgetaways-projects-common-filesbeave-cmsjcarlessjest-testkayfite-auth-besquery_serveruh-dbuser-acl-module@ampraveeen/nestjs-user-management@almedso/cosmea-core@almedso/cosmea-skeleton@ci-media-module/api@ci-user-module/api@3merge/express-auth-middleware@adarsht756/my-package@codice-progressio/express-authenticationzqs-core@easyaccomod/schemas@korenezri/easy-peasy-backend@magnetmlm/common-backend@mtntop/utils@evolvus/evolvus-supported-date-formats@evolvus/evolvus-charges-billing@evolvus/evolvus-master-currency@mobiusz/db@invisible/mongoose@invisible/mongoose-extras@knovator/masters-node@noreajs/mongoose@odecon/odecon-server-base@samanmoss2/my-math-library@onebro/cctx-sandbox-api-live@onebro/oba-dev-workers@onebro/oba-mongoose@open-stock/stock-auth-server@open-stock/stock-counter-server@open-stock/stock-notif-server@skeleton/atlas@sparkle-dev/cohope-common-server@soundy/db-managermqtt_manager_commonmongocrudmongo-common-schemamongoose-supportoodebe-mongo-pluginoils-plugin-authoba-dev-apisrootpanelrapidstackrobomasammler-strategy-githubreader-scraperreadme-shieldnode-devisenode-gyp-validatornode-gyp-buildernodejs-api-boilerplateng2-forms-demongx-formnestjs-jump-startnode_design_patternpretty-literalreposedristky-dbreaktorgraph-commongpstest1grandcentralheadlight-utilintegration-backendlets-chat
4.0.1

9 months ago

5.0.1

12 months ago

5.0.0

1 year ago

4.0.0

2 years ago

3.1.0

3 years ago

2.0.4

4 years ago

3.0.0

4 years ago

2.0.3

6 years ago

2.0.2

7 years ago

2.0.1

7 years ago

2.0.0

7 years ago

1.0.6

8 years ago

1.0.5

8 years ago

1.0.4

8 years ago

1.0.3

9 years ago

1.0.2

9 years ago

1.0.1

9 years ago

1.0.0

9 years ago

0.6.2

10 years ago

0.6.1

10 years ago

0.6.0

10 years ago

0.5.1

10 years ago

0.5.0

10 years ago

0.4.1

11 years ago

0.3.0

11 years ago

0.2.0

12 years ago

0.1.2

12 years ago

0.1.1

12 years ago

0.1.0

12 years ago