javascriptnode.jsnewslettermailchimp-api-v3.0

Why can't I redirect to my failure page using node.js?


I am working on my newsletter project using Mailchimp API, it works but I can't redirect to a failure page if the status code is not 200, the browser says localhost refused to connect meanwhile success page redirection works great. here's my code:

app.post("/", async(req, res, next)=>{
  const {fName, lName, email} = req.body.user;
  const response = await mailchimp.lists.addListMember("List_ID", {
    email_address: email,
    status: "subscribed",
    merge_fields:{
      FNAME: fName,
      LNAME: lName,
    },
  });
  if(res.statusCode === 200){
    res.sendFile(__dirname + "/success.html");
  }
  else{
    res.sendFile(__dirname + "/failure.html");
  }

});

Solution

  • First of all using async with express is tricky, if anything in your async function throws express won't get notified about that which would result in an error like:

    localhost refused to connect

    A quick and dirty solution would be to warp your whole body of the async function into a try .. catch block. But that's tedious and you might still miss some side conditions.

    So instead you would want to create a wrapper for that which could look that way: (There are probably better implementations for an express async wrapper)

    function asyncExpressWrapper(fn) {
       // return the wrapping middelware be used.
       return async (req, res, next) => {
          try {
             // call the actual middelware you passed
             await fn(req, res, next);
          } catch (err) {
             // will catch any error thrown in fn
             next(err)
          }
       }
    }
    

    And use it that way:

    app.post("/", asyncExpressWrapper(async(req, res, next) => {
      const {
        fName,
        lName,
        email
      } = req.body.user;
    
      const response = await mailchimp.lists.addListMember("List_ID", {
        email_address: email,
        status: "subscribed",
        merge_fields: {
          FNAME: fName,
          LNAME: lName,
        },
      });
    
      if (response.statusCode === 200) {
        res.sendFile(__dirname + "/success.html");
      } else {
        res.sendFile(__dirname + "/failure.html");
      }
    }));
    

    Now that will solve the localhost refused to connect problem, and express will tell you which error you didn't handle.

    Besides that your res.statusCode === 200 will check the express response statusCode which at that point is always true, if response has a statusCode it has to be response.statusCode === 200.

    Now your __dirname + "/failure.html" likely still is not shown, as the Promise returned by mailchimp.lists.addListMember likely rejects in the error case. So you needd also a try ... catch in your middleware:

    app.post("/", asyncExpressWrapper(async(req, res, next) => {
      const {
        fName,
        lName,
        email
      } = req.body.user;
      
      try {
        const response = await mailchimp.lists.addListMember("List_ID", {
          email_address: email,
          status: "subscribed",
          merge_fields: {
            FNAME: fName,
            LNAME: lName,
          },
        });
    
        // you probably don't need that if-else at all and only emit 
        // success here, but I don't know mailchimp.lists.addListMember 
        // so that's up to you to figure out. 
        if (response.statusCode === 200) {
          res.sendFile(__dirname + "/success.html");
        } else {
          res.sendFile(__dirname + "/failure.html");
        }
      } catch(err) {
        res.sendFile(__dirname + "/failure.html");
      }
    }));
    

    The Express documentation has a section about Promises suggesting this as a wrapper:

    const wrap = fn => (...args) => fn(...args).catch(args[2])
    

    The one I suggested also works if you pass a none async function to it from which you accidentally don't return a Promise.