What Is The Best Node Js Express Shortcut, that will blow your mind?
Building web applications with nodejs and express has become the industry standard. Yet, so many developers still have to write complex code with lots of error-handling boilerplate code. I have found this simple solution, yet saving you hundreds of lines of boilerplate implementing small Express js middleware I am going to show you.
The problem
Every express developer has been facing the issue how to efficiently handle errors. I have seen countless times developers stick to this syntax:
if (notFound) {
res.send(404, {
message: 'Not found'
});
return;
}
Resolving http errors like this leads to lot of boilerplate and makes the code less readable. So how do we simplify it?
The solution
Firstly we need to make a strong foundation for handling errors. Code should provide simple extendable error resolution, even for unhandled exceptions thrown by poorly written code or calling some function on null. All the errors should have the same JSON structure for easy processing on the frontend in the web application.
The foundation can look like this:
class ApiError extends Error {
constructor(status, msg, code, detail) {
super(msg);
this.status = status;
this.code = code;
this.detail = detail;
}
}
function handle_error(err, res) {
let status = 500;
let error = {
error: 'Internal error',
};
// unified error returning interface for JSON endpoints
if (err instanceof ApiError) {
status = err.status;
error.error = err.message;
error.code = err.code;
error.detail = err.detail;
} else if (err instanceof Error) {
error.error = err.message;
console.log(err);
Logger.fatal(`Unhandled failure of server, ${res.req.method} ${res.req.originalUrl}:\n status code 500, error ${err.message} \n ${err?.stack}`);
}
// don't cache error responses
res.set('Cache-Control', 'no-store');
res.status(status);
res.json(error);
}
This code meets all the requirements mentioned above. Now for the fun part, we need to implement this for all endpoints in our application.
So we can move from this:
if (notFound) {
res.send(404, {
message: 'Not found'
});
return;
}
to this:
if (notFound) {
throw new ApiError(404, 'Not found');
}
For this purpose will create a middleware wrapper for express HTTP functions for easy implementation, even in later stages of development.
const FallThrough = Symbol('api-fall-through');
function async_json_middleware(fn) {
return function(req, res, next) {
try {
// some defaults for JSON endpoints
req.res.set('Cache-Control', 'no-store');
Promise.resolve(fn(req, res)).then(data => {
if (data === undefined) {
res.json(null);
} else if (data === FallThrough) {
next();
} else {
res.json(data);
}
}, err => handle_error(err, res));
} catch(err) {
return handle_error(err, res);
}
};
}
['get', 'post', 'put', 'delete', 'all'].forEach(function(m) {
express.application[m + '_json'] = function(path, fn) {
this[m](path, async_json_middleware(fn));
}
});
This express middleware brings us 2 important improvements. We can now return value from the endpoint function, and it will do the same as sending res.json with http status code 200. And secondly more importantly we can throw errors in our code. Significantly simplifying our codebase, eliminating the need for res.send and termination of our function with return.
app.get_json('/api/good-morning', async req => {
const now = new Date();
if (now.getHours() < 6) {
throw new ApiError(400, 'It's not morning, yet');
}
if (now.getHours() > 10) {
throw new ApiError(400, 'It's not morning anymore');
}
return 'Good morning';
});
Conclusion
The solution I have described above has helped me through the years making my node js express applications easier to write with less boilerplate. If thi stutorial has helped you do not mind leaving clap or check my other articles.