Skip to content

Instantly share code, notes, and snippets.

@dev-opus
Last active May 4, 2021 08:56
Show Gist options
  • Save dev-opus/896722a6814fd8a9f1f2e44f990f8bbb to your computer and use it in GitHub Desktop.
Save dev-opus/896722a6814fd8a9f1f2e44f990f8bbb to your computer and use it in GitHub Desktop.
better error handling method for asynchronous javascript programming
/*
so uhm, up until about a week ago, my
defacto method for handling errors in an
async/await program-flow was to wrap my
entire code-block in a try/catch; performing
the operations in the `try {}` block and
handling any errors in the `catch(err) {}` block.
it worked well, but it didn't sit well with me
'cause them try/catch blocks be making my code
"messy" and "bulky". also, i asked a friend [not
sure if he considers me a friend though (^_^)]
to review a rest api [scribble_api, check my github :)]
i built for learning purposes.
one of the key issues he raised was my over-
reliance on the try/catch blocks, stating that
it wasn't a "best practices" kinda thing.
so i traversed the internet, searching for a
better method and i found a few, but the one
that appealed the most to me was the method
wes bos used in his 2017 dotjs conference talk
on async/await.
enough of the [lenthy] comments, lets get to the
code! :)
*/
/*
oops! one more lengthy comment block!
i'll be demonstrating this method
by mimicking a node.js/express.js
app development environment
*/
// code begins right under this line!!!
// ./utils/handleErrors.js
// catch async errors and propagate
// them to the error handler function
// using express.js `next()`
const catchAsyncErrors = fn => {
return function (req, res, next) {
return fn(req, res, next).catch(next)
}
};
// error handler to serve errors
// in json instead of express'
// default stack trace printing
const handleError = (err, res) => {
const { message } = err;
const statusCode = err.statusCode || 500;
res.status(statusCode).json({
status: 'Error',
statusCode,
message,
});
};
// export the methods
module.exports = { handleError, catchAsyncErrors };
// ./controllers/notes.js
// require Notes model
const Notes = require('../models/Notes');
// create the async/await function with no
// cares about error handling, in essence,
// no try/catch block
const getNotes = async (req, res) => {
const {noteSize} = req.params
const notes await Notes.find({}).populate('author -__v').limit(noteSize).lean()
if (!notes) {
return res.status(404).json({
status: 'failed',
message: 'No notes in the DB',
data: null
});
}
return res.status(200).json({
status: 'success',
message: 'get notes',
data: notes
});
};
// export the module methods
module.exports = {getNotes, ...otherControllers}
// ./routes/notes.js
// import the catchAsyncErrors method and
// the getNotes cotroller method
const {catchAsyncErrors} = require('../utils/handleErrors');
const {getNotes} = require('../controllers/notes');
// create a route to handle GET requests on /api/notes
router.get('/', catchAsyncErrors(getNotes)); // this is the line of truth!!!
/*
i know. i promised that you wouldn't be seeing this
humongous comment blocks again, but this whole gist would
be a waste if i don't explain what is happening on line
110 to the best of my ability. and yeah, i need the
lenghty comments for that.
getNotes is the async/await controller method with no
built-in error mechanism(s) [!try/catch].
catchAsyncErrors is the higher order func that recieves
a single arg func, in this case, `getNotes`.
when this catchAsyncErrors is called, it creates and
returns another func that recieves three args (req, res, next).
this unnamed func returns the return-value of calling the
func arg passed to catchAsyncErrors, by calling this func
(getNotes) with the the three args passed to it and a
`.catch(next)` method appended to it.
that is where the magic occurs for catching and propagating
any async error that occured in the `getNotes` controller
method.
this explanation might still be very vague for some people
to comprehend, so i intend to make a flow-diagram for the
entire process discussed above and attach it to this gist
at a later time.
pheeew! back to the code now..
*/
// export the router
module.exports = router;
// ./app.js --> the entry point of the application
/*
*face palm* yet another lengthy comment block. *sigh*
assume that the basic module loading for an express
entry point app happens here.
*/
// load the notes-router as well as the error handler modules
// from their respective directories
const notesRouter = require('./routes/notes');
const {handleError} = require('./utils/handleErrors');
// mount the /api/notes route
app.use('/api/notes', notesRouter)
// mount the custom error handler
app.use((err, req, res, next) => {
handleError(err, res);
})
// and that's it! if our app encounters an error
// from say database timeout, our catchAsyncError
// method propagates the error from the contoller
// to the handleError method above using `next`
@Raphdon832
Copy link

It's an awesome find/research. A lot must've been put into this. Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment