How to specify HTTP error code using Express.js? - node.js

I have tried:
app.get('/', function(req, res, next) {
var e = new Error('error message');
e.status = 400;
next(e);
});
and:
app.get('/', function(req, res, next) {
res.statusCode = 400;
var e = new Error('error message');
next(e);
});
but always an error code of 500 is announced.

Per the Express (Version 4+) docs, you can use:
res.status(400);
res.send('None shall pass');
http://expressjs.com/4x/api.html#res.status
<=3.8
res.statusCode = 401;
res.send('None shall pass');

A simple one liner;
res.status(404).send("Oh uh, something went wrong");

I'd like to centralize the creation of the error response in this way:
app.get('/test', function(req, res){
throw {status: 500, message: 'detailed message'};
});
app.use(function (err, req, res, next) {
res.status(err.status || 500).json({status: err.status, message: err.message})
});
So I have always the same error output format.
PS: of course you could create an object to extend the standard error like this:
const AppError = require('./lib/app-error');
app.get('/test', function(req, res){
throw new AppError('Detail Message', 500)
});
'use strict';
module.exports = function AppError(message, httpStatus) {
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
this.status = httpStatus;
};
require('util').inherits(module.exports, Error);

You can use res.send('OMG :(', 404); just res.send(404);

In express 4.0 they got it right :)
res.sendStatus(statusCode)
// Sets the response HTTP status code to statusCode and send its string representation as the response body.
res.sendStatus(200); // equivalent to res.status(200).send('OK')
res.sendStatus(403); // equivalent to res.status(403).send('Forbidden')
res.sendStatus(404); // equivalent to res.status(404).send('Not Found')
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
//If an unsupported status code is specified, the HTTP status is still set to statusCode and the string version of the code is sent as the response body.
res.sendStatus(2000); // equivalent to res.status(2000).send('2000')

From what I saw in Express 4.0 this works for me. This is example of authentication required middleware.
function apiDemandLoggedIn(req, res, next) {
// if user is authenticated in the session, carry on
console.log('isAuth', req.isAuthenticated(), req.user);
if (req.isAuthenticated())
return next();
// If not return 401 response which means unauthroized.
var err = new Error();
err.status = 401;
next(err);
}

The version of the errorHandler middleware bundled with some (perhaps older?) versions of express seems to have the status code hardcoded. The version documented here: http://www.senchalabs.org/connect/errorHandler.html on the other hand lets you do what you are trying to do. So, perhaps trying upgrading to the latest version of express/connect.

I tried
res.status(400);
res.send('message');
..but it was giving me error:
(node:208) UnhandledPromiseRejectionWarning: Error: Can't set headers
after they are sent.
This work for me
res.status(400).send(yourMessage);

Old question, but still coming up on Google. In the current version of Express (3.4.0), you can alter res.statusCode before calling next(err):
res.statusCode = 404;
next(new Error('File not found'));

Express deprecated res.send(body, status).
Use res.status(status).send(body) or res.sendStatus(status) instead

I would recommend handling the sending of http error codes by using the Boom package.

Async way:
myNodeJs.processAsync(pays)
.then((result) => {
myLog.logger.info('API 200 OK');
res.statusCode = 200;
res.json(result);
myLog.logger.response(result);
})
.fail((error) => {
if (error instanceof myTypes.types.MyError) {
log.logger.info(`My Custom Error:${error.toString()}`);
res.statusCode = 400;
res.json(error);
} else {
log.logger.error(error);
res.statusCode = 500;
// it seems standard errors do not go properly into json by themselves
res.json({
name: error.name,
message: error.message
});
}
log.logger.response(error);
})
.done();

If you want to send the status code without its string representation you can do:
res.status(200).send();

Related

Status 404 instead of 400 when testing with Superagent / Supertest / Jest

Situation
I am building and testing a simple api with express. One of the routes is /api/blogs/:id, where the api should return a status 400 if the provided id is in the wrong format, and 404 if the id is not present in the database.
Problem
The api works fine and responds with the right status codes when making requests by browser or the REST client plugin in vsCode. But when I make requests with a malformed id via Superagent in my unit tests, the server responds with a 404, when it should be a 400.
Route
blogsRouter.get('/:id', async (req, res, next) => {
try {
const { id } = req.params;
const blog = await Blog.findById(id);
if (blog) {
res.status(200).json(blog);
} else {
res.status(404).end();
}
} catch (error) {
next(error);
}
});
Normally, Mongoose throws an error when running Blog.findById(id), triggering catch (error) { next(error) }, which executes the next middleware function errorHandler. For some reason though, this doesn't seem to happen when testing.
errorHandler
const errorHandler = (error, req, res, next) => {
if (error.name === 'CastError') {
res.status(400).json({ error: 'malformatted id' });
return;
} if (error.name === 'ValidationError') {
console.log(error.message);
res.status(400).json({ error: error.message });
return;
}
next(error);
};
test
test("fails with status 400 when 'id' has wrong format", async () => {
const invalidId = '45234sdsdasf';
await api.get(`/api/blogs/${invalidId}`)
.expect(400);
});
I found the solution.
The problem was const invalidId = '45234sdsdasf'.
This should be a malformed id, but for some reason Mongoose sees 12 characters long strings as a correct objectId. Changing invalidId to something else solved the problem.

Express router, Router.post executing before router.use middleware

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.

Express central error handling with app.use

Hey guys I want to achieve central error handling in express, I've done this.
app.use(function(err,req,res,next){
logger.error(err);
utils.jsonOutHandler(err,null,res);
next(err);
});
app.get('/',(req,res)=>{
throw new Error('Testing');
});
Also I made a special jsonOutHandler method which sends proper response to the user.
function jsonOutHandler(err, result, out) {
if (err) {
let status = 500;
const message = 'Something broke';
if (err instanceof DbError) {
status = 500;
}
if (err instanceof ValidationError) {
status = 400;
}
if (err instanceof SystemError) {
status = 500;
}
out.status(status).send({message});
return;
}
out.status(result.status).send({data: result.data});
}
But whenever I throw error on '/' route my error handler is never triggered. Why?
Express is based on middlewares, so, if you wanna catch the errors inside the middleware, you should call the error middleware:
app.get('/',(req,res)=>{
next(new Error('Testing'));
});
/**
* middleware to catch errors happened before this middleware
* express knows this middleware is for error handling because it has
* four parameters (err, req, res, next)
**/
app.use((err, req, res, next) => {
res.status(500).send({
message: err.message,
});
});
I hope you can adapt this example to your requirements. The thing to keep in mind is an error middleware can be used from previous middlewares. In your example you couldn't catch the error because your middleware was defined before your main router app.get('/')

NodeJS route with API request in it error

I am writing a route that checks if a system app is online and then responds to the client with a simple 200 status ok, or a 404 status.
I'm using express and request to make the api call.
Route looks like this:
app.get('/status/keymgr', async (req, res, next) => {
try{
var endpoint = `http://${config.KeyManager.host}:${config.KeyManager.adminPort}/healthcheck`;
console.log(endpoint);
await request.get(endpoint, function(err, response, body){
if (!err && response.statusCode == 200){
res.send('OK');
}else{
res.status(404);
}
}).end();
}catch(error){
res.status(404);
}finally{
next();
}
});
For some reason, I am getting the following error:
uncaughtException: Can't set headers after they are sent.
I am guessing some kind of response is being sent to the browser before the route runs the res.send() or the res.status().
I can't figure out what's wrong here. Any idea??
AS #ndugger mentioned, the reason you are getting this exception is because request.get does not return a promise and hence await here is of no use. You have two options, either you use util.promisify or wrap your request under a new promise and resolve only when the callback finishes. Something like this
app.get('/status/keymgr', async (req, res, next) => {
var endpoint = `http://${config.KeyManager.host}:${config.KeyManager.adminPort}/healthcheck`;
console.log(endpoint);
try {
await new Promise((resolve, reject) => {
request.get(endpoint, function (err, response, body) {
if (!err && response.statusCode == 200) {
// res.send('OK');
resolve('OK');
} else {
reject('404')
// res.status(404);
}
});
});
res.send('OK');
} catch (err) {
res.status(404);
} finally {
next();
}
}

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