Antonio Pavicevac-Ortiz
Antonio Pavicevac-Ortiz

Reputation: 7759

Not getting correct status code (409) if email exists using Next.js, Mongoose, MongoDb Atlas and Express

I am building a login/Register portion of my app. Right now I'm using express-validator to check if an email exists in my collection.

This is my route:

var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body } = require('express-validator');  

router
    .route('/registration')
    .get(function(req, res) {
        console.log(0)
        UserModel.find({}, (err, users) => {
            console.log(1);
            if (err) return res.status(500).send(err)
            console.log(2);
            return res.json(users);
        })
    })
    .post(body('username_email').custom(value => {
        console.log("value ", value);

        console.log(3)
        UserModel.findOne({ 'username_email': value }, (err) => {
            console.log(4);
            if (err) return res.status(409).send(err);

        })

    }), async(req, res, next) => {
        console.log(5)
        try {
            let newUser = new UserModel(req.body);

            let savedUser = await newUser.save();
            console.log(6);
            if (savedUser) return res.redirect('/users/registration?success=true');
            return next(new Error('Failed to save user for unknown reasons'))

        } catch (err) {
            return next(err)
        }

    })

module.exports = router

In my component on the front end I have a function in my fetch which will catch the error if there is one.

  handleErrors(response) {
    if (!response.ok) {
      console.log('This email exists!')
      throw Error(response.statusText);
    }
    return response;
  }


  handleSubmit(event) {
    event.preventDefault()

    var { username, password } = this.state

    var mailFormat = /^(([^<>()\[\]\\.,;:\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,}))$/
    var error = false

    if (!username.match(mailFormat)) {
      this.setState({ usernameError: true })
      error = true
    } else {
      this.setState({ usernameError: false })
    }

    if (password.length <= 8) {
      this.setState({ passwordError: true })
      error = true
    } else {
      this.setState({ passwordError: false })
    }

    console.log(`error ${error}`)
    if (error == false) {
      this.setState({ formError: false, formSuccess: true })
    }


    window.fetch('http://localhost:8016/users/registration', {
      method: 'POST',
      headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
      body: JSON.stringify({ username_email: username, password: password })
    })
      .then(this.handleErrors)
      .then(function (response) {
        console.log(`response ${response}`)
        return response.json()
      }).then(function (data) {
        console.log('User created:', data)
      }).catch(function (error) {
        console.log(error);
      });
  }

The console.log in the fetch, handleErrors is being registered in the console, but why isn't the error status a 409 like I indicated.

Closer excerpt of post route!

.post(body('username_email').custom(value => {
        console.log("value ", value);

        console.log(3)

Is this the problem? Node style should have a error and callback?

        UserModel.findOne({ 'username_email': value }, (err) => {
            console.log(4);
            if (err) return res.status(409).send(err);

        })

    }), async(req, res, next) => {
        console.log(5)
        try {
            let newUser = new UserModel(req.body);

            let savedUser = await newUser.save();
            console.log(6);
            if (savedUser) return res.redirect('/users/registration?success=true');
            return next(new Error('Failed to save user for unknown reasons'))

        } catch (err) {
            return next(err)
        }

    })

UPDATE

I tried Nick's solution but I get this:

MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }
    at Function.create (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/error.js:43:12)
    at toError (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/utils.js:149:22)
    at coll.s.topology.insert (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb/lib/operations/collection_ops.js:859:39)
    at handler (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/topologies/replset.js:1155:22)
    at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/mongodb-core/lib/connection/pool.js:397:18
    at process._tickCallback (internal/process/next_tick.js:61:11)
POST /users/registration 500 312.485 ms - 51
^C

Two things I am noticing:

I get back MongoError: E11000 duplicate key error collection: development.users index: email_1 dup key: { : null }

which is the error from having a duplicate email, but number one where is E-mail already in use message in the console from the promise? And two how can I pass the error status "res.status(409).send(err);" from the promise?

Upvotes: 0

Views: 1469

Answers (1)

Nick
Nick

Reputation: 352

The issue was that during your validation, you weren't returning the promise since the mongoose call is async. The rest the code ran before your validator was finished. I commented where you were missing the return.

router.route('/registration')
    .get(function(req, res) {
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })
    .post(body('username').custom(value => {      
        return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
          if (user) {
              return Promise.reject('E-mail already in use');
          }
        });
    }), async(req, res, next) => {

        try {
            let newUser = new UserModel(req.body)
            let savedUser = await newUser.save(err => {
              if (err) return res.json({ success: false, error: err })
              return res.json({ success: true })
            })

            if (savedUser) return res.redirect('/users/registration?success=true');
            return next(new Error('Failed to save user for unknown reasons'))
        } catch (err) {
            return next(err)
        }
    })

module.exports = router

UPDATE

Just read through express-validator docs. I think you would need to validate the errors during the request process

var router = require('express').Router()
var UserModel = require('../models/UserModel')
var { body, validationResult } = require('express-validator');  

router.route('/registration')
    .get(function(req, res) {
        UserModel.find({}, (err, users) => {
            if (err) res.status(500).send(err)
            res.json(users)
        })
    })
    .post(body('username').custom(value => {      
        return UserModel.findOne({ 'email': value }).then(user => { // Return Promise
          if (user) {
              return Promise.reject('E-mail already in use');
          }
        });
    }), async(req, res, next) => {
        // Checks for errors in validation
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
          return res.status(422).json({ errors: errors.array() });
        }

        try {
            let newUser = new UserModel(req.body)
            let savedUser = await newUser.save(err => {
              if (err) return res.json({ success: false, error: err })
              return res.json({ success: true })
            })

            if (savedUser) return res.redirect('/users/registration?success=true');
            return next(new Error('Failed to save user for unknown reasons'))
        } catch (err) {
            return next(err)
        }
    })

module.exports = router

Upvotes: 1

Related Questions