Last active
August 8, 2024 12:10
-
-
Save dev-opus/896722a6814fd8a9f1f2e44f990f8bbb to your computer and use it in GitHub Desktop.
better error handling method for asynchronous javascript programming
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It's an awesome find/research. A lot must've been put into this. Cheers.