Easy and Clean Way to Use Fastest-Validator in Your Express.js App
Tags: javascript, tutorial, node, mongodb
Published: 2022-12-23 06:18:31


## Introduction

Data validation is a way of checking the integrity, structure and accuracy of data before sending to the database. It is a way of ensuring that your data have been cleaned before entering the database to assure data quality, that is, that they are both accurate and valuable. You do not want to go to bed thinking of the possible wrong data users might have sent to the database. Therefore, data validation must be placed as a topmost priority while creating your API.

**Fastest-Validator** is one of the different types of validation libraries used for data validation. All thanks to everyone behind the numerous validation libraries that have made data validation a lot easier lately. 

In this tutorial, we shall understand what the fastest-validator is, its advantages over other types of validation libraries, and learn the simple and clean way to validate data using the fastest-validator. We shall achieve this by building a product management API using Express.js, MongoDB database and the fastest-validator library to validate every input.


## **Prerequisites**

This tutorial is a hands-on-tutorial, code along with me to get the most out of it and ensure you have:

- [Node.js](https://nodejs.org/en/) installed
- Knowledge of [Express.js](https://expressjs.com/)
- knowledge of [MongoDB](https://www.mongodb.com).
- Your favorite code editor opened
- [Postman](https://www.postman.com/) or [Insomnia](https://insomnia.rest) installed.


## **What is Fastest-Validator**

Fastest-validator is a validation library for Node.js, browser, and Deno used to validator input data to ensure clean and quality data in our database. It is very easy to use. Some of its key features are:

- It's super fast.
- It has custom validators & aliases.
- nested objects & array handling.
- strict object validation.
- customizable error messages.
- It has no dependencies.

## **Advantages of Fastest-Validator Over Other Validation Libraries**

Compared to other popular validation libraries, the fastest-validator is super fast.
It has a customizable error message and a programmable error object
 

## **Project Set-up**

Without further ado, let’s start our project by creating the project directory and installing the necessary dependencies. 

First, create your project directory:


    mkdir Fastest_Val_Express

Open the folder in your code editor and initialize npm by running `npm init -y`


## **Install Dependencies**

Now that we have set-up our project by initializing npm, let’s install the necessary dependencies to get us started.

Go to your terminal, run:


    npm install express dotenv

Take a deep breath, in a few seconds, the installation will be done.

After installing express and dotenv, create a **src** folder, and inside the folder, create **models**, **validations**, **controllers**, **routes** folders, and **app.js** file; create a **.env** file in the root directory. Then, create a product file inside each of the folders in the **src** folder.

**Your project directory should look like this:**

**Fastest_Val_Express**
│
└─**src**
│ └───controllers
│ │            └───product.controller.js
│ │
│ └───models
│ │            └───product.model.js
│ │
│ └───routes
│ │            └───product.route.js
│ │
│ └───validations
│ │            └───product.vaidation.js
│ │
│ └───**app.js**
│ │ 
└─**.env**


##**Create Server**

Moving on, we need to create the server

Go to your **app.js** file, then require express and create the server.

**src/app.js**

    const express = require('express');
    const app = express();
    
    const port = 5000;
    
    app.listen(port, console.log(`server is listening on port: ${port}...`));

You can start the server by running the command `node src/app.js`, but to avoid restarting our project every time we make any change, let's install nodemon to help monitor our project for any change and automatically restart the server.

In your terminal, run:  `npm install --save-dev nodemon` 

Take another deep breath while nodemon is installing, it only takes a few seconds. Once you have installed nodemon, head to your package.json file, and add `"dev": "nodemon src/app"` to the scripts object, then run `npm run dev`  to re-start the server.

## **Connect Database**
 
Once the server is up and running, you need to set up and connect the database. Since we will be using MongoDB database, install mongoose with the command:  `npm i mongoose`, then set up and connect the database. 

**N.B:** as a best practice, put your database-sensitive information in your .**env** file, require the file in your app.js file so you can have access to the environment variables.

**src/app.js**

    const express = require('express');
    const mongoose = require('mongoose');
    const { config } = require('dotenv');
    config();
    
    const app = express();
    
    app.use(express.json());
    
    //connect database
    mongoose.set("strictQuery", false);
    mongoose
      .connect(`mongodb+srv://${process.env.DB_username}:${process.env.DB_password}@cluster0.kngtf.mongodb.net/${process.env.DB_name}?retryWrites=true&w=majority`)
      .then(console.log('database connected...'))
      .catch((err) => console.log(err));
    
    const port = 5000;
    
    app.listen(port, console.log(`server is listening on port: ${port}...`));


Example of dotenv file

**.env.example**

    DB_username = 
    DB_password = 
    DB_name = 


Having cleared that up, let’s create our product schema; we want the product name, the category it belongs to and the price. 

Go to your **product.model.js** file in your models' folder, and create your product schema.

**product.model.js**

    const mongoose = require('mongoose');
    const ProductSchema = new mongoose.Schema({
      productName: {
        type: String,
        trim: true,
      },
      category: {
        type: String,
        trim: true,
      },
      amount: {
        type: Number,
      },
    },
    { timestamps: true },
    );
    module.exports = mongoose.model('Product', ProductSchema);
    

With that out of the way, we need to validate every data going to our database using the **fastest-validator** library.

Install the fastest-validator library with the command: `npm install fastest-validator`  

Go to your **product.validation.js** file in your validation folder, and require the fastest-validator library, then instantiate Validator.

**src/vaidation/product.validation.js**

    const Validator = require("fastest-validator");
    const validate = new Validator(); 

Using the Fastest-Validator library is quite easy; the simple, clean and fastest way to use it is to compile the schema first to a compiled "checker" function. After that, to validate your object, just call this "checker" function. The error reporting is pretty detailed and straightforward.

Now, head over to your **product.validation.js file,** write your schema, and compile it to a compiled "checker" function, then export the module so you can call the checker function in your controller.

**src/vaidation/product.validation.js**

    const Validator = require("fastest-validator");
    const validate = new Validator(); 
    
    const productVaidationSchema = {
        productName: {
            type: "string", 
            min: 3, 
            max: 255,
            label: "Product name error"
        },
        category: { 
            type: "string", 
            min: 3, 
            max: 255,
            label: "Product category",
            optional: true
        },
        amount: {
            type: "number",
            positive: true, 
            integer: true,
            label: "Amount"
        }
    };
    const check = validate.compile(productVaidationSchema);
    
    module.exports = check

If you have successfully accomplished the steps so far, you should take a chilled glass of wine and relax, knowing that every data going to your database is accurate and valuable. Cheers to that!


## **Project Logic**

Now, we need to write our logics.

Go to your **product.controller.js file** in your controller folder, and require both the **product.validation.js** and **product.model.js** files.

For the createProduct and updateProduct functions, we need to check if the input data we are sending to the database meet the validation conditions we specified in our **product.validation.js** file, if "noError" is not true, then all validation conditions are not met, and so, it returns the error message else we proceed.

**src/controllers/product.controller.js**

    const Product = require('../models/product.model');
    const check = require('../validations/product.validation');
    const  ObjectId = require('mongoose').Types.ObjectId;
    
    module.exports = {
      //create product function
      createProduct: async (req, res) => {
        //check if validation conditions are met
        const noError = check(req.body);
        if (noError != true) {
          return res.status(400).json({ status: 400, error: noError});
        };
        //check if product already exists
        const productExist = await Product.findOne({ productName: req.body.productName});
        if (productExist) {
          return res.json({msg: "Product already exist"})
        };
       //create the product
        const product = await Product.create(req.body);
        res.status(201).json({ msg: 'successfully added a product', product });
      },
    
      //get all products function
      getAllProducts: async (req, res) => {
        const products = await Product.find({});
        res.status(200).json({ products, number: products.length });
      },
    
      //get product by Id function
      getProductById: async (req, res) => {
          const productId = req.params.id;
          const product = await Product.findById(productId);
          //check if product Id is valid
          if (!product || ObjectId.isValid(productId) == false) {
          return res.status(404).json({
                 message: `No product with id: ${productId}` 
         });
      }
        res.status(200).json({ message: product });
      },
    
      //update product by Id function
      updateProductById: async (req, res) => {
        const productId = req.params.id;
        const product = await Product.findByIdAndUpdate(productId, req.body);
        //check if Id is valid
        if (!productId || ObjectId.isValid(productId) == false) {
        return res.status(404).json({ 
               message: `No product with id: ${productId}` 
       });
    } 
        //check if validation conditions are met
        const error = check(req.body);
        if (noError != true) {
          return res.status(400).json({ status: 400, error: noError});
        };
        res.status(200).json({ message: `Product successfuly updated` });
      },
    
      //Delete product by Id
      deleteProductById: async (req, res) => {
        const productId = req.params.id;
        const product = await Product.findByIdAndDelete(productId);
        //check if Id is a valid
        if (!product || ObjectId.isValid(productId) == false) {
       return res.status(404).json({
              message: `No product with id: ${productId}` });
      }
        res.status(200).json({message: `Product successfuly deleted`});
      },
    };

Now that we have completed our logic, let’s work on our routes. 

Go to your **product.routes.js** file in your route folder, require Router from express, and all the functions from your controller, then create the routes for the respective functions and export your route.

**src/routes/product.route.js**

    const route = require('express').Router();
    const {
      getAllProducts,
      createProduct,
      getProductById,
      updateProductById,
      deleteProductById,
    } = require('../controllers/product.controller');
    
    route.post('/product', createProduct);
    route.get('/products', getAllProducts);
    route.get('/product/:id', getProductById);
    route.patch('/product/:id', updateProductById);
    route.delete('/product/:id', deleteProductById);
    
    module.exports = route;

Now, take another deep breath because we're almost done, all that is left is to call our route in the app.js file so that our app can access all the routes. 

Go to your app.js file, require the routes folder and expose the routes to be used by the app.

**src/app.js**

    const express = require('express');
    const mongoose = require('mongoose');
    const route = require('./routes/product.route');
    const { config } = require('dotenv');
    
    config();
    
    const app = express();
    
    //connect database
    mongoose.set("strictQuery", false);
    mongoose
      .connect(`mongodb+srv://${process.env.DB_username}:${process.env.DB_password}@cluster0.kngtf.mongodb.net/${process.env.DB_name}?retryWrites=true&w=majority`)
      .then(console.log('database connected...'))
      .catch((err) => console.log(err));
    
    const port = 5000;
    
    app.use('/api/v1', route);
    
    app.listen(port, console.log(`server is listening on port: ${port}...`));

##  **Testing our API**

Here is the moment of truth, let's test our API routes to see if they work appropriately.
Head over to your postman or insomnia, and test all the routes.

 **Create Product:**
![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139792538_post.png)

![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139750808_post3.png)

![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139722931_post2.png)

![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139815143_post4.png)

Fastest-validator gives you a detailed error report; for the **Create Product** route, we were only able to successfully create a product when all validation conditions were met.



**Update Product by id:**
![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139966926_update1.png)

![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139992168_update2.png)

![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671139941210_update.png)

Like the **Create Product**, when we try to update a particular product by its Id, it is only successful once it fulfils all validation conditions. Notice that it was also successful without the **category** because category in our validation is not required.



**Get products:**
![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671140047683_get.png)



**Get Product by Id:**
![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671140093543_getid.png)



**Delete product by Id:**
![](https://paper-attachments.dropboxusercontent.com/s_E09D6782AAF10B6FA40BC349DC880388575D7449466248BC76CF60F1450CB71A_1671140134807_delete.png)

This returns a message "Product successfully deleted" to let you know that the delete process was successful.


## **Conclusion**

The first two things to consider in choosing a validation library are the flexibility of the validation rules and speed. Understanding the pros and cons of a validation library before using it is key to getting the best out of it.
In this tutorial, we learnt what data validation and fastest-validator are; we further briefly looked at some of the advantages of fastest-validator over other types of validation libraries. We demonstrated the clean and easy way to use the fastest-validator library in our Express.js API/application by building a product management API using the express.js framework, MongoDB database, and the fastest-validator library. At the end of the tutorial, the API routes were tested and the results were as expected as seen in the tutorial.


**Get the complete code** [**here**](https://github.com/Augusta-E/Fastest_Val_Express)

**connect with me:**
[Linkedin](https://www.linkedin.com/in/augusta-e/)
[Twitter](https://twitter.com/EhiheboloA)