Express router, Router.post executing before router.use middleware - node.js

My problem is that for some reason express router doesn't wait for next() function inside middleware and goes str8 to execution fo router.post.
Router.use('/posts/add', addPosts);
Router.post('/posts/add', (req, res) => {
if(req.success){
res.status(200).send('post added');
}else{
res.status(400).send({error: true, message: 'sth went wrong'})
}
});
Below is the middleware code:
module.exports = (req, res, next) => {
try {
if (req.authorization && req.authorization.access_level < 3) {
let post = new postsModel(req.body);
post.save().then(post => {
console.log(post);
req.success = true;
next();
});
} else {
throw new Error('unauthorized access');
}
} catch (err) {
res.status(400).send({ error: true, message: err.message });
}
};
Despite middlewares execution, router for some reason is always executing the router.post, doesn't wait for the next() function therefore awlays return error. Anybody could help with that?

Maybe try with the following approach:
Router.route('/posts/add', addPosts)
.post((req, res) => {
Refer to this documentation for further info.

I wanted to deeply apologize as I haven't posted the code for 1 more middleware invoked before anything else.
try {
const path = req.path;
switch (path) {
case '/add':
let data = req.body;
let error = addPostSchema.validate(data).error;
console.log(error);
if (error) {
throw new Error(error.message);
} else {
console.log('addPost validated');
next()
}
}
next();
} catch (err) {
console.log('walalala');
res.status(400).send({ error: true, message: err.message });
}
};
As you can see above, I've had double next() invoked. What I didn't know is that invoking next() does not stop rest of the code in middleware from executing, therefore it was prematurly invoking Router.post. Apologies for the confusion. Hope it will help somebody though having similiar problem.

Related

How to catch all errors in route as a middleware in ExpressJS?

I have a problem and I haven't found a solution yet. I want to catch all errors if occur any error in each route but it's very inconvenient when I have to do it many times.
How can i implement it as a middleware same like app.use(ErrorHandle); ?
Code in ErrorHandler.js:
export const ErrorHandler = func => async (req, res, next) => {
try {
await func(req, res, next);
} catch (error) {
next(error);
}
}
Code in index.js
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
Code in route need to catch error:
import { ErrorHandler } from './ErrorHandler';
export const uploadMedia = ErrorHandler(async (req, res) => {
// do something...
let error = new Error();
error.statusCode = 404;
error.message = 'Content not found!';
}
Sorry if misunderstood your question... When you do the code below which you provided, you are assuming that if an error reaches the end of the stack via next(err), such handler should be called. Hence it's the last declaration after all your routes.
app.use((err, req, res, next) => {
if (err) {
return res.status(err.statusCode || 500).json(err.message);
}
next()
});
That, however, won't catch unhandledExceptionErrors. You still need the good old
try {
// throw error somewhere
} catch (e) {
next(e);
}
Personally, I haven't tried this package but it seems so be a nice hack for Express router's inability to handle promise returns. About express-promise-router:
A simple wrapper for Express 4's Router that allows middleware to return promises. This package makes it simpler to write route handlers for Express when dealing with promises by reducing duplicate code.

How to properly log erros in my application using ExpressJS

I am part of a project which uses nodeJS + ExpressJS for the backend application, and We have a middleware function to log accesses on routes in the database.
When an User tries to access the /user route with a post method, a middleware receives the Request, get information like the URL, ip address, origin, a description of the event and record it in the database.
Everything works just fine, but some of my teammates were discussing about how to log the erros also in the database.
I will put bellow a code example
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
return response.status.json({ error: 'E-mail already in use' });
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
response.status(500).json({ error });
}
};
When we were discussing about how to implement it, we realized we'd have to call a log error function in a lot of places since we have many flows which leads to an error response.
So the code would be just like:
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
function() // here we would log the error
return response.status.json({ error: 'E-mail already in use' });
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
function() // here we would log the error
response.status(500).json({ error });
}
};
is it a properly way of dealing with error logging or is there any better way of doing it? Thank you for reading!
You can use the built-in error handler provided by Express.JS for this kind of logic, of course it requires a bit of setup. Like most things in Express.JS, the error handler it's just a middleware function with four parameters err, req, res and next, which MUST be placed after all your other middlewares. It comes to play when, inside a router handle (for example), your call next(err) (where err it's an Error) or by simply throwing err. Check out the documentation for more.
app.use(...)
app.use(...)
app.use((req, res, next) => {
if (req.params.id === undefined) {
let error = new Error("ID required.")
error.statusCode = 400
error.statusMessage = "Request not valid, ID not found."
throw error;
} else {
// Do some stuff...
}
})
// NOTE: After ALL your other middlewares
app.use((err, req, res, next) => {
console.error(err)
res
.status(err.statusCode)
.json(err.statusMessage)
})
Ideally you should log the errors only inside the catch block. Whenever you encounter an error just throw a new error by calling throw new Error("Type your error message here"). Then your function inside catch block will log and handle the error appropriately.
I would change your code to this:
const create = (request, response) => {
try {
const user = request.body;
const userExists = await usersRepository.findOne({ where: { email } });
if(userExists) {
throw new Error("E-mail already in use")
}
const creadtedUser = await usersRepository.create(user);
return response.status(200).json({ user: creadtedUser });
} catch (error) {
function() // log your error
response.status(500).json({ error.message });
}
};
Read more about Errors here.

Conditionally send responses in an Express app

I'm curious whether you can write if statements in an Express app to conditionally execute your code without providing else statements.
if(pred) {
doSomething()
}
return foo;
calcBar(); // doesn't run.
Above is the synchronous code that stops execution after the return statement.
My Express function looks like this:
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
if(data.length === 0) {
res.json({ message: "No data." });
}
const someOtherData = await someOtherGraphQLCall(data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({err})
}
}
I know because of this question that code after the first res.json might still be executed. Is there a way to stop that? I don't want the second GraphQL call to execute if the first if condition is met. Is that possible without using else ?
Edit:
As the question I linked above mentioned, using a return statement is a bad option because:
it also makes it less meaningful and vague, cause it uses incorrect semantics. If you are not using the value from the function, then you shouldn't return one.
You can use return keyword on the first response to immediately return from the function.
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
if(data.length === 0) {
return res.json({ message: "No data." });
}
const someOtherData = await someOtherGraphQLCall(data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({err})
}
}
Edit:
As an alternative, you can split the logic of the data and building up response. This way you can use return and it's easier to read:
app.get('/matches', async function (req, res) {
try {
const data = await getDataFromGraphQLCall();
res.json(data);
} catch (err) {
res.json({ err })
}
});
async function getDataFromGraphQLCall() {
const data = await someGraphQLCall();
if (data.length === 0) {
return { message: "No data." };
}
const someOtherData = await someOtherGraphQLCall(data.foo);
return { someOtherData };
}
If you are wondering if there is a way to achieve that without the else, yes it is.
But, It might not be THE cleanest way. IMO, using return is the best way to stop the execution of the controller.
Anyways, You can split the chunk of code into middlewares and use ternary operator to conditionally send responses.
In your example, separate out data = await someGraphQLCall(); as follows:
const middlewareOne = async function(req, res, next) {
let data = [];
let response = { message: "No data." };
try {
data = await someGraphQLCall();
req.locals.data = data; // <- attach the data to req.locals
} catch (err) {
response = { err };
}
data.length === 0 ? res.json(response) : next();
};
And then, mount the middlewareOne BEFORE your controller:
app.get("/matches", middlewareOne, async function controller(req, res) {
try {
const someOtherData = await someOtherGraphQLCall(req.locals.data.foo);
res.json({ someOtherData });
} catch (err) {
res.json({ err });
}
});
How this works is, the controller function would only be executed by express if the next() is called from the previous middleware -- middlewareOne in the example.
And as middlewareOne only calls next() if the data.length is not 0, it would work as you expected.
For more information on passing data from one middleware to other, read this
The return statement terminates the function execution in this context. In my opinion, you should handle the success case then the error case since the code will be read top to bottom.
In if statement, data could be undefined or null.
You can read more here: MDN - return
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
// alternative, if (data && data[0]) {
if (data && data.length) {
const someOtherData = await someOtherGraphQLCall(data.foo);
return res.json({ someOtherData });
}
return res.json({ message: "No data." });
} catch (err) {
console.log(err); // log error with logger and drain to loggly.
res.json({ err })
}
}
With Void operator:
Void operator allows you to return undefined but evaluate the given expression.
You can read more here: MDN - Void
app.get('/matches', async function(req, res) {
try {
const data = await someGraphQLCall();
// alternative, if (data && data[0]) {
if (data && data.length) {
const someOtherData = await someOtherGraphQLCall(data.foo);
return void res.json({ someOtherData });
}
return void res.json({ message: "No data." });
} catch (err) {
console.log(err); // log error with logger and drain to loggly.
res.json({ err })
}
}

Error: Callback was already called in loopback

I have the following code:
"use strict";
const Raven = require("raven");
Raven.config(
"test"
).install();
module.exports = function(Reservation) {
function dateValidator(err) {
if (this.startDate >= this.endDate) {
err();
}
}
function sendEmail(campground) {
return new Promise((resolve, reject) => {
Reservation.app.models.Email.send(formEmailObject(campground),
function(
err,
mail
) {
if (err) {
console.log(err);
Raven.captureException(err);
reject(err);
} else {
console.log(mail);
console.log("email sent!");
resolve(mail);
}
});
});
}
function formEmailObject(campground) {
return {
to: "loopbackintern#yopmail.com",
from: "noreply#optis.be",
subject: "Thank you for your reservation at " + campground.name,
html:
"<p>We confirm your reservation for <strong>" +
campground.name +
"</strong></p>"
};
}
Reservation.validate("startDate", dateValidator, {
message: "endDate should be after startDate"
});
Reservation.observe("after save", async function(ctx, next) {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
next();
} catch (e) {
Raven.captureException(e);
next(e);
}
});
};
Sorry for the poor formatting. When the flow is done I get this error:
(node:3907) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Callback was already called.
I am calling the next() callback in two places, one in the try code and one in the catch code. I assume that when it all goes right, next callback is called only once, and the same when it goes wrong. But it seems that it is called twice and I don't know why.
I also tried to call next outside the try/catch code but it results in the same error. If I left only the next that is called inside the catch code it doesn't throw the error.
Any idea? Thanks!
if you are using async function you shouldn't explicitly call next, it gets automatically called.
check out this github issue for loopback async/await
so your hook can be like the following.
Reservation.observe("after save", async ctx => {
try {
const campground = await Reservation.app.models.Campground.findById(
ctx.instance.campgroundId
);
const mail = await sendEmail(campground);
} catch (e) {
Raven.captureException(e);
throw e;
}
});
NB: you don't need to wrap it in try catch unless you want to modify/work with the error.
You should declare your sendEmail method as async as it returns a promise.
async function sendEmail(campground) {
...
}
After reading this article, I created a await-handler.js file which include following code.
module.exports = (promise) =>
promise
.then(data => ({
ok: true,
data
}))
.catch(error =>
Promise.resolve({
ok: false,
error
})
);
Then in MyModel.js file, I created a async function to get a value from database as follow.
const awaitHandler = require("./../await-handler.js")
const getMaxNumber = async (MyModel) => {
let result = await awaitHandler(MyModel.find());
if (result.ok) {
if (result.data.length) {
return result.data.reduce((max, b) => Math.max(max, b.propertyName), result.data[0] && result.data[0].propertyName);
} else {
return 0;
}
} else {
return result.error;
}
}
As per #Mehari's answer, I've commented call to next() method as follow:-
module.exports = function(MyModel) {
MyModel.observe('before save', async(ctx, next) => {
const maxNumber = await getMaxNumber (MyModel);
if(ctx.instance) {
...
set the required property using ctx.instance.*
like createdAt, createdBy properties
...
// return next();
} else {
...
code for patch
...
// return next();
}
})
}
This solves the warning issue whenever saving endpoint is triggered.
But the warning issue still appear when I run the endpoint to load the resource.Like
http://localhost:3000/api/MyModel
Previously, the issue appear only when the before save operation hook gets triggered.
After encountering this issue, I checked adding access and loaded operation hooks and I found that the the warnings are issued after loaded operation hook.
MyModel.observe('access', (ctx, next) => {
return next();
})
MyModel.observe('loaded', (ctx, next) => {
return next();
})
What could have caused this issue and how can it gets resolved?

Node.js with Express - throw Error vs next(error)

Can someone expound on the times when it's appropriate in a node.js Express app to throw an error like so:
throw new Error('my error');
or to pass this error on via the callback usually labelled 'next' like so:
next(error);
and could you please explain what each of them will do in the context of an Express app?
for example, here is an express function dealing with URL parameters:
app.param('lineup_id', function (req, res, next, lineup_id) {
// typically we might sanity check that user_id is of the right format
if (lineup_id == null) {
console.log('null lineup_id');
req.lineup = null;
return next(new Error("lineup_id is null"));
}
var user_id = app.getMainUser()._id;
var Lineup = app.mongooseModels.LineupModel.getNewLineup(app.system_db(), user_id);
Lineup.findById(lineup_id, function (err, lineup) {
if (err) {
return next(err);
}
if (!lineup) {
console.log('no lineup matched');
return next(new Error("no lineup matched"));
}
req.lineup = lineup;
return next();
});
});
In the line commented "//should I create my own error here?"
I could used "throw new Error('xyz')", but what exactly would that do? Why is it usually better to pass the error to the callback 'next'?
Another question is - how do I get "throw new Error('xyz')" to show up in the console as well as the browser when I am in development?
In general express follows the way of passing errors rather than throwing it, for any errors in the program you can pass the error object to 'next', also an error handler needs to be defined so that all the errors passed to 'next' can be handled properly.
http://expressjs.com/en/guide/error-handling.html
Throwing an error inside a callback doesn't work:
app.get('/', function (req, res) {
fs.mkdir('.', (err) => {
if (err) throw err;
});
});
But calling next works:
app.get('/', function (req, res, next) {
fs.mkdir('.', (err) => {
if (err) next(err);
});
});
Errors that occur in synchronous code inside route handlers and middleware require no extra work. If synchronous code throws an error, then Express will catch and process it. For example:
app.get('/', function (req, res) {
throw new Error('BROKEN') // Express will catch this on its own.
})
For those who prefer throwing errors, here is a workaround decorator:
export function safeThrow(
target: object,
key: string | symbol,
descriptor: TypedPropertyDescriptor<(req: Request, res: Response, next: NextFunction) => Promise<any>>) {
const fun = descriptor.value;
descriptor.value = async function () {
try {
await fun.apply(this, arguments);
} catch (err) {
arguments[2](err);
}
};
}
#safeThrow
private async get(req: Request, res: Response, next: NextFunction) {
throw { status: 404, message: 'Not supported' }
}

Resources