A number of our systems running Express/Node.js have implemented a /healthcheck
endpoint that our load balancers and other monitoring systems can watch to detect when one of our applications is in trouble. Basically the endpoint checks connections to all downstream dependencies the app has, including MongoDB.
This issue is that when the /healthcheck
endpoint tries to run its test transaction and it fails, there's no obvious way to catch the error in the endpoint so that I can have it report that the MongoDB dependency has an issue. Instead the server throws a 500 error for the /healthcheck
request (while still detectable as an issue, is not as helpful).
This code shows basically what we're doing. It's not complete and runnable, but Express users with Mongoose experience should recognize it easily.
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const HealthcheckSchema = new Schema({
widget: {
type: String,
required: true,
unique: false,
},
createdAt: Date,
})
const HealthCheck = mongoose.model('Healthcheck', HealthcheckSchema, 'healthcheck')
const mongoCheck = () => Healthcheck.remove({}).exec()
.then(() => Healthcheck.create({widget: 'xyzpdq'}))
.then((results) => Healthcheck.findOne({widget: results.widget}).exec())
.then(() => true)
.catch(() => false)
const healthCheckEndpoint = (req, res) => {
let status = {}
Promise.all([mongoCheck()]) // normally there are other checks as well
.then(function (values) {
status.mongoUp = values[0]
return res.status(200).json(status) // always return 200; the payload gives the health status, not the HTTP status code
})
}
app.get('/healthcheck', healthCheckEndpoint)
In the main setup for the application, not specific to the healthcheck, we use mongoose.connect()
with a callback. We have event handlers for various states like disconnected
and error
and such, and those events catch, but that doesn't help get the proper status in the /healthcheck
endpoint.
I imagine I could use those event handlers to set state that the /healthcheck could pick up upon and return that, but I'd really prefer to make an actual query and use its success/failure as the result.
This all turned out to be a red herring. The issue here was that there was an Express middleware layer that was making use of Mongo before the /healthcheck
endpoint was reached.
In this case it was a Mongo session manager.
The solution was to exclude the /healthcheck
URL from the session middleware.
function excludeSessionsForStaticURLs (req, res, next) {
if (!new RegExp(/\/about|\/api\/heartbeat|\/healthcheck|\.css|\.js|\.jpg|\.png'/)
.test(req.originalUrl)) {
return sessionMiddleware(req, res, next)
}
return next()
}
app.use(excludeSessionsForStaticURLs)
So now that this exclusion has been put in place, the normal try/catch (async/await) and .catch() for Promises works and the status for Mongo is reflected as False when appropriate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With