I have an Express.js 4 app, with an error logging middleware of the following form:
...
const logErrors = (err, req, res, next) => {
logger.error(err);
res.status(400).json({ error: err.message });
}
app.use(logErrors);
I was previously under the (incorrect) assumption, that by not calling next() the request would simply not continue onto my route handler. This is not the case as when an error occurs, it gets logged and sent as a response to the user but the request continues to /myroutehandler where it causes problems.
How do I stop the route/middleware chain after I've logged and responded to the error in my middleware? or what is the correct way to go about this?
Bonus question: Since I am not calling next() shouldn't the request hang? how/why is express handling this?
EDIT:
More of my code, to figure out why request jumps from the middleware to the route
app.use(morgan('combined', { stream: logger.inputStream }));
app.use(authMiddleware);
app.use(xmlParser({
normalize: false,
normalizeTags: false,
trim: true,
mergeAttrs: true,
explicitArray: false
}));
app.use(logErrors);
app.post('/HotLeadsFeed', async (req, res) => {
if (!req.body || !req.body.pi || !req.body.pi.pa) {
//error logging
}
try {
//logic
} catch (err) {
//error handling
}
});
Related
Server.ts
import express, { Request, Response, NextFunction } from 'express'
import { router } from "./routes"
import { Database } from './Database/index'
const app = express()
const db = new Database()
db.connect()
app.use(router) // contains all my routes
// The middleware I'm trying to be fired
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof Error) {
return res.status(400).json({
error: err.message
})
}
return res.status(500).json({
status: 'error',
message: 'Internal Server Error :('
})
})
app.listen(3000, () => console.log('Server is running'))
According to Express4 Documentation
You define error-handling middleware last, after other app.use() and routes calls;
So I think it's right. But for some reason its NOT being called/triggered.
Why it's NOT working?
When I try to access an URL that doesn't exists inside routes.ts it gives a standard 404 error, but I want to get it from my middleware.
I want to use this Middleware for all errors, but most for when user makes a request for an endpoint that doesn't exists.
This is because in 404 is not an error. It just means there is no matching route.
From the docs:
In Express, 404 responses are not the result of an error, so the error-handler middleware will not capture them.
This is the default code from the docs. Add this at the bottom:
app.use(function (req, res, next) {
res.status(404).send("Sorry can't find that!")
})
i'm working on node.js using Express to built a backend.
i'm intended to handle status 500 error that may happened.
router.put('/test', async (req, res) => {
try {
return res.send(await request.updateTest(req.body, 1))
} catch(err) {
console.log(err)
return res.status(500).send(err.stack)
}
})
this is my example of the code. it's do work perfectly. but when i'm try to make unknown error from the database query, i want to log the error and return status 500 as response with the error detail.
but i'll need to add try and catch every time i'm build a new controller/routes
is there anyway i could express them in form of middleware instead of write try and catch everytime?
this is an example of code i've try to make it as middleware but it's has no work and no effect when called.
error.js
module.exports = function (err, req, res, next) {
console.log(err)
res.status(500).send({
error: 'Internal Server Error',
message: err.stack
})
next(err)
}
main.js
const errorHandler = require('./error')
const { corsOption } = require('./cors')
const cors = require('cors')
const test = require('./test')
module.exports = function (app) {
app.use(cors(corsOption))
app.use(errorHandler)
app.use('/api/test', test)
}
is there anyway that i can do for this to work?
Your global error handler should be placed after all other middlewares/routes:
app.use(middleware)
// all other middlewares
app.use('/api/test', test)
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
To avoid adding try/catch to everything, better to wrap your route handler to catch the errors (asyncWrapper):
app.use(middleware)
// all other middlewares
const asyncWrapper = (cb) => {
return (req, res, next) => cb(req, res, next).catch(next);
};
const test = async (req, res) => {
return res.send(await request.updateTest(req.body, 1))
}
// wrap your handler to catch errors (Async functions return a promise)
app.use('/api/test', asyncWrapper(test))
// all other routes
// error handler
app.use(function (err, req, res, next) {
res.status(500).json({
error: err.message,
});
});
There are two approaches to resolve unhandled exceptions in Node.js
Using try-catch blockwhich is already you are using
Using Process i.e use Process to handle exception. A process is a global object that provides information about the current Node.js process. The process is a listener function that is always listening to the events. The most effective and efficient approach is to use Process. If any uncaught or unhandled exception occurs in your code flow, that exception will be caught in code
process.on('uncaughtException', function(err) {
// Handle the error safely
console.log(err)
})
The above code will be able to handle any sort of unhandled exception which occurs in Node.js. see this Process Events
I'm having some trouble error handling my authentication API calls. When I send the 500 status from Express, my frontend (Vue in this case) only picks up the message Request failed with status code 500 rather than something more helpful for triage like this is the worst error ever (in the example below).
In the below example, when I call '/post' from the API, I throw an error which is handled by my custom middleware. The middleware successfully handles the error and sends the appropriate status to my frontend, but I can't figure out how to send useful messages (e.g. 'this is the worst error ever') / access them in the front end.
Is this a common use case? Am I doing anything obviously wrong? Does the message I send come up in the (err) parameter, or do I need to add a resp parameter?
Express Route for '/login'
router.post('/login', (req, res, next) => {
throw Error('this is the worst error ever')
})
Custom express error handler
app.use(function(err, req, res, next) {
res.status(err.status || 500).send({
error: {
status: err.status || 500,
message: err.message || 'Internal Server Error',
},
});
});
Handle the API Response in Vue
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.message
return
})
}
Found the answer to this for those that find this helpful. The err that is caught has a response variable. This is where the data is sent via the express send command. See corrected code below for the frontend:
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.response.data.message
return
})
}
I think you need to add:
Throw new Error()
instead of
Throw Error
If you are making an asynchronous calling you can do this
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
Code should look like this:
router.post('/login', (req, res, next) => {
throw new Error('this is the worst error ever')
})
Check this in express documentation
I have a route that goes to the index page. I have a secret token that allows access to this page. I want to compare the requested URL with a custom string. The current URL in use is http://localhost:3000/?token=secret but if I enter http://localhost:3000/as?token=secret it doesn't render the 404 error page that I created, instead says Cannot GET /as. I wondering how to validate this and render out the error page correctly
app.get('/', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
In Express each app.get or other related method handles it's own route. So when you do app.get('/' you are only matching routes that are / not /as.
You could change it to * to match all routes. Maybe like the following?
app.get('*', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
Or of course you could have a dedicated section for your 404 messages.
app.get('/', (req, res, next) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
return next();
}
// Valid request
});
app.get('*', (req, res) => {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
});
At the end of the day there are so many ways you can handle Express routing. It's very powerful and flexible.
I'd suggest you look here under the How do I handle 404 responses? section for another idea as well.
Also, remember, having secrets in a URL like that, probably isn't the most secure thing. So there are a lot of reasons I wouldn't suggest this for security reasons. But just answering your question, the above should work.
The idiomatic way to handle 404s in Express is to register your final route handler with the use method rather than using one of the HTTP-specific methods.
app.use((req, res) => {
res.render('error', {
title: '404 not found',
errorMessage: '404 not found'
})
})
I emphasize the word final because use registers a catch-all handler, so this will override any route that it precedes in your code. If all your other routes are registered before this, then this will catch any request that has not matched any other route – regardless of the HTTP method that was used. So this will apply to any GET, POST, PUT, DELETE request.
An even more idiomatic way in Express to handle 404s (and all HTTP error responses) is to use the next argument that comes with all route handlers. This will re-route the request to the next handler that specifically takes an error as it's first argument:
app.use((req, res, next) => {
const error = new Error('404 not found')
error.statusCode = 404
next(error)
})
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.render('error', {
title: error.message,
errorMessage: error.message
})
})
This is great because you now have a generic error handler, which you can access from inside any other route. So this will handle not only 404s, but also 401s, 403s, 503s, anything you want that doesn't render successfully for the user. And you can access this route simply by calling next with an error as the first argument from inside any other route handler.
I suggest you to use passport-auth-token for validating the token & display success or error pages.
Configure Strategy
The token authentication strategy authenticates users using a token. The strategy requires a verify callback, which accepts these credentials and calls done providing a user.
passport.use('authtoken', new AuthTokenStrategy(
function(token, done) {
AccessToken.findOne({
id: token
}, function(error, accessToken) {
if (error) {
return done(error);
}
if (accessToken) {
if (!token.isValid(accessToken)) {
return done(null, false);
}
User.findOne({
id: accessToken.userId
}, function(error, user) {
if (error) {
return done(error);
}
if (!user) {
return done(null, false);
}
return done(null, user);
});
} else {
return done(null);
}
});
}
));
Authenticate Requests
Use passport.authenticate(), specifying the 'authtoken' strategy, to authenticate requests.
For example, as route middleware in an Express application:
app.post('/login',
passport.authenticate(
'authtoken',
{
session: false,
optional: false
}
),
function(req, res) {
res.redirect('/');
}
);
I'm making a search-page on my server. When the endpoint is reached and the user waits for the search function to return the results and render the page Express falls through to the 404 handler instead, and I get the following error when I suppose the render function is called:
Error: Can't set headers after they are sent.
What am I doing wrong?
router.get("/", async (req, res) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if(s.indexOf(",") > -1){
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": {tags: {$all: searchedTags}, _forSale: true}
};
const results = await Search.search(options).then(result => result).catch(err => {
throw err;
});
//This res.render -call is called after the 404 splat-route.
return res.render("partial/search.pug", {user: user, search: {
query: queryString,
results: results
}});
//If I'd use res.send for debugging, it is instead called before the splat-route, like the following:
return res.send(results);
} catch(err) {
next(err);
}
});
module.exports = router;
I register the router:
const search = require("./search.js");
app.use("/search", search);
Followed by the 404 splat-route:
app.get("*", async (req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
To clarify:
My question is how can I make the res.render function get called just as the res.send function?
UPDATE [2017-10-05]:
I continued with another part of the site, a similar endpoint, and discovered that sending the result provided by the promise worked as expected if using res.send but not res.render. Using res.render the 404-handler kicked in again. Can this be a bug in Express?
This happens if you attempt to write to res after it is sent, so you must be calling additional code after res.render() or you already responded before calling that.
change it to return res.render(...) so it exits the functions, otherwise it will continue through the function and hit other res.render()s etc.
Something is up with that error handler also. I will update my post in a few mins with tips (on phone). It should probably have (req, res, next) and call return next(err) and pass it to your error handling middleware.
Here is the pattern I like to use in async/await Express:
// these routes occur in the order I show them
app.get('/route', async (req, res, next) => {
try {
const data = 'asdf'
const payload = await something(data)
.then((result) => createPayload(result))
// remember, if you throw anywhere in try block, it will send to catch block
// const something = willFail().catch((error) => {
// throw 'Custom error message:' + error.message
// })
// return from the route so nothing else is fired
return res.render('route', { payload })
} catch (e) {
// fire down to error middleware
return next(e)
}
})
// SPLAT
app.get('*', async (req, res, next) => {
// if no matching routes, return 404
return res.status(404).render('error/404')
})
// ERRORS
app.use(async (err, req, res, next) => {
// if err !== null, this middleware fires
// it has a 4th input param "err"
res.status(500).render('error/500')
// and do whatever else after...
throw err
})
Note: next() callback called without param is treated as no error, and proceeds to the next middleware. If anything is passed in, it will fire the error middleware with the param as the value of err in the error handling middleware. You can use this technique in routes and other middlewares, as long as the error middleware comes last. Mind your use of return with res.send/render() to prevent double setting headers.
NEW:
Something looks a little bit off with that .then() having a callback in it. I don't see logically where err would come from since the value of the resolved promise goes into the .then() function as result. At this point, it is suspect and should be removed or refactored if possible. This part here:
try {
let results = [];
await Search.search(options).then(result => {
results = result;
}, err => {
throw err;
});
console.log("res.render");
return res.render("partial/search.pug", {user: user, search: {
query: string,
results: results
}});
} catch(err) {
next(err);
}
First, here is about what I would expect to see with async/await syntax:
router.get("/", async (req, res, next) => {
try {
const queryString = req.query.q;
const user = helper.checkAndGetUser(req, res);
let s = String(queryString), searchedTags = [""];
if (s.indexOf(",") > -1) {
searchedTags = s.replace(" ", "").split(",");
}
const options = {
"query": { tags: { $all: searchedTags }, _forSale: true }
};
// If a promise is ever rejected inside a try block,
// it passes the error to the catch block.
// If you handle it properly there, you avoid unhandled promise rejections.
// Since, we have async in the route function, we can use await
// we assign the value of Search.search(options) to results.
// It will not proceed to the render statement
// until the entire promise chain is resolved.
// hence, then(data => { return data }) energizes `results`
const results = await Search.search(options)
.then(data => data)
// If any promise in this chain is rejected, this will fire
// and it will throw the error to the catch block
// and your catch block should pass it through to your
// error handling middleware
.catch(err => { throw 'Problem occurred in index route:' + err });
return res.render("partial/search.pug", {
user: user, search: {
query: string,
results: results
}
});
} catch (err) {
// look at the top how we added next as the 3rd, callback parameter
return next(err);
}
});
module.exports = router;
Error handler:
// notice how we add `err` as first parameter
app.use((err, req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
From the Express docs:
Define error-handling middleware functions in the same way as other middleware functions, except error-handling functions have four arguments instead of three: (err, req, res, next). For example:
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
http://expressjs.com/en/guide/error-handling.html
That might be your true issue because the error handler should only fire if next() is called with any input, but yours appears to be firing every time like a normal middleware, so I suspect it's because there is no err parameter on that middleware function, so it is treated as a normal one.
The Default Error Handler
Express comes with a built-in error handler, which takes care of any errors that might be encountered in the app. This default error-handling middleware function is added at the end of the middleware function stack.
If you pass an error to next() and you do not handle it in an error handler, it will be handled by the built-in error handler; the error will be written to the client with the stack trace. The stack trace is not included in the production environment.
If you call next() with an error after you have started writing the response (for example, if you encounter an error while streaming the response to the client) the Express default error handler closes the connection and fails the request.
So when you add a custom error handler, you will want to delegate to the default error handling mechanisms in Express, when the headers have already been sent to the client:
// code example in docs
Note that the default error handler can get triggered if you call next() with an error in your code more than once, even if custom error handling middleware is in place.
I also recommend using that splat route app.get('*', async (req, res, next) => {}) right above the error handler middlware (aka as the last loaded route in your list). This will catch all unmatched routes, such as /sih8df7h6so8d7f and forward the client to your 404. I think the error handler middlware is more suited for error 500 and clean formatted type errors because it gives you a function that can parse the value of next(err) anytime it is called from a route.
I usually do this for authentication failures with JSON web token (as the first line of code inside every auth required route):
if (!req.person) return res.status(403).render('error/403')
I realize some of this may fry your wig wholesale, so try all this stuff out and see each piece working before you determine if you would like to utilize it or not.
After a few days of going through the code again and again I stumbled upon a problem in the checkAndGetUser-function, that when run without the user being signed in, and since it's faster than the async call to the DB, triggered the splat endpoint and thus showed the 404 page.
I believe the reason for not triggering the splat endpoint when the res.render call was replaced by res.send was that the res.send function is that much faster than the render-call, since it doesn't have to parse any HTML.
Thank you #agm1984 for providing very useful information about the Express framework, if anyone else are having the same or a similar problem, make sure to read through his post.