63

I have a mongoose schema for users (UserSchema) and I'd like to validate whether the email has the right syntax. The validation that I currently use is the following:

UserSchema.path('email').validate(function (email) {
  return email.length
}, 'The e-mail field cannot be empty.')

However, this only checks if the field is empty or not, and not for the syntax.

Does something already exist that I could re-use or would I have to come up with my own method and call that inside the validate function?

Tamas
  • 10,953
  • 13
  • 47
  • 77

10 Answers10

132

you could also use the match or the validate property for validation in the schema

example

var validateEmail = function(email) {
    var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
    return re.test(email)
};

var EmailSchema = new Schema({
    email: {
        type: String,
        trim: true,
        lowercase: true,
        unique: true,
        required: 'Email address is required',
        validate: [validateEmail, 'Please fill a valid email address'],
        match: [/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
    }
});
ramon22
  • 3,528
  • 2
  • 35
  • 48
  • nice solution. but only problem is it's not passing eslint regex rule. could you please update? – Liu Zhang Jul 14 '17 at 02:16
  • 18
    What's difference between `validate` and `match`? – Anatoly Aug 13 '17 at 14:51
  • 5
    @Anatoly `validate` specifies a function to be called for validation (doesn't need to use regexes), `match` specifies a validation regex directly. – UpTheCreek Sep 26 '18 at 10:06
  • 1
    proposed regex allows only 2-3 letter TLD, but there are available longer TLD https://en.wikipedia.org/wiki/List_of_Internet_top-level_domains also, single letter TLD is technically correct (even if there're no such at the moment) so my suggestion is to use regex with `w+` instead of `w{2,3}` – Adrian Bienias May 20 '21 at 17:47
  • 1
    plus `+` is a valid character for email and this regex doesn't allow emails like`user+alias@gmail.com`, consider using another regex e.g. `/^.+@(?:[\w-]+\.)+\w+$/` – Adrian Bienias May 20 '21 at 18:15
93

I use validator for my input sanitation, and it can be used in a pretty cool way.

Install it, and then use it like so:

import { isEmail } from 'validator';
// ... 

const EmailSchema = new Schema({
    email: { 
        //... other setup
        validate: [ isEmail, 'invalid email' ]
    }
});

works a treat, and reads nicely.

Kris Selbekk
  • 7,438
  • 7
  • 46
  • 73
24

You can use a regex. Take a look at this question: Validate email address in JavaScript?

I've used this in the past.

UserSchema.path('email').validate(function (email) {
   var emailRegex = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
   return emailRegex.test(email.text); // Assuming email has a text attribute
}, 'The e-mail field cannot be empty.')
Community
  • 1
  • 1
dannyp32
  • 474
  • 5
  • 14
  • Awesome! I didn't know about the test() method either. – Tamas Aug 02 '13 at 17:45
  • 7
    You should define the regex outside of the function for better performance. – klh Dec 17 '13 at 05:26
  • 4
    This regex fails to validate some valid emails, such as those include the plus sign on the lefthand side. – Mark Stosberg Mar 21 '14 at 09:21
  • What exactly is this `email.text` attribute? – chovy May 09 '14 at 06:48
  • @chovy the email.text attribute is basically the email address string. For example "myemail@gmail.com" – dannyp32 May 12 '14 at 03:46
  • 2
    can't help but notice the end part, is that any word between 2-4 chars long? does this mean an email like info@company.london will not work? the regex is probably wrong. – Val Jun 13 '17 at 09:33
  • 2
    @Val these TLDs didn't exist back in 2013. Take a look at [this answer](https://stackoverflow.com/a/46181/7770081) for an up-to-date regex – Robin Métral Nov 02 '19 at 14:21
14

The validator dosn't play well with mongoose to get rid of the warning set isAsync to false

const validator = require('validator');

email:{
type:String,
validate:{
      validator: validator.isEmail,
      message: '{VALUE} is not a valid email',
      isAsync: false
    }
}
Patryk Acuña
  • 141
  • 1
  • 5
  • without `isAsync: false` , validation was not working in my case, i was using mongoose with promise. mongoose version `5.2.13` – Rakibul Haq Sep 26 '18 at 09:46
13

I know this is old, but I don't see this solution so thought I would share:

const schema = new mongoose.Schema({
    email: {
        type: String,
        trim: true,
        lowercase: true,
        unique: true,
        validate: {
            validator: function(v) {
                return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v);
            },
            message: "Please enter a valid email"
        },
        required: [true, "Email required"]
    }
});

You can do this for any type you want to validate and just pass the appropriate regex expression. If you google the type you want to validate and it's related regex expression it's easy to find a solution. This will keep your validations consistent and puts all the code in the schema instead of hanging functions.

Isaac S. Weaver
  • 135
  • 1
  • 3
5

For some reason validate: [ isEmail, 'Invalid email.'] doesn't play well with validate() tests.

const user = new User({ email: 'invalid' });
try {
  const isValid = await user.validate();
} catch(error) {
  expect(error.errors.email).to.exist; // ... it never gets to that point.
}

But mongoose 4.x (it might work for older versions too) has other alternative options which work hand in hand with Unit tests.

Single validator:

email: {
  type: String,
  validate: {
    validator: function(value) {
      return value === 'correct@example.com';
    },
    message: 'Invalid email.',
  },
},

Multiple validators:

email: {
  type: String,
  validate: [
    { validator: function(value) { return value === 'handsome@example.com'; }, msg: 'Email is not handsome.' },
    { validator: function(value) { return value === 'awesome@example.com'; }, msg: 'Email is not awesome.' },
  ],
},

How to validate email:

My recommendation: Leave that to experts who have invested hundreds of hours into building proper validation tools. (already answered in here as well)

npm install --save-dev validator

import { isEmail } from 'validator';
...
validate: { validator: isEmail , message: 'Invalid email.' }
Community
  • 1
  • 1
zurfyx
  • 31,043
  • 20
  • 111
  • 145
4

Email type for schemas - mongoose-type-email

var mongoose = require('mongoose');
require('mongoose-type-email');

var UserSchema = new mongoose.Schema({
    email: mongoose.SchemaTypes.Email
});

Possible Reference:

o.z
  • 1,086
  • 14
  • 22
2
const mongoose = require("mongoose");

const validateEmail = function(email) {
  const regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
  return regex.test(email);
};

const userSchema = new mongoose.Schema({
  email: {
    type: String,
    required: [true, "Please enter your email"],
    validate: [validateEmail, "Please enter a valid email"],
    unique: true,
  },


module.exports = mongoose.model("User", userSchema);
elias-soykat
  • 100
  • 1
  • 8
0
email: {
    type: String,
    match: [/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, `Please fill valid email address`],
    validate: {
      validator: function() {
        return new Promise((res, rej) =>{
          User.findOne({email: this.email, _id: {$ne: this._id}})
              .then(data => {
                  if(data) {
                      res(false)
                  } else {
                      res(true)
                  }
              })
              .catch(err => {
                  res(false)
              })
        })
      }, message: 'Email Already Taken'
    }
  }
adiga
  • 34,372
  • 9
  • 61
  • 83
Eka Cipta
  • 229
  • 1
  • 5
  • 1
    do NOT recommend such an approach. As you are defining `User` schema and you are using `User.findOne` before schema was declared. – YUzhva Jul 06 '19 at 10:19
0
  • Use npm package called validator that can give you a bunch of validations out of the box and not only for just the email

  • Use validate property in your schema on the email field that takes in two nested properties - validator (Function) and a message. The validator function is where we use the npm package we installed in our first step

Code

npm install validator

Define schema

const mongoose = require('mongoose')
const validatorPackage = require('validator')

const UserSchema = new mongoose.Schema({
      .......
    
      email: {
        type: String,
        unique: true,
        required: [true, 'Email address is required'],
        validate: {
          validator: validatorPackage.isEmail,
          message: 'Please provide a valid email',
        },
      },
      .......
      
})

const model = mongoose.model('User', UserSchema)

module.exports = model
Sandeep Amarnath
  • 5,463
  • 3
  • 33
  • 43