title: Streaming a PDF File to the Browser Using Dynamically Generated HTML in Node.js
body: <p>Okay, I see a few versions of this question out here, but I think mine goes a bit deeper than some of the others and I don't see satisfactory answers.</p>
  
  <p><strong>What I Want:</strong></p>
  
  <p>I have a page on a website that will have its contents frequently changed by users, and I want them to hit a button that says 'Generate PDF', and be able to turn the relevant information on the page into a nice looking PDF report.  </p>
  
  <p>I'm trying to do this with NodeJS, Express, with a deployment on Heroku.</p>
  
  <p>Generically, this is how I'm trying to accomplish this:</p>
  
  <p><strong>MY PLAN:</strong></p>
  
  <ol>
  <li>Initiate a GET Request</li>
  <li>Query a database for relevant results</li>
  <li>Load data into an EJS template</li>
  <li>Save the resulting HTML</li>
  <li>Use the npm module <a href="https://www.npmjs.com/package/html-pdf" rel="nofollow noreferrer">HTML-pdf</a> to stream it into the browser</li>
  </ol>
  
  <p>Other important details:</p>
  
  <ol>
  <li>This file is meant to be ephemeral.  I don't want to save it
  anywhere.</li>
  <li>I prefer not to even have to write it anywhere, since it's only
  meant to exist in the browser at that moment.</li>
  <li>This app is deployed on Heroku, and need to work within its
  constructs.  I prefer not to use a file store like S3.</li>
  </ol>
  
  <p><strong>MY CODE:</strong></p>
  
  <pre><code>router.get('/formulas/:id/pdf', function(req, res){
    var db = req.db.collection('users');
    var id = new ObjectID(req.params.id);
    var pointer = {"formulas.$": 1, "_id": 0};
  //query database, get the info out
  db.aggregate([
    {$match: {"formulas.f_id": id}},
    {$unwind: "$formulas"},
    {$match: {"formulas.f_id": id}},
    {$project : {"formulas": 1, "_id": 0}}
  ]).toArray(function(e, doc){
    if (e) {
    throw e;
  } else {
    var html = null;
    ejs.renderFile('./views/pdf.ejs', { //create template from db query
      project: doc[0].formulas,
      title: 'Formula Info Report',
      description:  'PDF Report For Your Formula by Nutraceutical Pro',
      ID: 'pdf',
      keywords: 'PDF, PDF generator, Formula Info Report',
      user: req.user,
      loggedIn: req.isAuthenticated()
    }, function(err, results){
      if (err) {
        console.log(err);
        }
        html = results; //save results of HTML output
      });
      var options = { format: 'Letter' };
      var path = './public/pdf/formula-' + req.params.id + '.pdf';
      pdf.create(html, options).toStream(function(err, stream) {//initiate stream
        if (err) {
          return console.log(err);
        }
        if (stream) {
          console.log(stream);
          stream.pipe(fs.createWriteStream(path));
          console.log("the pdf was streamed.");
          res.end();
          }
        });
      }
    });
  });
  </code></pre>
  
  <p>I initiate the <code>GET</code> request with an AJAX call like so:</p>
  
  <pre><code> $('.getPDF').click(function(){
     var filepath = 'https://my-domain.herokuapp.com/pdf/formula-' + this.id + '.pdf';
   $.ajax({
     url: '/formulas/'+ this.id +'/pdf',
     type: 'GET',
     success: function () {window.open(filepath);}
   });
  </code></pre>
  
  <p>When I run the script I generate a stream, and the console logs that the PDF was created.  If I just save the resulting file to my local system it comes out fine, but in my app I get the following error message:</p>
  
  <pre><code>Cannot GET /pdf/formula-59cc38992fb99b00045832ad.pdf
  </code></pre>
  
  <p>So it's like the raw material for the PDF is being created, but it's not being logged and created in such a way that allows the app to read it.  </p>
  
  <p>I'm open to a variety of solutions for this, so if there are much easier ways to do it I'm all ears.  Prefer to stick with my current stack though.</p>