According to the documentation of the redis npm package, it is apparent that it follows standard Node-style callbacks. In this callback style, the first argument passed to your provided callback function is either an error or null. This is where errors are reported. Errors cannot be caught by a try/catch block because by the time the error occurs, control has already moved out of the try/catch and the surrounding function.
To handle errors in this scenario, you would do something like this:
module.exports = (req, res, next) => {
const redis = require('redis')
const client = redis.createClient()
client.select('2d', (err) => { // using '2d' string instead of number deliberately generates an error
if (err) {
// Error handling goes here
err.type = 'SilentSystem'
next(err)
} else {
// Success handling goes here
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
client.set(ip, true, 'EX', 120, (err, rep) => {
if (err) {
err.type = 'SilentSystem'
next(err)
} else {
next()
}
})
}
})
}
In a comment, you mentioned:
My actual code is a bit more complex so I was trying to avoid repeating calling if(err) and next(err) by using try. What's a better way (less verbose) to handle errors here?
This verbosity is inherent in Node-style callbacks. One approach could be to create a filtering function to pass all results through for centralized error handling.
Alternatively, you might consider utilizing a library that "promisifies" Node-style callbacks, allowing you to use promises with their chaining mechanism for centralized error handling. A package like `promisify` can help achieve this transformation. With promisified versions of client methods, the code could be simplified as follows:
module.exports = (req, res, next) => {
const redis = require('redis')
const client = makeNiftyPromiseVersionOf(redis.createClient())
client.select('2d')
.then(data => {
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
return client.set(ip, true, 'EX', 120)
})
.then(() => {
next()
})
.catch(err => {
err.type = 'SilentSystem'
next(err)
})
}
Note how the consolidated error handling at the end simplifies the process. Any errors from client operations will flow through the catch block for handling.
This setup also allows for utilizing ES2017's async/await syntax for writing asynchronous code in a synchronous style:
module.exports = (req, res, next) => {
(async () => {
const redis = require('redis')
const client = makeNiftyPromiseVersionOf(redis.createClient())
try {
const data = await client.select('2d');
const ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
await client.set(ip, true, 'EX', 120)
next()
} catch (err) {
err.type = 'SilentSystem'
next(err)
}
})();
}
As a side note, it's recommended to move the require call outside of the exported function and perform it at the module level:
const redis = require('redis')
module.exports = {
// ...
}