Priyanka Phillips
Priyanka Phillips in Tutorials
Fri Jul 19 2019 · 24 min read

Create a REST API [Part 3]: User Registration and Validation

In this tutorial, you will create a user registration route for your API, validate input passed to the route and add a user to your PostgreSQL database. This is part 3 of a series of tutorials on building a REST API in Node with ExpressJS, KnexJS, and PostgreSQL.

More From This Series

• Part 3 - User Registration and Validation
Create a REST API [Part 3]: User Registration and Validation

Download the code for this series

Before you create your register route, you should decide what you'll require for your own projects. You might want usernames instead of email addresses for example. In this tutorial we register users with email addresses and a password.

1. Validation

When a user registers with our API, we will need to do some basic checks to make sure they are using a valid email address, their password is long enough and password1 and password2 match before entering them in the database. We will create our own validation as well as using the validator package from NPM. Install it now:

npm i validator

Now lets setup our validation files so we can bring them into our project later on. Create a folder called validation in your root project directory and create two files there called 'checkForEmpty.js' and 'register.js'.

// simple-api/validation/checkForEmpty.js
const ifEmpty = field => {
  try {
    let result = false;

    if (
      field === undefined ||
      field === null ||
      (typeof field === "string" && field.trim().length === 0) ||
      (typeof field === "object" && Object.keys(field).length === 0)
    )
      result = true;

    return result;
  } catch (err) {
    return err;
  }
};

module.exports = ifEmpty;

‘checkForEmpty.js’ contains a function which accepts a value and decides whether it is empty or not

// simple-api/validation/register.js
const Validator = require("validator");
const ifEmpty = require("./checkForEmpty");

module.exports = function checkRegistrationFields(data) {
  // An errors object is created
  let errors = {};

  // If data.email is not empty, data.email = data.email
  // else if empty, data.email = ""
  data.email = !ifEmpty(data.email) ? data.email : "";
  data.password1 = !ifEmpty(data.password1) ? data.password1 : "";
  data.password2 = !ifEmpty(data.password2) ? data.password2 : "";

  if (Validator.isEmpty(data.email)) {
    errors.email = "Email is required";
  }
  if (!Validator.isEmail(data.email)) {
    errors.email = "Email address is invalid";
  }
  if (Validator.isEmpty(data.password1)) {
    errors.password1 = "Password is required";
  }
  if (!Validator.isLength(data.password1, { min: 8, max: 120 })) {
    errors.password1 = "Passwords must be greater than 8 characters";
  }
  if (Validator.isEmpty(data.password2)) {
    errors.password2 = "Confirmation password is required";
  }
  if (!Validator.equals(data.password1, data.password2)) {
    errors.password2 = "Both password fields must match";
  }

  // Return the errors from the checkRegistrationFields function
  // and use the ifEmpty function to check if the errors object is empty
  return {
    errors,
    isValid: ifEmpty(errors)
  };
};

‘register.js’ uses validator and the ‘ifEmpty’ function we just created in ‘checkForEmpty.js’ to ensure that all the entries entered by the user while registering are valid.

2. Create the register route

In your users.js file, bring in bcrypt, crypto, your database.js file and the validation functions we just created.

// simple-api/api/routes/users.js
const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const crypto = require("crypto");
const database = require("../../database");

// Validation
const checkRegistrationFields = require("../../validation/register");

Remove the '/' route we created in part 1 of this series and create a new post route for '/register'. Using our validation functions we check that input from users is valid.

// Register route
router.post("/register", (req, res) => {

  // Ensures that all entries by the user are valid
  const { errors, isValid } = checkRegistrationFields(req.body);

  // If any of the entries made by the user are invalid, a status 400 is returned with the error
  if (!isValid) {
    return res.status(400).json(errors);
  }

Using crypto.randomBytes, we generate a random token with a size of 48 bytes that we will use in the next tutorial to verify users email addresses

let token;
crypto.randomBytes(48, (err, buf) => {
  if (err) throw err;
  token = buf
    .toString("base64")
    .replace(/\//g, "") // Because '/' and '+' aren't valid in URLs
    .replace(/\+/g, "-");
  return token;
});

Note: In production, you should use something more secure than ‘crypto.randomBytes’ to generate tokens.

Next we'll add our database function which inserts the users email, password, registration date, token, the date the token was created, whether the user is verified or not and if the token has been used before. With a salt factor of 12 we hash the users password with bcrypt so that we don't just store it as plain text in the database.

  bcrypt.genSalt(12, (err, salt) => {
    if (err) throw err;
    bcrypt.hash(req.body.password1, salt, (err, hash) => {
      if (err) throw err;
      database("users")
        .returning(["id", "email", "registered", "token"])
        .insert({
          email: req.body.email,
          password: hash,
          registered: Date.now(),
          token: token,
          createdtime: Date.now(),
          emailverified: "f",
          tokenusedbefore: "f"
        })
        .then(user => {
          // This is where the api returns json to the /register route
          // Return the id, email, registered on date and token here
          // Sending the user's token as a response here is insecure,
          // but it is useful to check if our code is working properly
          res.json(user[0]);
        })
        .catch(err => {
          errors.account = "Email already registered";
          res.status(400).json(errors);
        });
    });
  });
});
module.exports = router;

Note: The salt factor you use depends on your application requirements. While choosing a higher number might provide more security, it comes at a price of computing power. So ultimately, it’s up to you to decide what works for your application based on how much resources you have (computing power). You must also remember that, as the complexity of your password verification methods increase, your application is more vulnerable to Denial-Of-Service attacks.

Find the complete code for the users.js file here.

3. Testing the /register route

We are just about done, but let's make sure your new /register route is working. With your server running, open up postman and enter 'localhost:3000/v1/users/register' in the address bar. Select 'Body' -> 'raw' and 'JSON(application/json)' and enter in the following json:

{
  "email": "tim@example.com",
  "password1": "12345678",
  "password2": "12345678"
}

Press 'Send' and if everything is working properly, you should see:

temptemp

Have a look at the users table in your database with pgAdmin and see if anything changed:

temptemp
Pretty cool! Posting to your users route with postman created a new entry in your database with the email you entered, a hashed password and even a unique user id.

Conclusion

This API is starting to take shape! When the /register endpoint is hit, the input is validated, the password is hashed and everything is stored in a PostgreSQL database. You can see how you can really start to build on to this. In the next tutorial in this series we will verify our user has registered with a valid email address by sending them a token using Amazon's simple email server (Amazon SES).

Leave a comment below and share this tutorial!