node.js wait for file operation to complete

I am brand new at node.js and I need to write a web server that takes data and write it to a file. that file is then used in an external program.

my problem is that node.js executes the external program before the data is completely written to the file. How can I wait for it to complete before executing the external program

var http = require('http')
var map = require('through2-map')
var fs = require('fs')
var str = ""
var sh = require('execSync');
var tmp_filename = ""

    function random (low, high) {
    return Math.random() * (high - low) + low;
}

function execute_program(){
  var command = 'ruby ' + __dirname +  '/check_content.rb --file '+ tmp_filename
  var result = sh.exec(command);
  console.log('return code ' + result.code);
  console.log('stdout + stderr ' + result.stdout);
  return result.stdout
}

function write_to_file (chunk){
  str = chunk.toString();
  fs.appendFile(tmp_filename, str, function (err){
      if (err) throw err;
      console.log('The "data to append" was appended to file!');    
    }
  ) 
}

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    message = req.pipe(map(function (chunk, res) {
      write_to_file(chunk);
    }))
    var out = execute_program();
    res.write(out)
    message.pipe(res)
  }
)

server.listen(8000)

Note: I'm not sure what you're attempting with res.write(out) and then message.pipe(res). That will write the output from your ruby script to the response and then it looks like you're piping the request body in after that. As it is, the message.pipe(res) line won't work because you'll be trying to pipe from a writable stream, which you can't do. For the purpose of this answer I'm going to assume you don't want the message.pipe(res) line; please correct me if I'm wrong.

Simple / Quick Answer

The simplest answer is to listen for the end event on req and then execute your program, etc. when that event fires:

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    req.on('end', function() {
        var out = execute_program();
        res.write(out);
        res.end();
    });
    req.pipe(map(function (chunk, res) {
        write_to_file(chunk);
    }));
  }
)

Optimization: fs.createWriteStream()

You could also use fs.createWriteStream() instead of appending chunks with fs.appendFile(). That would look like:

var server = http.createServer( function(req, res){
    tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
    res.writeHead(200, {'Content-Type': 'text/plain'});
    req.on('end', function() {
        var out = execute_program();
        res.write(out);
        res.end();
    });
    req.pipe(fs.createWriteStream(tmp_filename));
  }
)

With that in place, you can remove your entire write_to_file function.

That will also prevent a problem you might have if you happen to get the same random number for your temp file; with your current code, you'll append to the existing file, which is probably not what you want. If for some reason you do want to append to the file in that case, you can just pass the a flag (for append) to fs.createWriteStream: fs.createWriteStream(tmp_filename, {flags: 'a'});.

Thanks Mike S. The message.pipe(res) and res.write(out) is redundant...but i did some research and used async.waterfall package and the following worked for me. but will take your suggestion for optimization

var http = require('http');
var map = require('through2-map');
var fs = require('fs');
var str = "";
var sh = require('execSync');
var async = require('async');
var tmp_filename = "";

function random (low, high) {
     return Math.random() * (high - low) + low;
}

function write_to_file (chunk){
  str = chunk.toString();
  fs.appendFile(tmp_filename, str, function (err){
      if (err) throw err;
      console.log('The "data to append" was appended to file!');    
    }
  ) 
}

function execute_program(){
  var command = 'ruby ' + __dirname +  '/check_content --file ' +  tmp_filename
  var result = sh.exec(command);
  console.log('return code ' + result.code);
  console.log('stdout + stderr ' + result.stdout);
  return result.stdout
}

async.waterfall([
    function(callback){
      var server = http.createServer(function(req, res){
          callback(null, req, res)
        }
      ).listen(8000);      
    },
    function(req, res, callback){
      tmp_filename = '/tmp/tmp_template_' + random(1000, 99999) + '.template'
      var message = req.pipe(map(function (chunk) {
        write_to_file(chunk);
      }))
      setTimeout(function () {
        callback(null, req, res, message);
      }, 666);
    },
    function(req, res, message, callback){
      var out = execute_program()
      res.write(out)
      message.pipe(res)
      callback(null, 'done');
    }
  ])