DEV Community

Augusta Ehihebolo
Augusta Ehihebolo

Posted on

Easy and Clean Way to Use Fastest-Validator in Your Express.js App

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:

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}...`));
Enter fullscreen mode Exit fullscreen mode

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}...`));
Enter fullscreen mode Exit fullscreen mode

Example of dotenv file

.env.example

DB_username = 
DB_password = 
DB_name = 
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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(); 
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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`});
  },
};
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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}...));

Enter fullscreen mode Exit fullscreen mode




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:

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:

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:

Get Product by Id:

Delete product by Id:

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

connect with me:
Linkedin
Twitter

Top comments (0)