I'm fairly new to Marko and I'm having some difficulties handling errors gracefully when rendering templates (on the server). Initially I structured my app to pipe the render stream straight to the response:
const marko = require('marko')
const template = marko.load(filename, {
buffer: false
})
return (req, res) => {
...
return template.render(data, res)
})
But when the rendering process errors (E.G. attempting to access an undefined property) this results in an unfinished response.
Preferably I'd like to catch the error, log it and fail more gracefully, even it means taking a small perf hit:
// with callback
return (req, res) => {
...
return template.render(data, (err, data) => {
if (err) {
log(err)
res.send(500).end()
} else {
res.send(data)
}
})
})
// or with streams
return (req, res) => {
...
return template.stream(data)
stream.on('data', data => { output.push(data) })
stream.on('error', err => { errors.push(err) })
stream.on('end', () => {
if (errors.length) {
log.apply(null, errors)
res.send(500).end()
} else {
res.send(output.join(''))
}
})
return stream
})
However neither the streaming nor callback interface are currently able to catch such an error, with the effect of potentially crashing the process.
Are you able to recommend a strategy whereby templates can fail more gracefully at render time?
There are no try...catch blocks in Marko (for performance reasons, but also because the Marko runtime wouldn't know how to best handle the error). A common strategy for error handling in Node.js is to gracefully close all connections when an uncaught error occurs and to shutdown the process. The following is a good read: https://www.joyent.com/node-js/production/design/errors
However, another good alternative is to use promises. Promise implementations use an implicit try...catch so you can do the following to catch any errors when rendering the initial HTML that is rendered synchronously:
function controller(req, res) {
new Promise(function(resolve, reject) {
template.render({name: 'Frank'}, res);
})
.catch(function(err) {
res.end();
// Handle the error...
console.log('Templating rendering failed! ', err);
});
}
In addition, if you are using the <await(...)> you can provide a promise as the data provider to handle the errors when rendering portions of your template asynchronously.
Also, we plan on adding a promise API method soon: https://github.com/marko-js/marko/issues/251
Does that answer your question?
Thank you for your fast response @patrick-steele-idem - I actually read the article you linked to before posting my question, too 馃榿
I'd also tried promises (for the implicit try/catch) but I'd made the mistake of _returning__ the result of the render method - thanks to your example I'll continue with that path.
A promise based API sounds like a very good idea to me 馃憤