Express Validator body not function - node.js

I am working to validate data input from an API call using express-validator version 6.11.1 and every time I validate using either check or body, I get the error below:
TypeError: body(...).not(...).IsEmpty is not a function
I created a helper called validator.js with the code below
const { body, validationResult } = require('express-validator')
const bcrypt = require('bcrypt')
const signupValidation = () => {
return [
body('firstname')
.not().IsEmpty().withMessage('Firstname field is required'),
body('lastname')
.not().IsEmpty().withMessage('Lastname field is required')
]
}
const validate = (req, res, next) => {
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
const extractedErrors = []
errors.array().map(err => extractedErrors.push({ msg: err.msg }))
res.status(200).json({
statusCode: 422,
error: extractedErrors
})
}
module.exports = {
signupValidation,
validate
}
The route where I am calling it looks like
const { signupValidation, validate } = require('../../helpers/validator')
//Endpoint to create new merchant
router.post('/account/create-merchant', signupValidation(), validate, async (req, res) => {
res.status(200).json({
statusCode: 201,
message: req.body
})
})
Sample Data from the API
{
"firstname": "",
"lastname": "Jon",
"phone" : "*****",
"email" : "oayayayaya",
"password": "******"
}
Can you please guide me on what to do to solve the error message (TypeError: body(...).not(...).IsEmpty is not a function)

I think it should be isEmpty() instead of IsEmpty(), try this:
const signupValidation = () => {
return [
body('firstname')
.not().isEmpty().withMessage('Firstname field is required'),
body('lastname')
.not().isEmpty().withMessage('Lastname field is required')
]
}
Check the doc here

Related

400 Bad Request for POST request (Node)

I'm trying to sign in but getting 400 Bad Request & "Wrong credentialsss".
app.post("/signin", (req, res) => {
const {email, password} = req.body;
db.from ('login').select('*').eq("email", email)
.then(data => {
const isValid = bcrypt.compareSync(password, data[0].hash)
if (isValid) {
return db.from ('users').select('*')
.eq ("email", email)
.then(resp => res.json(resp[0]))
.catch(err => res.status(400).json("failed to get user"))
} else {
res.status(400).json("wrong credentials")
}
})
.catch(err=>res.status(400).json("wrong credentialsss")) // <-- HERE
})
However, when I update the code as below, I can successfully fetch the data. I guess the problem is the middle part but have no idea.
app.post("/signin", (req, res) => {
const {email, password} = req.body;
db.from ('login').select('*').eq("email", email)
.then(data => res.json(data))
.catch(err=>res.status(400).json("wrong credentialsss"))
})
EDIT: This is what I get from second code
{
"error": null,
"data": [
{
"id": 1,
"hash": "$2a$10$JJ/i0c1bcu/1ZcV8f8DSGOipZUm5FLTGmvEygPjdpny3k0DI9/jrC",
"email": "admin#gmail.com"
}
],
"count": null,
"status": 200,
"statusText": "OK"
Tried to log the error
"message": "Cannot read properties of undefined (reading 'hash')",
"error": {}
Try replacing "data[0].hash" with "data.data[0].hash"
You receive a "data" object in the .then and then need to access the "data" property that contains the array of objects
.then(data => {
const isValid = bcrypt.compareSync(password, data.data[0].hash)
if (isValid) {
return db.from ('users').select('*')
.eq ("email", email)
.then(resp => res.json(resp[0]))
.catch(err => res.status(400).json("failed to get user"))
} else {
res.status(400).json("wrong credentials")
}
})
Sorry, I wanted to comment, but my reputation prevents me. Hence asking this question here. Instead of printing data[0].hash, did you try printing data[0] or data, just to ensure you are getting it in the right format and need no conversions.

POST going directly to the catch error and not saving data using mongoose, MongoDB, NodeJS, and Express

I already tried some possible solutions and even created and wrote the code again but I am still getting errors. I have created a diminute version of my whole code which connects to the database using Mongoose but after the Schema is created and I import the model in places-controllers my data that I write in POSTMAN goes directly to:
FYI: In this case I want POST request from createPlace to properly work.
Data entry: URL: http://localhost:5000/api/places/
{
"title": "Punta Arena Stfdsfdsfsdfop",
"description": "One stop Stop. Does not have tr12affic lights.",
"busrespect": "12ysdfdsfsfes",
"address": "Avenida Solunna",
"creator": "52peru soiflsdjf36"
}
OUTPUT:
{
"status": "error caught"
}
which is what I told the program to define if the try did not work.
IN app.js I have the following code:
const express= require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const placesRoutes = require("./routes/places-routes");
const HttpError = require ("./models/http-error");
const app = express();
app.use(bodyParser.json());
app.use('/api/places', placesRoutes);
app.use((req, res, next) => {
const error= new HttpError('Route not available. Try something different?', 404);
throw error;
});
app.use((error, req, res, next) =>{
if (res.headerSent) {
return next(error);
}
res.status(error.code || 500)
res.json({message: error.message || "An unknown error occured! Sorry" });
});
url = '<mongo_url>'
mongoose.connect(url, {useNewUrlParser: true}).then(()=>{
console.log("Connected to database")
app.listen(5000);
}).catch(erro => {
console.log(erro)
});
In places-routes.js I have the following code:
const express = require('express');
const {check} = require('express-validator')
const placesControllers=require('../controllers/places-controllers');
const router = express.Router();
router.get('/:pid', placesControllers.getPlaceById );
router.get('/user/:uid',placesControllers.getPlacesByCreatorId );
router.post('/' ,[
check('title')
.not()
.isEmpty(),
check('description').isLength({ min: 5 }),
check('address')
.not()
.isEmpty()
],
placesControllers.createPlace);
router.patch('/:pid', [
check('title')
.not()
.isEmpty(),
check('description').isLength({ min: 5 })
] , placesControllers.updatePlace );
router.delete('/:pid', placesControllers.deletePlace);
module.exports=router;
In places-controllers.js I have the following code:
const HttpError = require('../models/http-error');
const { validationResult } = require('express-validator');
//const getCoordsForAddress= require('../util/location');
const BusStop = require('../models/place');
let INITIAL_DATA = [
{
id: "p1",
title: "Samoa Stop",
description: "My first bus stop in Lima",
//location: {
// lat: 40.1382,
// lng:-23.23
// },
address: "Av. La Molina interseccion con calle Samoa",
busrespect: "yes",
creator: "u1"
}
];
const getPlaceById = (req, res, next) => {
const placeId = req.params.pid // Accessing the p1 in pid URL scrapping {pid:'p1'}
const place= INITIAL_DATA.find(p => { //find method goes over each element in the array, the argument p represents the element where find loop is
return p.id ===placeId
});
if (!place) {
const error= new HttpError('No bus stop found for the provided ID.', 404);
throw error;
}
res.json({place: place});
};
const getPlacesByCreatorId = (req, res, next)=> {
const userId = req.params.uid;
const places = INITIAL_DATA.filter(p=>{ //filter to retrieve multiple places, not only the first one
return p.creator ===userId;
});
if (!places || places.length===0) {
return next(
new HttpError('Could not find bus stops for the provide user id', 404)
);
}
res.json({places});
};
const createPlace = async (req, res,next) => {
const errors = validationResult(req);
if (!errors.isEmpty()){
return next(new HttpError ('Invalid bus stop please check your data', 422));
}
//const { title, description, busrespect, address, creator } = req.body; //erased location for now.
/* let place = new BusStop({
title: req.body.title,
description: req.body.description,
busrespect: req.body.busrespect,
address : req.body.address,
creator: req.body.creator
})
awaitplace.save()
.then(response=>{
res.json({
message : "Employee added sucessfully!"
})
})
.catch(err=>{
res.json({
message : "An error has occured!"
})
})
} */
const { title, description, busrespect, address, creator } = req.body;
try {
await BusStop.create({
title:title,
description: description,
busrespect:busrespect,
address: address,
creator: creator
});
res.send({status: "ok"});
} catch(error) {
res.send({status:"error caught"});
}
};
const updatePlace = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()){
console.log(errors);
throw new HttpError ("Invalid inputs passed, please check your data ", 422);
};
const { title, description } = req.body;
const placeId = req.params.pid;
const updatedPlace = { ...INITIAL_DATA.find(p => p.id === placeId)};
const placeIndex = INITIAL_DATA.findIndex(p => p.id === placeId);
updatedPlace.title = title;
updatedPlace.description = description;
INITIAL_DATA[placeIndex] = updatedPlace;
res.status(200).json({place: updatedPlace});
};
const deletePlace = (req, res, next) => {
const placeId = req.params.pid;
if (!INITIAL_DATA.find(p=> p.id ===placesId))
throw new HttpError('Could not find a bus stop for that ID ')
INITIAL_DATA = INITIAL_DATA.filter(p=> p.id !== placeId)
res.status(200).json({message: 'Deleted Place'});
};
exports.getPlaceById= getPlaceById;
exports.getPlacesByCreatorId = getPlacesByCreatorId;
exports.createPlace = createPlace;
exports.updatePlace = updatePlace;
exports.deletePlace = deletePlace;
Inside models folder I have two files: http-error.js which has this code:
class HttpError extends Error {
constructor(message, errorCode) {
super (message);
this.code = errorCode;
}
}
module.exports = HttpError;
The other file inside is the schema which is place.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const placeSchema = new Schema({
title: {
type: String
},
description: {
type: String
},
address: {
type: String
},
busrespect: {
type: String
},
creator: {
type: String
}
},
)
const BusStop = mongoose.model('BusStop', placeSchema)
module.exports= BusStop
Summary: somewhere in the try catch part from createPlace something is going wrong since my data entry is always going to the error status I indicated in that part.

Express Typescript API Validating Body Parameters in Router using Middleware

Im using Router classes to manage all my Routes:
const router = express.Router();
/**
* User Sign up Route at /api/auth/register
*/
router.post(
"/register",
checkBodyParameters(['username', 'email', 'password']),
verifyRegister.ensurePasswordStrength,
verifyRegister.checkUsernameAndEmail,
AuthController.register
);
export = router;
I want to check the x-www-form-urlencoded body parameters. To see if either the key is not what it should be, or the value is empty.
I wrote a middleware function to check that:
import { Request, Response } from "express";
export default function checkBodyParameters(
bodyParams: Array<string>,
req: Request,
res: Response,
next
) {
let requestBodyParams: Array<string> = [];
requestBodyParams.push(req.body.username, req.body.email, req.body.password);
requestBodyParams.forEach((requestBodyParam) => {
if (bodyParams.includes(requestBodyParam)) {
if (requestBodyParam !== "") {
next();
} else {
res.status(400).json({
message: "Paremeter cant be empty",
value: requestBodyParam,
});
}
} else {
res
.status(400)
.json({ message: "Paremeter not specified", value: requestBodyParam });
}
});
}
But it seems like it doesnt like me passing Arguments to the middleware function in
checkBodyParameters(['username', 'email', 'password'])
My Question is how do i create a middleware function which acceppts more values than req, res and next? And how to use this function correctly with the router instance.
Any Feedback is appreciated
You are calling the function instead of returning a function as a middleware.
Instead, use:
const checkBodyParameters = (
bodyParams: Array<string>
) => (
req: Request,
res: Response,
next
) => {
let requestBodyParams: Array<string> = [];
requestBodyParams.push(req.body.username, req.body.email, req.body.password);
requestBodyParams.forEach((requestBodyParam) => {
if (bodyParams.includes(requestBodyParam)) {
if (requestBodyParam !== "") {
next();
} else {
res.status(400).json({
message: "Paremeter cant be empty",
value: requestBodyParam,
});
}
} else {
res
.status(400)
.json({ message: "Paremeter not specified", value: requestBodyParam });
}
});
}
export default checkBodyParameters

PUT request 404 not found error Postman? Node.js Express

When trying to make a PUT request to update a house for my MERN application. The route does not seem to work on the server-side, in postman it shows a 404 not found error. I have speculation that it may be the way my routes are set up because it does not even recognize the that I am trying to make a PUT request. All of my other routes work but for some reason I am getting the index.html Error in postman. The PUT request is at the bottom of the HouseList Route code.
HouseList Route
// const express = require('express')
// const router = express.Router()
const router = require('express').Router();
const {House} = require('../../Models/House');
const Formidable = require('formidable');
const cloudinary = require('cloudinary').v2
const mongoose = require('mongoose');
require("dotenv").config()
// const { request, response } = require('express');
// const dotenv = require("dotenv");
// dotenv.config();
//mongoDB and Cloudinary
const mongoURI = process.env.Mongo_URI;
cloudinary.config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET
})
mongoose.connect(mongoURI, {useNewUrlParser:true, useUnifiedTopology:true}, (error)=>{
if(error) {
return console.log(error)
}
return console.log("database is connected")
})
router.post("/api/house-listing", async (request, response)=>{
const form = new Formidable.IncomingForm();
form.parse(request, (error, fields, files)=>{
const {
price,
city,
county,
numOfBeds,
numOfBaths,
numOfGarages,
isSaleOrRent,
us_state,
squarefeet
} = fields
const { house_image } = files;
console.log('Price: ', price)
console.log('City: ', city)
console.log('county: ', county)
console.log('state: ', us_state)
console.log('squarefeet', squarefeet)
console.log('numOfGarages: ', numOfGarages)
console.log('numOfBeds: ', numOfBeds)
console.log('numOfBaths: ', numOfBaths)
console.log('isSaleOrRent: ', isSaleOrRent)
console.log('houseImage', house_image.path)
cloudinary.uploader.upload( house_image.path,
{folder:"/houseAgency/houses"}, async(error, result)=>{
if(error) {
console.log(error)
}
const image_url = result.url;
const newHouse = new House({
house_location: {
county: county,
city: city,
us_state: us_state
},
house_details: {
price: price,
squarefeet: squarefeet,
numOfBeds: numOfBeds,
numOfBaths: numOfBaths,
numOfGarages: numOfGarages,
isSaleOrRent: isSaleOrRent,
house_image: image_url,
}
})
const savedHouse = await newHouse.save();
return response.status(200).json(savedHouse)
})
})
})
router.put('/api/house-details/:id', (req, res) => {
House.findByIdAndUpdate({_id: req.params.id}, req.body).exec().then((data)=>{
console.log(req.body)
if (data === null) {
return res.status(404).json({ errors: [{location: "house-details", msg: "Not found", param: req.params.id}]})
}
return res.status(200).json(data)
}).catch((error)=>{
return res.status(500).json({"error": error})
})
});
module.exports = router;
HouseFetch
// const express= require('express')
// const router = express.Router()
const router = require('express').Router();
const {House} = require('../../Models/House');
//HOUSE Fetch
router.get('/api/house-sale', async(req, res)=>{
try{
House.find({'house_details.isSaleOrRent': 'SALE'}).exec().then((data)=>{
// console.log(data);
return res.status(200).json(data)
})
} catch(error) {
return res.status(500).json({msg: 'server is currently Down :('})
}
})
router.get('/api/house-rent', async(req, res)=>{
try{
House.find({'house_details.isSaleOrRent': 'RENT'}).exec().then((data)=>{
return res.status(200).json(data)
})
} catch(error) {
return res.status(500).json({msg: 'server is currently Down :('})
}
})
router.get('/api/house-details/:id', async(req, res)=>{
await House.findOne({_id:req.params.id}).exec().then(data=>{
return res.status(200).json(data)
}).catch(error =>{
return res.status(400).json(error)
})
})
router.get("/api/house-search/:query", async (request, response) => {
const us_states = [
"Alabama", "Alaska", "American Samoa", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "District of Columbia", "Florida", "Georgia", "Guam", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Minor Outlying Islands", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Northern Mariana Islands", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Puerto Rico", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "U.S. Virgin Islands", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"
];
const query = request.params.query;
const result = [];
for (let counter = 0; counter < us_states.length; counter++) {
let currentStates = us_states[counter];
if (query.toLowerCase().includes(currentStates.toLowerCase())) {
result.push(currentStates);
}
}
House.find({ "house_location.us_state": result[0] })
.exec()
.then((data) => {
return response.status(200).json(data);
})
.catch((error) => {
console.log(error);
});
});
module.exports = router;
don't use async/await and .then .catch together, use async/await with try/catch, and you don't need to return when you use res so you should update all queries like this:
try {
let data = await House.findByIdAndUpdate({_id: req.params.id}, req.body).toObject()
res.status(200).json(data)
} catch (error) {
res.status(404).json({"error": error})
}
if you want result of find be a plain object javascript you can use lean() for find and use toObject for findOne or findById also you can try like this
let data = await House.findByIdAndUpdate({_id: req.params.id}, req.body)
res.status(200).json({data})
You are trying to execute the method put in a route but you have not declared it in your code.
In your question you do not mention what route you are trying to access. I suppose you would like to do a put in route /api/house-details/:id.
In order to achieve this, you should declare a new route with put method, after put route declaration you should write your route handler:
router.put('/api/house-details/:id', (req, res) => {
const options = { new: true };
House.findByIdAndUpdate(req.params.id, req.body, options).exec().then((data)=>{
console.log(req.body)
if (data.value === null) {
return res.status(404).json({ errors: [{location: "house-details", msg: "Not found", param: req.params.id}]})
}
return res.status(200).json(data)
}).catch((error)=>{
return res.status(500).json({"error": error})
})
});
To return the modified the modified collection item (after update), you must include options: { new: true}, as stated by documentation.

Express-Validator fail on unknown key

I am working with nodejs, express, and express-validator to create a type safe rest api.
What I want is that when the user passes in a request that contains one or many extra keys that the validation fails.
These are my validator rules:
const prepaidValidationRules = () => {
return [
body('accountHolder').exists().isString(),
body('amount').exists().isFloat()
]
}
Now when some sends the following request body:
{
"amount": 20.0,
"accountHolder": "Claude"
}
Then the validation succeedes as expected, but when I send the following request body:
{
"amount": 20.0,
"accountHolder": "Claude",
"extrakey": "someValue"
}
Then I want the validation to fail.
Is there anyway to do this using express-validaiton.
Thank you very much for any help :)
You can disallow unknow fields with express-validator, but you need to do an extra function.
The middleware function:*
import { validationResult } from 'express-validator'
export function validate (validators, allowExtraFields = false) {
return async (req, res, next) => {
// You can meka the validation optional
if (!allowExtraFields) {
// Fields validation
const extraFields = checkIfExtraFields(validators, req)
if (extraFields) { res.status(400).json({ error: true, message: 'Bad request' }) }
}
// Type validation
await Promise.all(validators.map((validator) => validator.run(req)))
const errors = validationResult(req)
if (errors.isEmpty()) {
return next()
}
logger.error(`${req.ip} try to make a invalid request`)
return res.status(400).json({ error: true, errors: errors.array() })
}
}
function checkIfExtraFields (validators, req) {
const allowedFields = validators.reduce((fields, rule) => {
return [...fields, ...rule.builder.fields]
}, []).sort()
// Check for all common request inputs
const requestInput = { ...req.query, ...req.params, ...req.body }
const requestFields = Object.keys(requestInput).sort()
if (JSON.stringify(allowedFields) === JSON.stringify(requestFields)) {
return false
}
logger.error(`${req.ip} try to make a invalid request`)
return true
}
Usage
import { body, query } from 'express-validator'
const validator = [
body('email').isEmail().exist(),
query('new').isString(),
]
router.post(
'/user',
validate(validator ), // By default all extrange fields are rejected
handler.post
)
This one works to me
const { body } = require("express-validator");
...
...
body().custom((body) => {
const allowedKeys = [
"name",
"age",
"bio",
];
for (const key of Object.keys(body)) {
if (!allowedKeys.includes(key)) {
throw new Error(`Unknown property: ${key}`);
}
}
return true;
}),
I will leave this here if someone wants to achieve the same.
I simply used currying to achieve what I wanted:
app.post("/someRoute", validationRule(), validate(2), doSomething());
validate function
function validate(nmbOfExpectedKeys) {
return (req, res, next) => {
if (Object.keys(req.requestBody) > nmbOfExpectedKeys)
return res.status(422);
}
}

Resources