MongoDB Server-side validation

Server-side validation can be added at the top of any server-side route. The code below shows validation for adding a new car.

router.post(`/cars`, (req, res, next) => 
{
// validate input
const today = new Date();
if(!/^[a-zA-Z]+$/.test(req.body.model))
{
next(createError(400, `Model must be a string`))
}
else if(!/^[a-zA-Z]+$/.test(req.body.colour))
{
next(createError(400, `Colour must be a string`))
}
else if(req.body.year < 1990) // between 1990 and the current year
{
next(createError(400, `Year needs to be greater than or equal to 1990`))
}
else if(req.body.year > today.getFullYear())
{
next(createError(400, `Year needs to be this year or less`))
}
else if(req.body.price < 1000 || req.body.price > 100000) // between €1000 and €100000
{
next(createError(400, `Price needs to be between €1000 and €100000`))
}
else // input is valid
{
carsModel.create(req.body)
.then(data =>
{
res.json(data)
})
.catch(err => next(err))
}
})

 

If an error occurs, then the errorMessage can be handled in the axios call on the client-side, as shown below. If there is an error, then we deal with it. Otherwise, we proceed as normal.

axios.post(`${SERVER_HOST}/cars`, carObject)
.then(res =>
{
setRedirectToDisplayAllCars(true)
})
.catch(err => console.log(`${err.response.data}\n${err}`))

Open the mongoDB project from the previous section in these notes. Change the code so that all database querys are validated in the server-side router code with the following rules:

"Cars" Worked Example

The full project code for the "Cars" Worked Example that is described below can be downloaded from this link.

This code adds server-side validation to all mongoDB interactions.

Client-Side

There are no changes to the client-side code.

Server-Side

server/routes/cars.js

const router = require(`express`).Router()
const createError = require('http-errors')
const carsModel = require(`../models/cars`)

// read all records
router.get(`/cars`, (req, res, next) => 
{   
    //user does not have to be logged in to see car details
    carsModel.find({})
    .then(data => 
    {
        res.json(data)
    })
    .catch(err => next(err))
})


// Read one record
router.get(`/cars/:id`, (req, res, next) => 
{
    carsModel.findById(req.params.id)
    .then(data => 
    {
        res.json(data)
    })
    .catch(err => next(err))
})


// Add new record
router.post(`/cars`, (req, res, next) => 
{
    // validate input
    const today = new Date();
    if(!/^[a-zA-Z]+$/.test(req.body.model))
    {
        next(createError(400, `Model must be a string`))
    }
    else if(!/^[a-zA-Z]+$/.test(req.body.colour))
    {
        next(createError(400, `Colour must be a string`))      
    }
    else if(req.body.year < 1990)     // between 1990 and the current year
    {
        next(createError(400, `Year needs to be greater than or equal to 1990`))      
    }
    else if(req.body.year > today.getFullYear())
    {
        next(createError(400, `Year needs to be this year or less`))      
    }
    else if(req.body.price < 1000 || req.body.price > 100000)       // between €1000 and €100000                
    {
        next(createError(400, `Price needs to be between €1000 and €100000`))
    }
    else // input is valid
    {    
        carsModel.create(req.body)
        .then(data => 
        {
            res.json(data)
        })
        .catch(err => next(err))
    }
})


// Update one record
router.put(`/cars/:id`, (req, res, next) => 
{
    // validate input
    const today = new Date();
    if(!/^[a-zA-Z]+$/.test(req.body.model))
    {
        next(createError(400, `Model must be a string`))
    }
    else if(!/^[a-zA-Z]+$/.test(req.body.colour))
    {
        next(createError(400, `Colour must be a string`))       
    }
    else if(req.body.year < 1990)     // between 1990 and the current year
    {
        next(createError(400, `Year needs to be greater than or equal to 1990`))        
    }
    else if(req.body.year > today.getFullYear())
    {
        next(createError(400, `Year needs to be this year or less`))       
    }
    else if(req.body.price < 1000 || req.body.price > 100000)       // between €1000 and €100000                
    {
        next(createError(400, `Price needs to be between €1000 and €100000`)) 
    }
    else // input is valid
    {
        carsModel.findByIdAndUpdate(req.params.id, {$set: req.body})
        .then(data => 
        {
            res.json(data)
        }) 
        .catch(err => next(err))
    }
})


// Delete one record
router.delete(`/cars/:id`, (req, res, next) => 
{
    carsModel.findByIdAndDelete(req.params.id)
    .then(data =>
    {
        res.json(data)
    })    
    .catch(err => next(err))
})

module.exports = router

Every route has the same validation structure, as colour-coded in the code above.

Appropriate validation is done at the start of each route.

If the input is invalid, then a custom errorMessage will be returned to the client-side axios() method that called this router.post() method.

If the data passes all of the validation tests, then it is valid. In this case, the route behaves as normal. It interacts with the database and returns some data to the client-side axios() method that called it.

 
<div align="center"><a href="../versionC/index.html" title="DKIT Lecture notes homepage for Derek O&#39; Reilly, Dundalk Institute of Technology (DKIT), Dundalk, County Louth, Ireland. Copyright Derek O&#39; Reilly, DKIT." target="_parent" style='font-size:0;color:white;background-color:white'>&nbsp;</a></div>