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
Related
Im creating a video sharing app that allows users to sign in and update their profiles. i've created an error handler to identify errors in the backend and when im submitting my put request to update a user its going off.
here are my routes that im using
import express from 'express';
import { update} from '../controllers/user.js'
import { verifyToken } from '../verifyToken.js';
const router = express.Router();
router.put("/:id", verifyToken, update);
here is the controller for updating the user
import { createError } from "../error.js";
import User from '../models/User.js'
export const update = async (req, res, next) => {
if(req.params.id === req.user.id) {
try {
const updatedUser = await User.findByIdAndUpdate(req.params.id, {
$set: req.body
}, {new: true})
res.status(200).json(updatedUser)
} catch (error) {
next(error)
}
} else {
return next(createError(403, 'You can only update this account'));
}
}
here is the custom error handling im using which is in the index.js below and error.js
app.use((err, req, res, next) => {
const status = err.status || 500
const message = err.message || "something went wrong"
return res.status(status).json({
success: false,
status,
message
})
})
export const createError = (status, message) => {
const err = new Error()
err.status=status
err.message=message
return err
}
here is the middleware for the token
import jwt from 'jsonwebtoken'
import { createError } from './error.js'
export const verifyToken = (req, res, next) => {
const token = req.cookies.acess_token
if(!token) return next(createError(401, 'You are not authenticated'))
jwt.verify(token, process.env.JWT, (err, user)=>{
if(err) return next(createError(403, "Token invalid"))
req.user = user
next()
})
}
Custom error handler:
export const errorHandler: ErrorRequestHandler = (err, _, res) => {
if (err instanceof HttpError) {
res.status(err.statusCode).json({
message: err.message
});
return;
}
res.status(500).json({
message: err.message,
});
};
Handler where I throw the error:
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
The error handler middleware works as expected when everywhere except when it is thrown in catch block of the registerHandler. It's driving me crazy. Can somebody explain why this is so?
middlewares are pipes, in other words functions that runs after another function, so if you want to run an error handler you need to pass the next function to run
export const registerHandler: Handler = async (req, res, next) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
// to let express know that the next function to run is an errorhandler you need to pass a parameter to the function next
return next(new UnprocessableEntity());
}
try {
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
} catch (error) {
throw new BadRequest(error.message);
}
};
to create error handlers you need to create a function with 4 parameter
error: the error
req: request
res: response
next: next handler
function errorHandler (error, req, res, next) {
if (err instanceof HttpError) {
return res.status(err.statusCode).json({
message: err.message
});
}
return next(error);
}
for this to work you need to specify your error handlers after all your routes
const app = express()
app.use("/api", apiRoutes());
app.use("/more-routes", moreRoutes());
app.use(errorHandler);
app.use(anotherErrorHandler);
This might not be the exact solution you may be looking for, but it might help.
The express js documentation
says:-
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error.
So you don't need try and catch at all.
The above code can be written as:-
export const registerHandler: Handler = async (req, res) => {
const { username, password } = req.body as {
username: string | undefined;
password: string | undefined;
};
if (!username || !password) {
throw new UnprocessableEntity();
}
const user = await User.register(username, password);
req.logIn(user, (err) => {
console.log(err);
});
res.json({
user,
});
};
If any error occurs in await line, the errorHandler will automatically be called, and you don't have to explicitly throw an error.
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.
I have an express route that gets call with axios from the frontend. The thing is, not matter what I put into the route I always get the same error:
"Cast to ObjectId failed for value "getTodosMisProductos" at path "_id" for model "local""
I'm not doing any query to mongoose in that route but in any other route where I make a query everything works fine.
I've checked the middleware but there is not any query to mongoose
getTodosMisProductos
router.get("/getTodosMisProductos", auth, async (req, res) => {
/*
try {
const data = await Local.findOne({ user: req.user.id }).populate("products.producto");
console.log(data);
if (!data) {
return res
.status(404)
.json({ errors: [{ msg: "No se encontro el local" }] });
}
return res.status(200).json(data.products);
} catch (error) {
console.log(req.user.id);
console.error("error en llamado");
return res.status(500).send("Server Error");
}
*/
console.log("algo");
return res.status(200).json({ msg: "success" });
});
the code commented is the code I need to use, I changed it for testing purposes but even with that simple new code I get the same error.
auth middleware
const jwt = require("jsonwebtoken");
const config = require("config");
module.exports = function (req, res, next) {
// Get token from header
const token = req.header("x-auth-token");
// Check if not token
if (!token) {
return res
.status(401)
.json({ msg: "No tienes autorización para hacer esto" });
}
// Verify token
try {
const decoded = jwt.verify(token, require("../config/keys").jwtSecret);
req.user = decoded.user;
next();
} catch (error) {
res.status(401).json({ msg: "El token es inválido" });
}
};
action from where the route gets called
export const getAllProductos = () => async (dispatch) => {
try {
console.log("Esto se llama");
const res = await axios.get("/api/local/getTodosMisProductos/");
dispatch({
type: SET_PRODUCTS,
payload: res.data,
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
}
};
The response status is always 500 (Internal Server Error)
EDIT
//#route GET api/local/:id
//#desc obtener local por id
//#access private
router.get("/:id", auth, async (req, res) => {
try {
const local = await Local.findById(req.params.id);
if (!local) {
return res
.status(404)
.json({ errors: [{ msg: "No se encontro el local" }] });
}
return res.status(200).json(local);
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
});
You have another route that also match /api/local/getTodosMisProductos/
Apparently it got matched with /api/local/:id,
where you get req.params.id = "getTodosMisProductos" and got passed down to await Local.findById(req.params.id)
And mongoose can't convert "getTodosMisProductos" to ObjectId, hence the error.
The order in which you declare the route affects the matching priority.
The order is first comes first serves, so make sure you declare /api/local/addProducto or any other routes that starts with /api/local/ before declaring /api/local/:id
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