Node.js error handling setup not working as intended - javascript

I am trying to have all my error messages in one file, each error is denoted by an error code, then in my functions/services, when there is an error, I call a function that takes the error code as an argument, then returns an object to the client with the error code and the respective error message from the errors.js file.
as an example, a user trying to register with an email that already exists in the database, here is how I try to do it:
// userService.js -- where my register function is
const { errorThrower } = require('../../utils/errorHandlers');
...
static async registerNewUser(body) {
const exists = await User.where({ email: body.email }).fetch();
if(exists) {
errorThrower('400_2');
}
...
}
errorHandlers.js file:
exports.errorThrower = (errCode) => {
throw Object.assign(new Error(errors[errorCode]), { errorCode })
}
exports.errorHandler = (err, req, res, next) => {
if(!err.status && err.errorCode) {
err.status = parseInt(err.errorCode.toString().substring(0, 3), 10);
}
let status, message
if (err.status) {
status = err.status
message = err.message
} else {
status = 500;
message = 'unexpected behavior, Kindly contact our support team!'
}
res.status(status).json({
errorCode: err.errorCode,
message
})
}
errors.js
module.exports = {
'400_1': 'JSON payload is not valid',
'400_2': 'user already registered',
...
}
...
const user = require('./routes/user');
const { errorHandler } = require('../utils/errors');
...
app.use('/user' , user);
app.use(errorHandler);
...
now with this setup, when hitting the register endpoint by postman, I only get the following in the console
UnhandledPromiseRejectionWarning: Error: user already registered
could someone please tell me what am I missing here?
thanks in advance!

You're not catching the error which you throw inside your errorThrower, thus getting the error UnhandledPromiseRejectionWarning. What you need to do is catch the error and pass it on the the next middleware, in order for the errorHandler-middleware to be able to actually handle the error. Something like this:
exports.register = async(req, res) => {
try {
await registerNewUser(req.body);
} catch(err) {
next(err);
}
};
If you don't want to do this for every middleware, you could create a "base"-middleware which handles this:
const middlewareExecutor = async (req, res, next, fn) => {
try {
return await fn.call(fn, req, res, next);
} catch (err) {
next(err);
}
};
Now you can pass your middlewares as an argument and delegate handling the error to the executor:
app.use('/user' , async (req, res, next) => middlewareExecutor(req, res, next, user));

Related

nodejs api request function error handling crashes app

I'm trying to make an api call and pass it into a function that stores it into mongodb. The problem is that the entire site crashes when I try to handle errors instead of redirecting to the error page. All my other functions work, it's just the request function containing the api call that doesn't.
Instead of redirecting, I only get the error messages in the terminal.
Here's my code for it:
index.js
router.post('/',async function(req,res){
var {title,date,loc,count, save,del,update,csv} = req.body;
var {username} = req.session;
console.log(req.body);
var url = `http://api.openweathermap.org/data/2.5/weather?q=${loc}&appid=${API_KEY}&units=metric`
if(save){
//weather api
request({ url: url, json: true }, async function (error, response) {
if(error){
throw new Error ('type error');
}
else{
console.log('Current weather: '
+ response.body.main.temp
+ ' degrees celsius'
);
let w = JSON.stringify(response.body.main.temp);
console.log(w)
await db.addItem(username,title, loc, count, w, date);
}
});
res.redirect('/');
}
app.js
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
Here is what my terminal looks like when the app crashes:
terminal error message
You have not describe what to do if an error occurs in the else statement of the request call. So if an error occurs it will crash.
Since you are using async...await in the callback to insert into the db, you can get the functionality you want by wrapping it in a try...catch block and moving the redirect into the catch:
request({ url: url, json: true }, async function (error, response) {
if(error){
throw new Error ('type error');
} else {
try {
console.log('Current weather: '
+ response.body.main.temp
+ ' degrees celsius'
);
let w = JSON.stringify(response.body.main.temp);
console.log(w)
await db.addItem(username,title, loc, count, w, date);
} catch (error) {
console.log(error);
res.redirect('/');
}
}
});
However, the throw new Error('type error') will still cause the app to crash here. If you want to also redirect in this case, then you can add a res.redirect('/') there as well instead of throwing, or wrap the entire request call in a try...catch so that the catch block will also catch that thrown type error:
try {
request({ url: url, json: true }, async function (error, response) {
if(error){
throw new Error ('type error');
}
else{
console.log('Current weather: '
+ response.body.main.temp
+ ' degrees celsius'
);
let w = JSON.stringify(response.body.main.temp);
console.log(w)
await db.addItem(username,title, loc, count, w, date);
}
});
} catch (error) {
res.redirect('/')
}

Keep getting " No signatures found matching the expected signature for payload." for Stripe webhook (node.js)

app.use(
express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
},
}),
);
app.post('/webhook', async (req, res, next) => {
const sig = req.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(req.rawBody, sig, endpoint_secret);
} catch (err) {
return console.log(err)
}
if (event.type === 'invoice.payment_succeeded') {
//...
}
res.send();
});
I tried following this link but I kept getting express.raw is not a function error, I also tried this:
app.use((req, res, next) => {
if (req.originalUrl === '/webhook') {
next();
} else {
express.json()(req, res, next);
}
});
And still got the same error, would really appreciate it if I could get some help.
I'm using Firebase Cloud Functions as my webhook API, as so:
import webhook_app_creator from "express";
import cors from "cors";
// This example uses Express to receive webhooks
//the Cloud Function calls the webhook_app
export const webhook_app = webhook_app_creator();
// The Firebase Admin SDK to access Cloud Firestore.
//const cors = require("cors");
// Automatically allow cross-origin requests
webhook_app.use(cors({ origin: true }));
// build multiple CRUD interfaces:
webhook_app.post("/direct", async (request, response) => {
//send the response early - the only valid response is "received"
await commonHandler(request, response, endpointDirectSecret);
response.json({ received: true });
});
webhook_app.post("/connect", async (request, response) => {
//send the response early - the only valid response is "received"
await commonHandler(request, response, endpointSecret);
response.json({ received: true });
});
const commonHandler = async (request, response, secret) => {
const sig = request.headers["stripe-signature"];
try {
request.fullEvent = stripe.webhooks.constructEvent(
request.rawBody,
sig,
secret
);
} catch (err) {
logger(`Webhook Error: ${err.message}`);
return;
}
return queue_event(request.fullEvent);
};
The Cloud Function couldn't be simpler:
//import functions from "firebase-functions";
import { functions, webhook_app } from "../../../../services";
// Expose Express API as a single Cloud Function:
export default functions.https.onRequest(webhook_app);

Why is this user id not coming out of req.user;

This is my code for getting user info
router.post("/getuser", fetchuser, async (req, res) => {
try {
userId = req.user.id;
const user = await User.findById(userId).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal Server Error");
}
});
and this is the code for middleware fetchuser
const fetchuser = async (req, res, next) => {
const token = req.header('auth-token');
if (!token) {
res.status(401).send({ error: "Please authenticate using a valid token" })
}
try {
const data = jwt.verify(token, process.env.SECRET);
console.log(data);
req.user = data.user;
next();
} catch (error) {
res.status(401).send({ error: "Please authenticate using a valid token" })
}
};
I am getting the user id in console.log but when I try to get the user id in the router.post then I am not able to get the user Info.
Here is the result I am getting.
Server is running on port 5000
Connected to database
{ id: '61e98c45a9d8818292b38505', iat: 1642743501 }
Cannot read properties of undefined (reading 'id')
Please can anyone tell me what is wrong with this?
router.post("/getuser", fetchuser, async (req, res) => {
try {
const params = JSON.parse(req)
userId = params.id;
const user = await User.findById(userId).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal Server Error");
}
});
Your code is working fine , you just have to change this line on fetchuser middleware
req.user = data.user
to
req.user = data
and your code work as expected..
add file: src/types/express/index.d.ts
content:
declare namespace Express {
interface Request {
user: {
id: string;
};
}
}

Troubles with nodejs GET endpoint

I have the following endpoint:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;
try {
const user = await User.findById(_id);
if(!user) {
res.status(404).send();
}
res.send(user);
} catch (e) {
res.status(500).send(e);
}});
When I make the request with a valid user ID, the server sends back the user, no problem with that.
The problem is when I try to find a user with a ID which doesnt exist in the database. The server should response with a 404 Error but instead it sends back a Error 500 and I dont understand why!
Could anyone help me please?
Thank you in advance!
One nice way to handle the errors is to create an express error middleware, this allows you to put all of your error handling in one place so that you dont have to write it more than once.
With express when you use async routes handlers if a promise rejects the error will automatically be passed to the next error middleware.
// First register all of your routes
app.get('/user/:id', async (req, res) => {
const user = await User.findById(req.params.id);
if(!user) return res.status(404).send();
res.send(user);
})
// Then register you error middleware
app.use((err, req, res, next) => {
console.error(err.message)
// if mongoose validation error respond with 400
if(err.message.toLowerCase().includes('validation failed'))
return res.sendStatus(400)
// if moongoose failed because of duplicate key
if(err.message.toLowerCase().includes('duplicate key'))
return res.sendStatus(409)
// if mongoose failed to cast object id
if(err.message.toLowerCase().includes('objectid failed'))
return res.sendStatus(404)
res.sendStatus(500)
})
Thank you for your answers.
I have solved it adding the following to the user model schema:
_id: {type: String}
And adding a return before sending the 404 error:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;
try {
const user = await User.findById(_id);
if (!user) {
return res.status(404).send();
}
res.send(user);
} catch (error) {
res.status(400).send(error);
}});

Handling error sent back by express in postman

I am working with postman where i am sending my post data to api to insert data in mongodb.My issue is that i am not able to handle the error message properly. Here is my code for it
exports.addUser = (req, res, next) => {
const db = getdb();
// console.log(db)
// console.log(db)
db.collection12("user").insertOne({
name: req.body.name,
password:req.body.password
}).then((result) => {
res.send(result)
}).catch((err) => {
res.status(404).send('Error in adding')
});
};
so i knowingly made and error and wrote "collection12" so that i get and error but then in my catch method i am returning
("Error in addding")
so why then in postman i am not able to see this instead of that i am seeing a hude error meassge that says
See error here
I guess than same would be the issue in my react side where instead of getting my own error meassge i would get this huge message
You actually don't enter the catch block because it cannot even execute the db call. If you want a 404 error to be dispatched in this situation you need to add a try/catch statement like this:
exports.addUser = (req, res, next) => {
const db = getdb();
// console.log(db)
// console.log(db)
try {
db.collection12("user").insertOne({
name: req.body.name,
password:req.body.password
}).then((result) => {
res.send(result)
}).catch((err) => {
res.status(404).send('Error in adding')
});
} catch {
res.status(404).send('Error in adding')
}
};

Categories