If you find yourself trying to modify res
after it has already been sent, it means that additional code is being called after res.render()
or a response has already been given before that point.
To prevent this issue, use return res.render(...)
to exit the function immediately after rendering, avoiding any further processing that may interfere with the response.
The error handling mechanism also requires attention. Make sure your error handler includes (req, res, next)
and utilizes return next(err)
to pass errors to your error middleware for proper handling.
Here is an example of how I like to structure async/await functions in Express:
// organize your routes as shown below
app.get('/route', async (req, res, next) => {
try {
const data = 'example'
const payload = await something(data)
.then((result) => createPayload(result))
return res.render('route', { payload })
} catch (e) {
return next(e)
}
})
// Catch all unmatched routes
app.get('*', async (req, res, next) => {
return res.status(404).render('error/404')
})
// Error Handling
app.use(async (err, req, res, next) => {
res.status(500).render('error/500')
throw err
})
Note: When calling next()
without any parameter, it signifies no error and proceeds to the next middleware. Passing anything triggers the error middleware with the value as the error parameter. Maintain consistency with the usage of return
when using res.send/render()
to avoid header conflicts.
UPDATE:
Anomalies might arise from having a callback within your .then()
statement. It's unclear where the err
would originate, considering that resolved promises go into the .then()
function as result
. Consider revising or removing this part:
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);
}
Below is an updated version utilizing 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 }
};
const results = await Search.search(options)
.then(data => data)
.catch(err => { throw 'Problem occurred in index route:' + err });
return res.render("partial/search.pug", {
user: user, search: {
query: string,
results: results
}
});
} catch (err) {
return next(err);
}
});
module.exports = router;
Error handler:
app.use((err, req, res, next) => {
const user = helper.checkAndGetUser(req, res);
res.status(404);
res.render("partial/404.pug", {user: user});
});
Referencing the Express documentation:
Remember to define error-handling middleware functions with four arguments instead of three: (err, req, res, next).
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
Your current situation with the error handler appears to be causing issues as it behaves like a regular middleware rather than activating only when next()
is triggered with input. Ensure the presence of the err
parameter in the middleware function to address this concern.
The Default Error Handler
Express furnishes a built-in error handler to manage encountered errors. This default mechanism handles errors not addressed by custom middleware, sending them to the client sans stack trace display in production environments. Additionally, errors occurring post-response initiation prompt closure by the default error handler.
Integrating a custom error handler necessitates delegating to Express's default mechanisms once headers have been transmitted to the client.
I suggest placing the splat route
app.get('*', async (req, res, next) => {})
preceding the error-handling middleware at the end of your route list. This ensures capture of any unmatched paths and directs clients to the designated 404 page while preserving the error-handler's ability to process errors passed through
next(err)
.
Consider implementing the following authentication failure safeguard within auth-required routes as an initial check:
if (!req.person) return res.status(403).render('error/403')
Experiment with these practices individually to gauge effectiveness before deciding on implementation for your scenarios.