How does the the error handler get triggered?
In sample code, I find that it is placed at the bottom of all the middleware functions. Is the position important?
You can refer below example for some details.
Here, for the '/' GET endpoint a middleware explicitly throws an error 'problem error'.
At this point, express error handler mechanism is triggered and it looks for an error handler (with err as a param). As a result, subsequent 'Hello' is not sent back to the client as its handler is not an error one.
Subsequent error handlers logErrors, clientErrorHandler and errorHandler are invoked one by one to perform relevant tasks with final one writing back the response.
The reason they are placed at the end is to catch the errors thrown by declared middlewares and handle them elegantly i.e. printing it, logging it, sending mails etc. Think of it in terms of a try catch mechanism in other languages like Java. If declared above other middlewares, they would render useless as the errors won't be handled. You can see the difference in output by swapping the order of 'GET' request with the error handlers.
const express = require('express');
const app = express();
app.get('/', (req, res, next) => next(new Error('problem error')), (req, res) => {
res.status(200).send("Hello");
});
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
function logErrors (err, req, res, next) {
console.error(err.stack)
next(err)
}
function clientErrorHandler (err, req, res, next) {
if (req.xhr) {
res.status(500).send({ error: 'Something failed!' })
} else {
next(err)
}
}
function errorHandler (err, req, res, next) {
if (res.headersSent) {
return next(err)
}
res.status(500)
res.render('error', { error: err })
}
app.listen(3000, () => console.log('Example app listening on port 3000!'))
Related
I am wondering how express.js works when you specify multiple argument in the app.get, for example:
app.get('/', requireAuth, (req, res) => { })
Here, I believe you have the slash "/" as the route, and (req, res) as arguments for the callback function. But there is also the 'requireAuth' part which was used for authorization. But in the documentation I don't find anything about multiple arguments in the way we use 'requireAuth' now. It does mention that you can use an array of functions but that is not used here (https://expressjs.com/en/guide/routing.html). How does it work?
P.S. The 'requireAuth' code for reference:
module.exports = (req, res, next) => {
const { authorization } = req.headers;
// authorization === 'Bearer laksjdflaksdjasdfklj'
if (!authorization) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const token = authorization.replace('Bearer ', '');
jwt.verify(token, 'MY_SECRET_KEY', async (err, payload) => {
if (err) {
return res.status(401).send({ error: 'You must be logged in.' });
}
const { userId } = payload;
const user = await User.findById(userId);
req.user = user;
next();
});
};
All the arguments are simply middle-ware functions that you call before the actual route logic is run(for individual routes). As long as there is next handling in each of the middle-ware functions, the chain will run till your route logic, and then exit where next isn't handled.
This is the magic of express. You can do many things with it, like error handling, handling different content types etc.
Question: Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Background: The IIFE is used to create an async container to wrap await statements. I can't see a way out of this and I wonder if I'm using the wrong basic, pattern altogether.
Simplified Example:
app.get('/', function(req, res, next) {
(async function() {
try {
let example = await someLogic(x);
} catch(err) {
return next(new Error('oops'));
}
})();
console.log('main endpoint');
});
app.use(function(err, req, res, next) {
console.log('my error', err.message);
});
Using Express is it possible to return an error from inside an IIFE and advance to my error handling middleware?
Yes, that works fine. It will call next(err) just fine. But, your return will return only from the IIFE and the rest of your request handler after the try/catch will still execute (not sure if you want that or not).
FYI, it's probably simpler to declare the request handler as async and then you don't need the IIFE wrapper:
app.get('/', async function(req, res, next) {
try {
let example = await someLogic(x);
console.log('main endpoint');
// send some response here
} catch(err) {
return next(new Error('oops'));
}
});
Previously I worked with Express a bit and now learning Koa.
In Express, when I did a simple get method. The following code works
.get('/applications', (req, res, next) => {
const getTable = `SELECT * FROM applicationtable`
db.query(getTable, values, (err, result) => {
if (err) {
console.log(err)
}else {
res.json(result)
}
})
})
However when I tried to do the same thing in Koa with
.get('/applications', (ctx, next) => {
const getTable = `SELECT * FROM applicationtable`
db.query(getTable, values, (err, result) => {
if (err) {
console.log(err)
}else {
ctx.body = result
}
})
})
It returns a 404 error. I have to explicitly wrap the above one in Koa with a promise and put async await then only it will work, as shown below:
.get('/applications', async(ctx, next) => {
const getTable = `SELECT * FROM applicationtable`
const item = await new Promise(function(resolve, reject) {
db.query(getTable, (err, result) => {
if (err) {
reject(err)
}else {
resolve(result)
}
})
})
ctx.body = item;
})
My question is , why in Express the code does not have to be explicitly wrapped in async await? In what way Koa is behaving differently than Express with the examples here?
Koa is all designed around promises. The request handler itself is async which means it returns a promise and various things happen in the framework when that promise resolves. As such, you can't freely mix plain asynchronous callbacks with promises.
In the case of your second code block, your request handler returns (which resolves the async promise) BEFORE your asynchronous callback gets called and thus before you set ctx.body. So, Koa goes to process the request (thinking you are done), but ctx.body hasn't been set yet so it figures this must be a 404.
Wrapping your async operation in a promise and using await with it, then chains it into the main request handler promise so that promise doesn't resolve until your asynchronous operation is done (what you want).
Bottom line, use promises that are linked into the request handler (either with await or by returning a promise) for all your asynchronous operations in the request handler.
Here's a nice little example in the Koa developer's guide: https://github.com/koajs/koa/blob/master/docs/guide.md#async-operations
Express, on the other hand, does not do anything when your request handler returns. It doesn't do anything until you either call next() to continue looking for matches with other route handlers or call res.send() or some similar API call that sends a response so you're free to do that in a plain asynchronous callback like you show.
This is part of the core design difference between Express and Koa.
If I send a valid fetch api query to this Express server it works fine. If I send an invalid query the server crashes (ReferenceError: next is not defined). How can I change this so that if an error occurs;
the server does not crash
the client receives an error message from the server
Express server.js:
// Add a new test-resource-a
app.post('/test-resource-a', (request, response) => {
pool.query('INSERT INTO my_table SET ?', request.body, (error, result) => {
if (error) {
next(err);
}
response.status(201).send(`test-resource-a added with id: ${result.insertId}`);
});
});
//An error handling middleware
app.use(function (err, req, res, next) {
res.status(500);
res.send("Oops, something went wrong.")
});
This error is mean the next method is not define.
In your case, I think you don't need the next method.
// Add a new test-resource-a
app.post('/test-resource-a', (request, response) => {
pool.query('INSERT INTO my_table SET ?', request.body, (error, result) => {
if (error) {
response.status(400).send(err);
} else {
response.status(201).send(`test-resource-a added with id: ${result.insertId}`);
}
});
});
//An error handling middleware
app.use(function (err, req, res, next) {
res.status(500);
res.send("Oops, something went wrong.")
});
I actually have a couple of questions about Express middleware chaining and thought I'd group them in this single post since they're closely related.
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an argument to create a certain middleware, and that argument is dependent on which Express app object is the owner of the Request. So I need to access a property of the Request before it is passed to the middleware that needs to be created using that property. Would this work: ?
router.get('/d', (req, res, next) => {
var middlewareFunction = createDynamicMiddleware();
middlewareFunction(req, res, next); // Will this still work as expected?
});
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Q1: In a middleware function, if I send() the Response, pass to the next function, or just return, does any of the remaining code get
executed?
router.get('/a', (req, res, next) => {
res.sendStatus(200);
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/b', (req, res, next) => {
next();
// Does this ever get executed?
// Yes, but don't call next() or res.send() or it will throw an error
});
router.get('/c', (req, res, next) => {
return;
// Does this stop the middleware chain? What happens to the request/response?
// The request get stuck and after a while it will fail due 'timeout'
});
Q2: Can you pass on to another middleware function from inside of the preceding one? My reason for this is that I have to supply an
argument to create a certain middleware, and that argument is
dependent on which Express app object is the owner of the Request. So
I need to access a property of the Request before it is passed to the
middleware that needs to be created using that property.
Your factory of dynamic middleware should return a new function
Functions that returns new functions, are also knows as curried functions, here is a practical example:
// based on 'someId' we will return a specific middleware
req.get('/user/:someId',
createDynamicMiddleware,
(req,res) => {
res.send('ok')
})
function createDynamicMiddleware(req,res,next){
if(req.params.someId === 'me') {
return function(req, res, next){
// do some stuff if someId is me
return next();
}
} else {
return function(req, res, next){
// do some other stuff otherwise
return next();
}
}
}
Q3: How are the Request/Response arguments passed to the next middleware when you call next() without any arguments?
Express handle this for you!
Also, if you add a property to req or res objects, it will be present in the next middlewares
For example:
router.get('/', (req,res,next) => {
req.customProperty = 'hello!';
return next();
}, (req, res) => {
console.log(req.myCustomProperty)
// hello!
})