Returning status when saving to mulitple dbs in one api call

I’ve always been more of a frontend guy. Latley, I’ve been developing a backend using Node.js and Mongoose. When a user registers, I want to create a user (user db), create an email token (emailToken db) and then send an email in one method… and I’m not even sure if this is the right way to approach it.

I’ve been having a hard time trying to phrase this, so please excuse my brain barf. The question I have is based around returning a status when that endpoint is called. As far as I know, only one status can be returned, so what does a 200 actually mean here? In my code, it means all 3 things succeeded? I feel this approach is wrong. The situation can arise where a user is created, but the rest of the function fails… returning a 400 or whatever. That’s not ideal either. Logically, I feel like I want to return a 200 for user creation, then 200 for email token creation and a 200 for successfully sending an email.

I would appreciate any guidance.

  const user = new User(req.body);

  user
    .save()
    .then(user => {
      const emailToken = new EmailToken({
        _userId: user._id,
        token: crypto.randomBytes(16).toString("hex")
      });

      emailToken
        .save()
        .then(token => {
          const transporter = nodemailer.createTransport({
            service: "Sendgrid",
            auth: {
              user: USERNAME,
              pass: PASSWORD
            }
          });

          const mailOptions = {
            from: "[email protected]",
            to: user.email,
            subject: "Account Verification Token",
            text:
              "Hello,nn" +
              "Please verify your account by clicking the link: nhttp://" +
              req.headers.host +
              "/verify/" +
              token.token +
              ".n"
          };

          transporter.sendMail(mailOptions, (err, info) => {
            if (err) {
              return res.status(409).send({
                user,
                error: "Could not send verification email. " + err.message
              });
            }

            res.status(200).send({
              user,
              token: token.token,
              success:
                "A verification email has been sent to " + user.email + "."
            });
          });
        })
        .catch(error => {
          res.status(409).send({
            user,
            message: "Could not send verification email."
          });
        });
    })
    .catch(error => {
      if (error.name === "MongoError" && error.code === 11000) {
        return res.status(400).send({
          success: false,
          message: "Username or email already exists."
        });
      }
      res.status(400).send(error);
    });
});

Answer

what does a 200 actually mean here?

It means that the user creation, token generation and sending mail are successful.

I feel this approach is wrong. The situation can arise where a user is created, but the rest of the function fails.

You can delete the user on email failure

I feel like I want to return a 200 for user creation, then 200 for email token creation and a 200 for successfully sending an email.

You can only send response once, unless its a stream.

you can rewrite your promises to something like this to improve readability and prevent the callback hell. you can check promise-do-and-donts here

const nodemailer_credentials = {
    service: "Sendgrid",
    auth: {
        user: USERNAME,
        pass: PASSWORD
    }
};


const user = new User(req.body);
let _token, _user;

user
    .save()
    .then(user => {
        _user = user;

        return new EmailToken({
            _userId: user._id,
            token: crypto.randomBytes(16).toString("hex")
        }).save();
    })
    .then(token => {
        _token = token;
        return nodemailer.createTransport(nodemailer_credentials);
    })
    .then(transporter => {
        return transporter.sendMail(mailOptions, (err, info) => {
            if (err) {
                const mailError = new Error('MailerError');
                      mailError.details = `Could not send verification email. ${err.message}`

                throw(mailError);
            }
            return;          
        });
    })
    .then(() => {
        res
            .status(200)
            .send({
                user: _user,
                token: _token.token,
                success: `A verification email has been sent to ${user.email}.`
            })
    })
    .catch(error => {
        if (error.message === 'MailerError') {
            return res.status(400).send({
                user: _user,
                message: error.details
            });
        }

        if (error.name === 'MongoError' && error.code === 11000) {
            return res.status(400).send({
                success: false,
                message: `Username or email already exists.`
            });
        }

        res.status(400).send(error);
    });