In a previous article, I discussed how I used Express.js middleware to reduce the repetition in my code.
Previously, I had a serious repetition problem with the Node.js CMS that I built for my clients. Middleware made that easier where, for most of the routes, I only needed to call the middleware then render the page, like this:
JavaScript
router.get('/about', alerts, static, (req, res) => {
res.render('about', req.args)
})
For the '/about' route, I'd first call the alerts middleware that would check the database for any existing front-end alerts. Then, I'd call the static middleware to get relevant page and metadata from the database for the route. Finally, I'd render the page with the collected arguments.
Hey, Tyler here. I'm currently working on some great web development and digital marketing products and services over at ZeroToDigital.
If that sounds interesting to you, please check it out!
This proved to be way better than before where I'd basically copy/paste the database calls and data formatting for each route. However, I was still left with a lot of these:
JavaScript
router.get('/about', alerts, static, (req, res) => {
res.render('about', req.args)
})
router.get('/contact', alerts, static, (req, res) => {
res.render('contact', req.args)
})
router.get('/faqs', alerts, static, (req, res) => {
res.render('faqs', req.args)
})
Not terrible, but still repeating code, so I found 2 ways to make it even better.
In theExpress.js docs, a path can be a string, a path, a regular expression, or an array of the above. So we can combine our 'static' routes into one, singular definition that will replace all 3 above:
JavaScript
var staticRoutes = ['/about', '/contact', '/faqs']
router.get(staticRoutes, alerts, static, (req, res) => {
res.render(req.originalUrl.substr(1), req.args)
})
The only thing we changed was adding the routes to an array, calling the array inside the router function and then using the request value's originalUrl along with JavaScript's native substr function to cut off the leading '/'. Quite simply, I'm telling Express to render the file names 'about', 'contact' or 'faqs' depending on the route.
Instead of having multiple middleware functions, I put everything inside of one JavaScript closure.
JavaScript
function staticRender() {
return function(req,res) {
const sql0 = `SELECT * FROM meta WHERE url = '${req.originalUrl}'`
const sql1 = `SELECT * FROM pages WHERE url = '${req.originalUrl}'`
const sql2 = `SELECT * FROM alerts WHERE published = 'on'`
db.pool.query(`${sql0};${sql1};${sql2}`, function (error, results) {
// Do stuff with results
// Render page
req.args = { meta: results[0][0], alerts: results[2][0] ... }
res.render(req.originalUrl.substr(1), req.args)
})
}
}
The closure would handle the req and res parameters, the database calls and formatting, and the rendering of the page depending on the originalUrl value for the requested route.
Now that we have our closure, we can make our route definition even shorter:
JavaScript
var staticRoutes = ['/about', '/contact', '/faqs']
router.get(staticRoutes, staticRender())
Note: for sake of clarity I left out such things as error handling. If you use this post for reference, don't forget to add it in!