This is the third part of the Document Generator series, where we‘re plugging together the two distinct parts of our pipeline.
In the first part we took a look at automatically generating Word documents with pre-made templates. The second part investigated building PDF files from any Word document that can be automated.
Motivation
So, you might ask, why we‘re taking such a diversion from just plain out generating PDFs within our backend. I want to give some examples from my experience:
- PDF generation libraries are flexible but with their flexibility comes a lot of complexity that needs to be maintained, which can be very expensive
- while debugging might be easier, the visual appearance often comes second with PDF libraries
- no non-technical people can work on the templates, so the work on them has to be done by highly-skilled expensive workers
Thus the velocity of your team can be highly increased by using Word templates instead of manually building PDFs.
Plugging together
If we take a look at our result from the first post, we can see that the generate
returns a Readable. But we could instead wrap the part until we turn the Buffer into a Readable and call this function instead. As our convertWordToPdf
function also takes a Buffer, we can then pipe the Word result.
Let‘s revisit the API endpoint and change it a bit to return a PDF instead:
async function pdfFromTemplate(document: string, payload: any) {
const word = await generate(document, payload);
const pdf = await convertWordToPdf(word.buffer);
const readable = Readable.from(pdf);
return { pdf: readable, filename: word.filename };
}
router.get('/generate/:documentId', async (req, res) => {
try {
const generated = await pdfFromTemplate(req.params.documentId, req.query);
res.setHeader(
'Content-Disposition',
'attachment; filename="' + encodeURI(generated.filename) + '"'
);
res.setHeader(
'Content-Type',
'application/pdf'
);
return generated.pdf.pipe(res);
} catch (error) {
console.error(error);
if (error.name === 'ValidationError') {
res
.status(400)
.send({ message: 'validation failed', error: error.message });
} else {
res.status(500).send({ message: 'Could not generate documents.' });
}
}
});
Now, your backend returns sweet PDF files. You could of course also run both endpoints in parallel and offer your users the choice.