Search code examples
node.jsexpresspdfkit

how to download pdf with nodejs and expressjs


I'm using this GitHub repo to generate pdf invoices. code looks something like this :

let myInvoice = new MicroInvoice({
  ... pdf content obj goes here
});

  const file = await myInvoice.generate("example.pdf");
  const fileType = "application/pdf";

  res.writeHead(200, {
    "Content-Disposition": `attachment;"`,
    "Content-Type": fileType,
  });

  const download = Buffer.from(file, "base64");
  res.end(download);

But this code gives the following Error: could not handle the request. What could be the issue here?

EDIT : Generate pdf function looks like something like this :

 generate(output) {
    let _stream    = null;

    this.document = new PDFDocument({
      size : "A4"
    });

    this.loadCustomFonts();
    this.generateHeader();
    this.generateDetails("customer");
    this.generateDetails("seller");
    this.generateParts();
    this.generateLegal();

    if (typeof output === "string" || (output || {}).type === "file") {
      let _path = "";

      if (typeof output === "string") {
        _path = output;
      } else {
        _path = output.path;
      }

      _stream = fs.createWriteStream(output);

      this.document.pipe(_stream);
      this.document.end();
    } else {
      this.document.end();
      return this.document;
    }

    return new Promise((resolve, reject) => {
      this.document.on("end", () => {
        return resolve();
      });

      this.document.on("error", () => {
        return reject();
      });
    });
  }

Solution

  • Looking at the code of the library, the .generate() method doesn't return anything, so your file variable will be undefined (I also have no idea what the Buffer.from() is doing…).

    Instead, you can use res.attachment() and res.sendFile():

    await myInvoice.generate("example.pdf");
    res.attachment().sendFile('example.pdf', { root : __dirname });
    

    Be aware that every request will overwrite the file, which may not be what you want.

    EDIT: looking closer, you can make generate() return the document by not passing any arguments to it. In that case, you can prevent a local file from being written:

    const doc = myInvoice.generate();
    
    doc.pipe( res.attachment('invoice.pdf') );
    

    (untested)