Registration and Activation Email with React/Next and Node

Learn how to create a registration feature together with sending an activation email with Nodemailer.

TODO: provide alt

Setup an initial project and checkout to version v1.0 of the project. Here is a link to the project: https://github.com/Jerga99/next-youtube-course/tree/v1.0

If you don't know how to set up the project follow my other video where it is well explained: https://youtu.be/VgUng7isvlY

Everything I am going to explain you can also watch in my youtube video here: https://youtu.be/53uJ01cXDx0

First let's explain a work flow of what we are going to implement:

  1. We want to send a user informations(username, email, password) from the register form to our api server endpoint http://localhost:3001/api/register in form of a POST request.
  2. On server we want to verify user data
  3. We need to verify if a user already exists. If user exists we will send back response informing that user is already registered.
  4. If a user doesn't exists we will store a user to DB as a "pending user"
  5. we will send an email to a user email address with validation link pointing to our other API endpoint that's responsible for an activation of an user account.
  6. after a user will click an activation link it will make a GET request to our other API endpoint on http://localhost:3001/api/activate/user/userID
  7. since a request contains a user ID we can get a pending user from our database, we can copy a user informations(email, password, username) and store them as a "normal" user. After that we can remove a pending user.
  8. Since a user is stored now as a normal user now he can log in through the login page.
  9. Entire flow you can find as a diagram here: https://balsamiq.cloud/snc4lx3/p74pkbl/r7EDD

Ok now to the coding part, let's start step by step. We will start with an implementation of a register endpoint.

First install body-parser and register it: npm install --save body-parser

const bodyParser = require('body-parser');
app.use(bodyParser.json());
server/index.js

Then we can proceed with creation of an endpoint to register.

app.post('/api/register', async (req, res) => {
  // Get user informations from a request
  const { email, username, password } = req.body;
  try {
    // Look if user or pending user already exists
    const rUser = await User.find({email});
    const pUser = await PendingUser.find({email});
    // If he does, return an error
    if (pUser || rUser) { return res.status(422).send('User is already registered!');}
    
    // Create a new pending user
    const newUser = new PendingUser({email, username, password});
    // Hash a password
    await newUser.hashPassword();
    // Send a confirmation email including a link to an activation endpoint
    // Link like: <a href="http://localhost:3000/api/activate/user/<userID>">Activate an account!</a>
    // Where <userID> is an ID of user that has been saved as a pending user
    await sendConfirmationEmail({toUser: newUser.data, hash: newUser.data._id })

    // Save a pending user to DB
    const user = await newUser.save();
    // Success response
    res.json({message: 'You have been registered.'});
  } catch(e) {
    // Error Response
    res.status(422).send(e.message);
  }
})
server/index.js

You can find a complete code here: https://github.com/Jerga99/next-youtube-course/tree/master

Now we need to know how to send an email. Let's take a look on sendConfirmationFunction

Also, don't forget to install nodemailer package: npm install --save nodemailer

const nodemailer = require('nodemailer');

exports.sendConfirmationEmail = function({toUser, hash}) {
  // Return promise in order to use async/await or "then"
  return new Promise((res, rej) => {
    // Create transporter object with gmail service
    const transporter = nodemailer.createTransport({
      service: 'gmail',
      // provide your gmail email: e.g -> test@gmail.com
      // provide your gmail password
      // you can create .env file for this, see the instructions below
      auth: {
        user: process.env.GOOGLE_USER,
        pass: process.env.GOOGLE_PASSWORD
      }
    })

    // Create a message you want to send to a user
    const message = {
      from: process.env.GOOGLE_USER,
      // to: toUser.email // in production uncomment this
      // While we are testing we want to send a message to our selfs
      to: process.env.GOOGLE_USER,
      subject: 'Your App - Activate Account',
      html: `
        <h3> Hello ${toUser.username} </h3>
        <p>Thank you for registering into our Application. Much Appreciated! Just one last step is laying ahead of you...</p>
        <p>To activate your account please follow this link: <a target="_" href="${process.env.DOMAIN}/api/activate/user/${hash}">${process.env.DOMAIN}/activate </a></p>
        <p>Cheers</p>
        <p>Your Application Team</p>
      `
    }

    // send an email
    transporter.sendMail(message, function(err, info) {
      if (err) {
        rej(err)
      } else {
        res(info)
      }
    })
  })
}
server/mailer.js

Few things important to mention.

1. Create .env file in root folder of your project with following content:

GOOGLE_USER=YOUR_GOOGLE_EMAIL // e.g test@gmail.com
GOOGLE_PASSWORD=YOUR_GOOGLE_PASSWORD // testtest
DOMAIN=YOUR_DOMAIN // e.g http://localhost:3000
.env

2. You need configure your Gmail account to allow less secure apps here. This is required in order to enable sending of emails.

Important Note: This is not a recommended way how to send an email! It's very good for explanation of this concept but not suitable for production!

In a real application you want to provide some of the authentication methods described in nodemailer docs in order to allow sending of an email through your app. I can explain how to do it in some of my other videos if there will be enough of the interest.

Now you are good to go! Restart your node server and let's try it out.

  1. Go to localhost:3000/register
  2. Provide your register data and confirm
  3. Check your email and click on the activation link
  4. You will be redirected to activation page.
  5. Go to localhost:3000/login and try to login

I hope everything is working for you. You can watch full video here: https://youtu.be/53uJ01cXDx0

For more courses and lectures please visit: eincode.com

Best,

Filip