First, I have userController like this:
File userController.js
const { validationResults } = require('express-validator');
const { userSignUpValidation } = require('../validator/userSignUpValidate');
function userCreation(req, res) {
try{
const errors = validationResults(req);
if(errors) {
return res.status(400)
.json({
error: {
message: errors.array()[0].msg
}
})
}
bcrypt.hash(req.body.userPassword, saltRounds, function(err, hash) {
User.create({
userId: req.body.userId,
userEmail: req.body.userEmail,
userPhoneNumber: req.body.userPhoneNumber,
userPassword: hash
})
.then((user) => {
return res.status(200).json(user);
})
.catch((error) => {
return res.status(400).json.error;
});
});
} catch(error) {
return res.status(400)
.json({
error: {
message: error
}
})
}
}
In the validator/userSignUpValidate.js the code like this:
'use strict'
const { check } = require('express-validator');
module.exports = [
check('userId').isLength({ min: 5 }).withMessage('The username at least more than 5 characters!'),
check('userPassword').isLength({ min: 6 }).withMessage('The password at least more than 6 characters!'),
check('userPhoneNumber').isLength({ min: 10 }).withMessage('The phone number at least more than 10 characters!'),
check('userEmail').isEmail().withMessage('Is your email correct? Please be correct!')
]
When I test it on postman, the json response always show error by catch on userController.js without error message.
{
"error": {
"message": {}
}
}
My question. To ensure the express-validator run in the right place, where should I put the code?
Firstly you need to import validationResult from express-validator, in your code you are importing validationResults.
Secondly, you are not using your userSignUpValidate middleware. This middleware can be used in controller but it is better to appy it in the userRoute to keep controller clean.
So let's apply userSignUpValidate middleware to the userRoutes.
If the file paths don't match yours, please fix them.
const express = require("express");
const router = express.Router();
const usersController = require("../controllers/userController");
const userSignUpValidate = require("../validator/userSignUpValidate");
router.post("/register", [userSignUpValidate], usersController.userCreation);
module.exports = router;
Then in the controller, we need to import validationResult from express-validator and use it:
const { validationResult } = require("express-validator");
function userCreation(req, res) {
try {
const errors = validationResult(req);
if(!errors.isEmpty()) {
console.log(errors);
return res.status(400).json({
error: {
message: errors.array()[0].msg
}
});
}
//other code you have
} catch (error) {
return res.status(400).json({
error: {
message: error
}
});
}
}
exports.userCreation = userCreation;
When we send a request body with a 3 character password like this:
{
"userId": "userid",
"userPassword": "123",
"userPhoneNumber": "1234567890",
"userEmail": "abc#gmail.com"
}
The response will be like this with a 400 - Bad Request:
{
"error": {
"message": "The password at least more than 6 characters!"
}
}
you should pass like this
const errors = validationResults(req.body);
const Joi = require('#hapi/joi');
const loginValidation = data =>{ const schema = {
username: Joi.string().min(5).required(),
password: Joi.string().min(5).required(),
deviceId: Joi.string().min(2).required()
};
return Joi.validate(data, schema); };
module.exports.loginValidation = loginValidation;
My Routes File
const { registerValidation, loginValidation } = require('../../validation'); router.post('/login', async (req, res)=>{
try {
//validate the data to be submitted
const { error } = loginValidation(req.body);
if (error) return res.status(200).send(
{
status: 0,
message: "Validarion Error",
details: error.details[0].message
});
} catch (error) {
res.status(400).send({
status: 0,
message: "Failed",
error: error
});
}});
this code works fine and is in production mode
Just try like below,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
//this function you need to export in controller
const xxx = (req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
app.get('/', oneOf([
check('lang').isIn(['js', 'react', 'angular'])
]), xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
Sample working copy
UPDATE 2
I hope below one will help full,
const express = require('express');
const { check, oneOf, validationResult } = require('express-validator');
const app = express();
const xxx = [
check('lang').isIn(['js', 'react', 'angular']),
(req, res, next) => {
try {
validationResult(req).throw();
res.status(200).send('success');
} catch (err) {
console.log('error messagecame')
res.status(422).json(err);
}
}
]
app.get('/', xxx);
app.listen(4000, ()=>{
console.log("running server 4k")
})
SAMPLE WORKING COPY2
Error
https://TruthfulWeirdIrc--five-nine.repl.co?lang=jssddf
Success
https://TruthfulWeirdIrc--five-nine.repl.co?lang=js
Related
I am using express-validator in nodejs. The code is always throwing first error message as a validation result.
validation.js
const { check, validationResult } = require('express-validator')
const {handleError, ErrorHandler} = require('../helper/error');
const resultsOfValidation = (req,res,next) => {
const messages = [];
const errors = validationResult(req);
if(errors.isEmpty()) {
return next(); //pass to controller
}
errors.array().map( err => messages.push(err.msg));
throw new ErrorHandler(400,messages);
}
const createUserValidator = () => {
return [
check('firstName')
.exists({ checkFalsy: true }).withMessage('First Name is mandatory')
.bail()
.isAlpha().withMessage('First Name should have all alphabets')
.bail()
.isLength({min:3}).withMessage('First Name should have minimum 3 characters')
,
check('lastName')
.optional({ checkFalsy: true }) //ignore validation when null or empty
.isAlpha()
.bail()
]
}
module.exports = {
resultsOfValidation,
createUserValidator,
}
user route is below :
const router = express.Router();
router
.get('/',getAllUsers)
.post('/',createUserValidator(),resultsOfValidation,(req,res) => console.log('created'))
.get('/:id',getUserById)
.delete('/',deleteUserById)
module.exports = router;
My user.js controller file is:
module.exports = {
getAllUsers: async (req,res)=> {},
createUser: async (req,res,next)=> {
try{
const errors = resultsOfValidation(req);
if (errors.length >0) {
throw new ErrorHandler(400, errors)
}
const { firstName, lastName } = req.body;
// call createUser from model
const user = await UserModel.createUser(firstName, lastName);
if(!user) {
throw new ErrorHandler(404, 'User not created')
}
// return res.status(200).json({ success: true, user });
} catch (error) {
return res.status(500).json({ success: false, error: error })
}
},
getUserById: async (req,res)=> {},
deleteUserById: async (req,res)=> {},
}
Here is example of post request I made. It should throw First Name should have minimum 3 characters. But I am getting other error First Name is mandatory
I am trying to handle error with class constructor in a better way. I named my class HttpError that is written below, the problem is HttpError doesn't work properly.
This is "app.js" file:
const express = require('express');
const app = express();
const placesRoutes = require('./routes/placesRoutes');
app.use('/api/places',placesRoutes);
app.get('/', (req, res, next) => {
res.json({message: 'the server is working.'});
next();
});
app.use((error, req, res, next)=> {
if(res.headerSent){
return next(error);
}
res.status(error.code || 500);// This error shows when I am entering wrong pid
res.json({message: 'Does not match any route'});
})
app.listen(5000);
and this is placesRoutes.js:
const express = require('express');
const HttpError = require('../models/httpError');
const router = express.Router();
const DUMMY_PLACE = [
{
id: '1',
name: 'jahid',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gahid#gmail.com',
password: 'jahid5868'
},
{
id: '2',
name: 'saiful',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gahid#gmail.com',
password: 'jahid5868'
},
{
id: '3',
name: 'sadiqul',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'gkaid#gmail.com',
password: 'jahid5868'
},
{
id: '4',
name: 'hasib',
address: 'Rajoir, Rayenda, Sarankhola, Bagerhat',
email: 'ahid#gmail.com',
password: 'jahid5868'
},
]
router.get('/:pid', (req, res, next) => {
const sid = req.params.pid;
const data = DUMMY_PLACE.find(p=> p.id === sid);
if(!data){
return next(new HttpError('Does not found uid', 404));
}
res.json({data});
});
module.exports = router;
and this is "HttpError.js":
class HttpError extends Error {
constructor(message, statusCode) {
super();
this.message = message;
this.statusCode = statusCode;
}
};
module.exports = HttpError;
When I am entering /api/places/:(wrong-pid). It doesn't show HttpError rather it's showing the error 500 from bottom of app.js.
I am new to express so please don't bother if my question doesn't make sense.
With the above code an instance of HttpError gets passed to the error handling middleware, but you do not really use it there. You probably wanted to do something like:
app.use((error, req, res, next)=> {
if(res.headerSent){
return next(error);
}
if(error instanceof HttpError) {
res.status(error.statusCode);
res.json({message: error.message});
} else {
res.status(error.code || 500);
res.json({message: 'Does not match any route'});
}
})
So basically you have many ways of handling Errors, I like to use try/catch blocks very simple to understand and very useful and clear. You're doing a block asking him to do something if it's getting an error it's passing it to the catch block.
For example:
const validateUser = async (req, res, next) => {
try {
const { email, password } = req.body
if (!email || !password) {
throw new ErrorHandler(404, 'Missing required email and password fields')
}
const user = await db.User.findOne({ where: { email }});
if (!user) {
throw new ErrorHandler(404, 'User with the specified email does not exists')
}
next()
} catch (error) {
next(error)
}
}
so we try something (getting data checking it) if there is an error its passing to the catch block.
another simplest example:
try {
capture = await payPalClient.client().execute(request);
captureID = capture.result;
// console.log(captureID)
} catch (err) {
// 4. Handle any errors from the call
return res.status(500).send({
status: 500,
error: err,
});
}
trying something (some paypal stuff from one of my code not important).
if I'm getting an error from it I return a response (500 with a message of error .)
you can check on google for error handling express try/catch
I'm trying to validate a date. I have tried everything I can but I have not found a solution. Input {"dob": "2002-10-02"}
'use strict'
var validator = require('validator');
var controller = {
create: (req,res) =>{
//pick parameters
var parameters = req.body;
//validator
try {
//not working (always returns false)
//var validate_dob = validator.isDate(parameters.dob + '');
//also not working (always returns false)
//var validate_dob = validator.isISO8601(parameters.dob + '');
} catch (error) {
return res.status(400).send({
message: error
});
}
}
}
In your question you mention tag express-validator, but in your middleware you use pure validator.
Here I am putting an example using the express-validator lib (version 6.6.0). To use validate body parameters (login and password). But you can get the idea and pick the validation for your date from the validators list. Reference.
server/validators/login.validator.js
const { body, validationResult } = require('express-validator');
exports.validationBodyRules = [
body('login', 'login is required').exists(),
body('password', 'password is required').exists(),
body('login', 'login is required').notEmpty(),
body('password', 'password is required').notEmpty()
];
exports.checkRules = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
Here is the routes file
server/routes/login.route.js
const router = require('express').Router();
const loginService = require('../controllers/login.controller');
const loginValidator = require('../validators/login.validator');
router.post('/login', loginValidator.validationBodyRules, loginValidator.checkRules, loginService.hashPassword, loginService.lookupLogin, loginService.logEmployee);
module.exports = router;
server/controllers/login.controller.js
const postgres = require('../../lib/postgres');
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
exports.logEmployee = (req, res) => {
res.status(200).json({ token: 'Bearer ' + jwt.sign(req.employee, process.env.SECRET, { expiresIn: 1800 }) });//expires in 1800 seconds
res.end();
};
exports.hashPassword = (req, res, next) => {
crypto.scrypt(req.body.password.toString(), 'salt', 256, (err, derivedKey) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not do login', param: req.params.id }] });
}
req.body.kdfResult = derivedKey.toString('hex');
next();
});
};
exports.lookupLogin = (req, res, next) => {
const sql = 'SELECT e.employee_id, e.login FROM employee e WHERE e.login=$1 AND e.password = $2';
postgres.query(sql, [req.body.login, req.body.kdfResult], (err, result) => {
if (err) {
return res.status(500).json({ errors: [{ location: req.path, msg: 'Could not do login', param: req.params.id }] });
}
if (result.rows.length === 0) {
return res.status(404).json({ errors: [{ location: req.path, msg: 'User or password does not match', param: req.params.id }] });
}
req.employee = result.rows[0];
next();
});
};
But you can use the ideas here to use a date validator.
If you need a more complete example, please, let me know.
the question is pretty self explanatory. I am registering/signing up users in a mongoDB database. They are being registered fine and an accesstoken [jwt based] is also being generated.
Now, when I go to query the database to fetch the list of users I am getting that error -
jwt is not defined.
It is worthwhile to mention that users also in my backend can have two type of roles - basic and admin. And only an admin user can fetch list of all users by sending accessToken in the header as Bearer authorization parameter.
I have 2 main files in my backend project structure that uses jwt.access methods like jwt.verify or jwt.signIn; these are the server.js and userController.js [a separate file where I have written all individual db related methods].
As far as I am concerned, all necessary packages are there in my project - express, node, jwa, jws, jsonwebtoken, mongo, mongoose, bcrypt, cors etc. So what is the trouble?
My route.js -->
const User = require('../models/user.model');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const { roles } = require('../models/roles');
const JWT_SECRET = "$#GR24T4344$#$##%ETWWTEME%$6";
async function hashPassword(password) {
return await bcrypt.hash(password, 10);
}
async function validatePassword(plainPassword, hashedPassword) {
return await bcrypt.compare(plainPassword, hashedPassword);
}
exports.grantAccess = function (action, resource) {
return async (req, res, next) => {
try {
const permission = roles.can(req.user.role)[action](resource);
if (!permission.granted) {
return res.status(401).json({
error: "You don't have enough permission to perform this action"
});
}
next();
} catch (error) {
next(error);
}
}
}
exports.allowIfLoggedin = async (req, res, next) => {
try {
const user = res.locals.loggedInUser;
if (!user)
return res.status(401).json({
error: "You need to be logged in to access this route"
});
req.user = user;
next();
} catch (error) {
next(error);
}
}
exports.signup = async (req, res, next) => {
try {
const { role, email, password } = req.body;
const hashedPassword = await hashPassword(password);
const newUser = new User({ email, password: hashedPassword, role: role || "basic" });
const accessToken = jwt.sign({ userId: newUser._id }, JWT_SECRET, {
expiresIn: "1d"
});
newUser.accessToken = accessToken;
await newUser.save();
res.json({
data: newUser,
message: "You have signed up successfully"
});
} catch (error) {
next(error);
}
}
exports.login = async (req, res, next) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user)
return next(new Error('Email does not exist'));
const validPassword = await validatePassword(password, user.password);
if (!validPassword)
return next(new Error('Password is not correct'));
const accessToken = jwt.sign({ userId: user._id }, JWT_SECRET, {
expiresIn: "1d"
});
await User.findByIdAndUpdate(user._id, { accessToken });
res.status(200).json({
data: { email: user.email, role: user.role },
accessToken
});
} catch (error) {
next(error);
}
}
exports.getUsers = async (req, res, next) => {
const users = await User.find({});
res.status(200).json({
data: users
});
}
exports.getUser = async (req, res, next) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId);
if (!user)
return next(new Error('User does not exist'));
res.status(200).json({
data: user
});
} catch (error) {
next(error);
}
}
exports.updateUser = async (req, res, next) => {
try {
const { role } = req.body;
const userId = req.params.userId;
await User.findByIdAndUpdate(userId, { role });
const user = await User.findById(userId);
res.status(200).json({
data: user
});
} catch (error) {
next(error);
}
}
exports.deleteUser = async (req, res, next) => {
try {
const userId = req.params.userId;
await User.findByIdAndDelete(userId);
res.status(200).json({
data: null,
message: 'User has been deleted'
});
} catch (error) {
next(error);
}
}
My server.js -->
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const PORT = 4000;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const playerRoutes = express.Router();
const userRoutes = express.Router();
const userController = require('./controllers/userController');
const user_routes = require('./apiroutes/route');
const app = express();
const JWT_SECRET = "$#GR24T4344$#$##%ETWWTEME%$6";
const users = "users";
require("dotenv").config({path: __dirname+ '../.env'});
let Player = require('./models/player.model');
let User = require('./models/user.model');
app.use(cors());
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: false
})
);
mongoose.connect('mongodb://127.0.0.1:27017/playerDB', function (err, db) {
if (err)
throw err;
db.createCollection(users, function (err, resp) {
if (err)
throw err;
console.log("Collection created!");
});
}, { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', function () {
console.log("MongoDB database connection established successfully");
});
..... blablablaaaa
app.use('/playerDB', playerRoutes);
app.use(async (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
if (req.headers["x-access-token"]) {
try {
const accessToken = req.headers["x-access-token"];
const { userId, exp } = await jwt.verify(accessToken, JWT_SECRET);
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: "JWT token has expired, please login to obtain a new one"
});
}
res.locals.loggedInUser = await User.findById(userId);
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use('/users', user_routes);
app.listen(PORT, function () {
console.log("Server is running on Port: " + PORT);
});
I hope you understand my approach and scenario? Can you guess, where it could have gone wrong? Any idea?
Missing npm packages or something more critical?
Look forward to some hints on this problem! Can't seem to figure out a way!
it seems you forgot to add this line to server.js
const jwt = require('jsonwebtoken');
While register and login, this didn't caused a problem, because for these requests, req.headers["x-access-token"] was null, and the code didn't reach the if block where you used jwt, but one a request with this header came (like getUsers) the code tried to use jwt.verify, but since jwt wasn't imported it gave error.
I am trying to make a post request to the server (mongodb) but I get this error:
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'todo_description' of undefined
I am running mongodb on my localhost
// Require Express
const express = require("express");
// Setting Express Routes
const router = express.Router();
// Set Up Models
const Todo = require("../models/todo");
// Get All Todos
router.get("/", async (req, res) => {
try {
const todo = await Todo.find();
res.json(todo);
} catch (err) {
res.json({ message: err });
}
});
router.get("/:id", async (req, res) => {
try {
const id = req.params.id;
await Todo.findById(id, (err, todo) => {
res.json(todo);
});
} catch (err) {
res.json({ message: err });
}
});
router.post("/add", async (req, res) => {
const todo = new Todo({
todo_description: req.body.todo_description,
todo_responsible: req.body.todo_responsible,
todo_priority: req.body.todo_priority,
todo_completed: req.body.todo_completed,
});
try {
await todo.save();
res.json(todo);
} catch (err) {
res.json({ message: err });
}
});
router.patch("/update/:id", async (req, res) => {
try {
const updateTodo = await Todo.updateOne(
{ _id: req.params.id },
{ $set: { todo_description: req.body.todo_description } }
);
updateTodo.save().then(updateTodo => {
res.json(updateTodo);
});
} catch (err) {
res.json({ message: err });
}
});
router.delete("/delete/:id", async (req, res) => {
try {
const deleteTodo = await Todo.deleteOne({ _id: req.params.id });
res.json(deleteTodo);
} catch (err) {
res.json({ message: err });
}
});
module.exports = router;
my todo model
// Require Mongoose
const mongoose = require("mongoose");
// Define Schema
// const Schema = new mongoose.Schema;
// Define Todo-Schema
const TodoSchema = new mongoose.Schema({
// Creating Fields
todo_description: {
type: String
},
todo_responsible: {
type: String
},
todo_priority: {
type: String
},
todo_completed: {
type: Boolean
},
todo_date: {
type: Date,
default: Date.now
}
});
// Compile Model From Schema
// const TodoModel = mongoose.model("Todos", TodoSchema);
// Export Model
module.exports = mongoose.model("todos", TodoSchema);
error message:
(node:548) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'todo_description' of undefined
at router.post (C:\Users\kinG\Desktop\projects\mountain-of-prototype\mern\backend\routes\todo.js:33:32)
at Layer.handle [as handle_request] (C:\Users\kinG\Desktop\projects\mountain-of-prototype\mern\backend\node_modules\express\lib\router\layer.js:95:5)
thank you
You are accessing todo_description from req.body. req.body will only be available if you add the body-parser middleware or add a similar one yourself.
Add this right before your routes are loaded :
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
app.use(bodyParser.json());
You can also add this to a specific route. Read more about it here.
You should use body-parser in your master file of the application. Which gives you the parsed json before your middle-ware parse the body, which by-default in string. And also make sure you are sending todo_description in the req.body(should check before use).
const bodyParser = require('body-parser');
app.use(bodyParser.json());