Search code examples
javascriptnode.jsfastifypdfmake

How to use pdfmake with fastify?


PDFMake has this method for streaming a generated PDF document :

const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
pdfDoc.pipe(fs.createWriteStream('document.pdf'));  // send to output stream
pdfDoc.end();

And fastify has this to stream things :

fastify.get('/streams', function (request, reply) {
  const fs = require('fs')
  const stream = fs.createReadStream('some-file', 'utf8')
  reply.header('Content-Type', 'application/octet-stream')
  reply.send(stream)    // send from input stream
})

How are those two working together? Preferably, PDFMake should directly stream through fastify and not buffer the entire PDF before sending it.

For reference, with Koa (or Express), I would simply do :

app.use(async (ctx, next) => {
  const pdfDoc = printer.createPdfKitDocument(docDefinition, options);

  res.setHeader('content-type', 'application/pdf');
  res.setHeader('content-disposition', req.headers['content-disposition'] || 'inline');
  res.status(200);

  pdfDoc.pipe(res);
  pdfDoc.end(); 
});

However, I cannot do reply.send(pdfDoc).


Update

Since PDFKit's PDFKit.PDFDocument type seems to be a readable stream, I have tried

fastify.get('/streams', function (request, reply) {
  const pdfDoc = printer.createPdfKitDocument(docDefinition, options);

  reply.header('Content-Type', 'application/pdf');
  reply.header('content-disposition', 'inline');
  reply.send(pdfDoc);   // "Failed to load PDF document"
})

I also tried PDFKit's solution of using blob-stream :

fastify.get('/streams', function (request, reply) {
  const pdfDoc = printer.createPdfKitDocument(docDefinition, options);
  const stream = pdfDoc.pipe(blobStream());

  reply.header('Content-Type', 'application/pdf');
  reply.header('content-disposition', 'inline');
  reply.send(stream);   // "Failed to load PDF document"

  pdfDoc.end();
})

There are no errors except in the browser.


Solution

  • This is my current solution :

    fastify.get('/pdf', async (request, reply) => {
      const pdfDoc = printer.createPdfKitDocument(...getDocumentDefinition(request));
    
      const buffer = await new Promise((resolve, reject) => {
        const chunks = [];
        const stream = new Writable({
           write: (chunk, _, next) => {
              chunks.push(chunk);
              next();
           }
        });
        stream.once('error', (err) => reject(err));
        stream.once('close', () => resolve(Buffer.concat(chunks)));
    
        pdfDoc.pipe(stream);
        pdfDoc.end();
      });
    
      reply.type('application/pdf').code(200).send(buffer);
    })