user393219
user393219

Reputation:

Mongoose Unique Validator Triggers Server Error

I have a slug attribute that I'm setting to unique with Mongoose:

// Article
var Article = new Schema({
  title: { type: String, required: true },
  heading: { type: String },
  body: { type: String, required: true },
  is_premium: { type: Boolean, required: true },
  urlslug: { type: String, unique: true, required: true },
  images: [Images],
  videos: [Videos],
  user: {
    user_id: { type: String, required: true },
    url: { type: String, required: true }
  },
  modified: { type: Date, required: true }
});

Article.path('title').validate(function (v) {
  return v.length > 5 && v.length < 70;
});

I'm posting it successfully to the database like this:

router.post('/', passport.authenticate('bearer', { session: false }), function (req, res) {

if (req.user.role === "editor" || req.user.role === "moderator" || req.user.role === "admin") {

    var article = new ArticleModel({
        title: req.body.title,
        heading: req.body.heading,
        body: req.body.body,
        images: req.body.images,
        videos: req.body.videos,
        is_premium: req.body.is_premium,
        urlslug: req.body.slug,
        modified: Date.now(),
        user: {
            user_id: req.user.id,
            url: userUrlPrefix + req.user.username
        }
    });

    article.save(function (err) {
        if (!err) {
            log.info("article created");
            return res.send({ status: 'OK', article:article });
        } else {
            console.log(err);
            if(err.name == 'ValidationError') {
                res.statusCode = 400;
                res.send({ error: 'Validation error' });
            } else {
                res.statusCode = 500;
                res.send({ error: 'Server error' });
            }
            log.error('Internal error(%d): %s',res.statusCode,err.message);
        }
    });
} else {
    return res.send('your user can\'t create content');
}
});

When I try to use the same slug again, and ideally trigger a validation error, I get a server error, not a validation error:

HTTP/1.1 500 Internal Server Error
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 24
Content-Type: application/json; charset=utf-8
Date: Fri, 30 Jan 2015 23:07:56 GMT
Vary: Origin
X-Powered-By: Express

{
    "error": "Server error"
}

(output from my node console):

{ [MongoError: insertDocument :: caused by :: 11000 E11000 duplicate key error 

index: SSR.articles.$urlslug_1  dup key: { : "article-1" }]
  name: 'MongoError',
  code: 11000,
  err: 'insertDocument :: caused by :: 11000 E11000 duplicate key error index: SSR.articles.$urlslug_1  dup key: { : "article-1" }' }
error: [routes/articles.js] Internal error(500): insertDocument :: caused by :: 11000 E11000 duplicate key error index: SSR.articles.$urlslug_1  dup key: { : "article-1" }

Is uniqueness no explict to validation in Mongoose? If not, how can I check if something already exists (roll my own unique validator) with Mongoose's validate() method?

Upvotes: 0

Views: 803

Answers (1)

JohnnyHK
JohnnyHK

Reputation: 311835

You can create your own custom validator to check for duplication, but you'd still need to handle the E11000 duplicate key error case as another insert could still occur in between the validation and insertion. So it's typically better to just use the E11000 error so your duplicate error handling is in just one spot.

You can use an approach like:

article.save(function (err) {
    if (!err) {
        log.info("article created");
        return res.send({ status: 'OK', article:article });
    } else {
        console.log(err);
        if(err.name == 'ValidationError') {
            res.statusCode = 400;
            res.send({ error: 'Validation error' });
        } else if (err.name == 'MongoError' && err.code == 11000) {
            res.statusCode = 400;
            res.send({ error: 'Duplicate validation error' });            
        } else {
            res.statusCode = 500;
            res.send({ error: 'Server error' });
        }
        log.error('Internal error(%d): %s',res.statusCode,err.message);
    }
});

Upvotes: 2

Related Questions