meteorwebhooksiron-routermailgun

Webhook for Mailgun POST?


I am trying to store email messages as JSON (as parsed by Mailgun) in a Mongo.Collection through a Mailgun webhook. I set up an iron-router server-side route to handle the request, but this.request.body is empty. I am using Mailgun's "Send A Sample POST" to send the request, and the POST looks fine using e.g. http://requestb.in/. I was hoping that request.body would have the data, as mentioned in How do I access HTTP POST data from meteor?. What am I doing wrong?

Router.map(function () {
  this.route('insertMessage', {
    where: 'server',
    path: '/api/insert/message',
    action: function() {
      var req = this.request;
      var res = this.response;

      console.log(req.body);
      ...

Solution

  • I think the issue is that Mailgun sends a multipart POST request, e.g. it sends "fields" as well as "files" (i.e. attachments) and iron-router does not set up a body parser for multipart requests. This issue is discussed here and here on iron-router's Github Issues. I found this comment particularly helpful, and now I can parse Mailgun's sample POST properly.

    To get this working, in a new Meteor project, I did

    $ meteor add iron:router
    $ meteor add meteorhacks:npm
    

    In a root-level packages.json I have

    {
        "busboy": "0.2.9"
    }
    

    which, using the meteorhacks:npm package, makes the "busboy" npm package available for use on the server via Meteor.npmRequire.

    Finally, in a server/rest-api.js I have

    Router.route('/restful', {where: 'server'})
      .post(function () {
        var msg = this.request.body;
        console.log(msg);
        console.log(_.keys(msg));
        this.response.end('post request\n');
      });
    
    var Busboy = Meteor.npmRequire("Busboy");
    
    Router.onBeforeAction(function (req, res, next) {
      if (req.method === "POST") {
        var body = {}; // Store body fields and then pass them to request.
        var busboy = new Busboy({ headers: req.headers });
        busboy.on("field", function(fieldname, value) {
          body[fieldname] = value;
        });
        busboy.on("finish", function () {
          // Done parsing form
          req.body = body;
          next();
        });
        req.pipe(busboy);
      }
    });
    

    In this way I can ignore files (i.e., I don't have a busboy.on("file" part) and have a this.request.body available in my routes that has all the POST fields as JSON.