title: Firebase Cloud Functions - create pdf, store to bucket and send via mail
body: <p>I'm developing a Firebase Function, which is triggered when a new order is added to the Realtime Database. The first thing it does is to creat a pdf and pipe it to a google cloud storage bucket.</p>
  
  <p>On the .on("finish") event of the bucket stream, the next function gets started, which should send the piped pdf via email to the customer.</p>
  
  <p>Everything seems to work, at least a bit. </p>
  
  <p>First I had the problem, that the attached pdf always was empty. (Not just blank. I also opened it in notepad++ and it really was all empty). When I checked the doc and bucketFileSream vars inside the bucketFileStream.on("finished") function both had a length of 0. A check of the doc var directly after doc.end showed a length of somewhat 612.</p>
  
  <p>I then changed the flow, that in the sendOrderEmail function I also open a new Read Stream from the newly created File in the bucket.</p>
  
  <p>Now I get at least some stuff of the PDF in the attachement, but never the whole content.</p>
  
  <p>When I check the PDF uploaded to the bucket, it looks like it should.</p>
  
  <p>I googled alot and found some answers that were also targeting this topic, but as also seen in comments on these questions, they were not completly helpful.</p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/37232326/pdf-attachment-nodemailer">PDF Attachment NodeMailer</a></li>
  <li><a href="https://stackoverflow.com/questions/42378751/where-to-generate-a-pdf-of-firebase-database-data-mobile-app-or-firebase-host">Where to generate a PDF of Firebase Database data - mobile app, or Firebase Hosting web app</a></li>
  <li><a href="https://stackoverflow.com/questions/21934667/how-to-attach-file-to-an-email-with-nodemailer">How to attach file to an email with nodemailer</a></li>
  </ul>
  
  <p>I also checked with the nodemailer documentation how to pass the attachement correctly and implemented it as documented. But no success.</p>
  
  <p>I think that the mail gets sent before the Read Stream has finished.</p>
  
  <p>Here the Package Versions I use:</p>
  
  <ul>
  <li>"@google-cloud/storage": "1.5.2"</li>
  <li>"@types/pdfkit": "^0.7.35",</li>
  <li>"firebase-admin": "5.8.0",</li>
  <li>"firebase-functions": "^0.7.3"</li>
  <li>"nodemailer": "4.4.1",</li>
  </ul>
  
  <p>Can anyone tell me what I'm doing wrong or provide a working example, which uses current package versions, for this usecase?</p>
  
  <p>Here is the code which drives me crazy...</p>
  
  <pre><code>const functions = require("firebase-functions");
  const admin = require("firebase-admin");
  const nodemailer = require("nodemailer");
  const pdfkit = require("pdfkit");
  const storage = require("@google-cloud/storage")({projectId: `${PROJECT_ID}`})
  
  const mailTransport = nodemailer.createTransport({
    host: "smtp.office365.com",
    port: 587,
    secureConnection: false,
    auth: {
      user: "userName",
      pass: "userPassword"
   },
   tls: {
     ciphers: "SSLv3",
   }
  });
  
  exports.added = function(event) {
    const order = event.data.val();
    const userId = event.params.userId;
  
    // Load User Data by userId
    return admin
         .database()
         .ref("/users/" +userId)
         .once("value")
         .then(function (snapshot) {
                 return generateOrderPDF(snapshot.val(), userId);
         });
  };
  
  function generateOrderPDF(user, userId) {
    const doc = new pdfkit();
    const bucket = storage.bucket(functions.config().bucket);
    const filename =  `/${userId}/test-` + Date.now() + ".pdf";
    const file = bucket.file(filename);
    const bucketFileStream = file.createWriteStream();
  
    // Pipe its output to the bucket
    doc.pipe(bucketFileStream);
  
   // Do creation Stuff....
  
    doc.end();
  
    bucketFileStream.on("finish", function () {
      return sendOrderEmail(user, filename);
    });
  
    bucketFileStream.on("error", function(err) {
      console.error(err);
    });
  }
  
  function sendOrderEmail(user, filename) {
    const email = user.email;
    const firstname = user.firstName;
    const mailOptions = {
      from: "test@test.test",
      to: email,
      subject: "Order"
    };
  
    const bucket = storage.bucket(functions.config().bucket);
    const file = bucket.file(filename);
  
    mailOptions.html = mailTemplate;
    mailOptions.attachments =  [{
      filename: "test.pdf",
      content: file.createReadStream()
    }];
  
    return mailTransport.sendMail(mailOptions).then(() =&gt; {
      console.log("New order email sent to:", email);
    }).catch(error =&gt; {
      console.error(error);
    });
  }
  </code></pre>