title: Cannot set headers after they are sent to the client with express-validator, express, mongoose and Next.js
body: <p>I am building a login/registration form using <a href="https://express-validator.github.io/docs/custom-validators-sanitizers.html" rel="nofollow noreferrer">express-validator</a> and <a href="https://mongoosejs.com/docs/queries.html" rel="nofollow noreferrer">mongoose</a> in <a href="https://nextjs.org/" rel="nofollow noreferrer">next.js</a>. </p>
  
  <p>Heard the best practice was to sanitize your data on the front <em>and</em> backend.</p>
  
  <p>I have some validations on the frontend (i.e. checking if an email via Regex and making sure a password in a particular length). </p>
  
  <p>But now I'd like to use <a href="https://express-validator.github.io/docs/custom-validators-sanitizers.html#custom-validator" rel="nofollow noreferrer">Custom validator</a> to check if a email exists in my mongodb database. </p>
  
  <pre><code>    .post(body('username').custom(value =&gt; {
          UserModel.findOne({ 'email': value }).then(user =&gt; {
              if (user) {
                  return Promise.reject('E-mail already in use');
              }
          });
      }), 
  </code></pre>
  
  <p>This is the rest of my code:</p>
  
  <pre><code>var router = require('express').Router()
  var UserModel = require('../models/UserModel')
  var { body } = require('express-validator');
  
  router
      .route('/registration')
      .get(function(req, res) {
          UserModel.find({}, (err, users) =&gt; {
              if (err) res.status(500).send(err)
              res.json(users)
          })
      })
      .post(body('username').custom(value =&gt; {
          UserModel.findOne({ 'email': value }).then(user =&gt; {
              if (user) {
                  return Promise.reject('E-mail already in use');
              }
          });
      }), async(req, res, next) =&gt; {
  
          try {
              let newUser = new UserModel(req.body)
  
              let savedUser = await newUser.save(err =&gt; {
                  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
  </code></pre>
  
  <p>And this is the error I'm getting:</p>
  
  <pre><code>Error: Failed to save user for unknown reasons
      at router.route.get.post (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/users/index.js:34:25)
      at process._tickCallback (internal/process/next_tick.js:68:7)
  Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
      at ServerResponse.setHeader (_http_outgoing.js:470:11)
      at ServerResponse.header (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:767:10)
      at ServerResponse.send (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:170:12)
      at ServerResponse.json (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:267:15)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/index.js:108:17
      at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:71:5)
      at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
      at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
      at next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
      at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:73:5)
      at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
      at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
      at Immediate.next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
      at Immediate.&lt;anonymous&gt; (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:635:15)
      at runCallback (timers.js:706:11)
      at tryOnImmediate (timers.js:676:5)
      at processImmediate (timers.js:658:5)
  </code></pre>
  
  <p>Also do I even need this when Mongoose provides when designing models/schema?</p>
  
  <pre><code>var mongoose = require('mongoose')
  var emailValidator = require('email-validator')
  var bcrypt = require('bcrypt') // hashing function dedicated for passwords
  
  const SALT_ROUNDS = 12
  
  var UserSchema = new mongoose.Schema(
    {
      username_email: {
        type: String,
        required: true,
        lowercase: true,
        index: { unique: true }, // I mean this!
        validate: {
          validator: emailValidator.validate,
          message: props =&gt; `${props.value} is not a valid email address`
        }
      },
      password: {
        type: String,
        required: true,
        trim: true,
        index: { unique: true },
        minlength: 8
      }
    },
    {
      timestamps: true
    }
  )
  
  UserSchema.pre('save', async function preSave(next) {
    var user = this
    var hash
    if (!user.isModified('password')) return next()
    try {
      hash = await bcrypt.hash(user.password, SALT_ROUNDS)
      user.password = hash
      return next()
    } catch (err) {
      return next(err)
    }
  })
  
  UserSchema.methods.comparePassword = async function comparePassword(candidate) {
    return bcrypt.compare(candidate, this.password)
  };
  
  module.exports = mongoose.model('User', UserSchema)
  </code></pre>
  
  <p>And if I don't does that mean checking if the email exists should be moved to the frontend? And If that's the case how would I approach that?</p>
  
  <p><strong>UPDATE</strong></p>
  
  <p>I tried Nick's suggestion but not sure why I'm still getting </p>
  
  <pre><code>`Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
  </code></pre>
  
  <p>`
  These are the updated routes:</p>
  
  <pre><code>router
      .route('/registration')
      .get(function(req, res) {
          console.log(0)
          UserModel.find({}, (err, users) =&gt; {
              console.log(1)
              if (err) res.status(500).send(err)
              console.log(2)
              return res.json(users)
              console.log(3)
          })
      })
      .post(body('email').custom(value =&gt; {
          console.log(4)
          UserModel.findOne({ 'email': value }).then(user =&gt; {
              console.log(5)
              if (user) {
                  console.log(6)
                  return Promise.reject('E-mail already in use');
              }
          });
      }), async(req, res, next) =&gt; {
          console.log(7)
          try {
              let newUser = new UserModel(req.body)
  
              let savedUser = await newUser.save(err =&gt; {
                  if (err) return res.json({ success: false, error: err })
                  console.log(8)
                  return res.json({ success: true })
              })
              console.log(9)
              if (savedUser) return res.redirect('/users/registration?success=true');
              console.log("savedUser ", savedUser);
              console.log(10)
              return next(new Error('Failed to save user for unknown reasons'))
  
          } catch (err) {
              return next(err)
          }
  
      })
  
  Note that pages will be compiled when you first load them.
  GET /_next/static/webpack/d691821e71bf01c860e6.hot-update.json 404 299.194 ms - 1862
  GET /_next/static/webpack/42c7a9cb77dec12fc8a3.hot-update.json 200 40.276 ms - 35
  4
  7
  9
  savedUser  undefined
  10
  POST /users/registration 200 21.490 ms - 422
  Error: Failed to save user for unknown reasons
      at router.route.get.post (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/users/index.js:42:25)
      at process._tickCallback (internal/process/next_tick.js:68:7)
  Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
      at ServerResponse.setHeader (_http_outgoing.js:470:11)
      at ServerResponse.header (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:767:10)
      at ServerResponse.send (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:170:12)
      at ServerResponse.json (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/response.js:267:15)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/server/index.js:108:17
      at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:71:5)
      at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
      at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
      at next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
      at Layer.handle_error (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/layer.js:73:5)
      at trim_prefix (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:315:13)
      at /Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:284:7
      at Function.process_params (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:335:12)
      at Immediate.next (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:275:10)
      at Immediate.&lt;anonymous&gt; (/Users/antoniopavicevac-ortiz/Dropbox/developer_folder/hillfinder/node_modules/express/lib/router/index.js:635:15)
      at runCallback (timers.js:706:11)
      at tryOnImmediate (timers.js:676:5)
      at processImmediate (timers.js:658:5)
  5
  6
  (node:68936) UnhandledPromiseRejectionWarning: E-mail already in use
  (node:68936) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
  (node:68936) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
  ^C
  </code></pre>