Search code examples
javascriptjsonmeteorhttp-gethttp-error

Why is my Meteor API call throwing an exception?


I've got the following code (based on pages 242 and 243 from "Meteor In Action") to try to call an external API:

The Meteor method:

Meteor.methods({
  . . .
  'getTextAddrAsEmailAddr': function(phone) {
    this.unblock;
    var apiUrl = 'http://www.xminder.com/number.check.php?number=' + phone;
    var response = Meteor.wrapAsync(apiCall) (apiUrl);
    return response;
  }
});

The "dedicated function":

var apiCall = function (apiUrl, callback) {
  try {
    var response = HTTP.get(apiUrl).data;
    callback(null, response);
  } catch (error) {
    if (error.response) {
      var errorCode = error.response.data.code;
      var errorMessage = error.response.data.message;
    } else {
      var errorCode = 500;
      var errorMessage = 'Cannot access the API';
    }
    var myError = new Meteor.Error(errorCode, errorMessage);
    callback(myError, null);
  }
}

How I'm calling it (one Meteor method calling another):

Meteor.methods({
  'insertPerson': function(firstname, lastname, streetaddr1, streetaddr2, placename, stateorprov, zipcode, emailaddr, phone, notes) {
    console.log('insertPerson reached'); // TODO: Remove before deploying
    check(firstname, String);
    . . .
    console.log('phone is ' + phone);
    var textAddrAsEmailAddr = Meteor.call("getTextAddrAsEmailAddr", phone, function(error, result) {
      console.log("textAddrAsEmailAddr is " + textAddrAsEmailAddr);
      console.log(result);
    })
    console.log('textAddrAsEmailAddr is ' + textAddrAsEmailAddr);
    . . .

The (Chrome) browser console output:

reached addPerson.submit form
methods.js:29 insertPerson reached
methods.js:41 phone is 2624908739
debug.js:41 Exception in callback of async function Error: Cannot access the API [500]
    at apiCall (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:30:19)
    at http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:195:23
    at Meteor.methods.getTextAddrAsEmailAddr (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:82:45)
    at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
    at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
    at _.extend.apply (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3902:54)
    at _.extend.call (http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3780:17)
    at Meteor.methods.insertPerson (http://localhost:3000/app/both/methods.js?55690625ac0aa28550112db1c63d2b8912cc3410:52:38)
    at http://localhost:3000/packages/ddp-client.js?250b63e6c919c5383a0511ee4efbf42bb70a650f:3911:25
    at _.extend.withValue (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:971:17)
methods.js:50 textAddrAsEmailAddr is undefined
methods.js:51 undefined
methods.js:53 textAddrAsEmailAddr is undefined

The command prompt output:

I20151024-07:23:43.999(-7)? insertPerson reached
I20151024-07:23:44.021(-7)? phone is 2624908739
I20151024-07:23:44.641(-7)? textAddrAsEmailAddr is undefined
I20151024-07:23:44.649(-7)? null
I20151024-07:23:44.649(-7)? textAddrAsEmailAddr is undefined

So the upshot is, the catch block of my dedicated functio is hit. Why? I can enter the ApiUrl (http://www.xminder.com/number.check.php?number=2624908739) directly into the browser and get the following result:

{"success":true,"data":{"number":"2624908739","status":"YES","carrier_name":"ATT Mobility","carrier_id":"","sms_address":"2624908739@txt.att.net","mms_address":"2624908739@mms.att.net"}}

I want the sms_address value, specifically; why is my attempt to access this response failing?

UPDATE

I changed the code to this:

var apiCall = function (apiUrl, callback) {
  try {
    var response = HTTP.get(apiUrl).data;
    callback(null, response);
  }
  // catch (error) {
  //   if (error.response) {
  //     var errorCode = error.response.data.code;
  //     var errorMessage = error.response.data.message;
  //   }
  //   else {
  //     // var errorCode = 500;
  //     var errorCode = error.response.code;
  //     // var errorMessage = 'Cannot access the API';
  //     var errorMessage = error.response.message;
  //   }
  //   var myError = new Meteor.Error(errorCode, errorMessage);
  //   callback(myError, null);
  // }
  catch (error) { console.log(error) };
}

...and get the same thing in the command prompt console:

I20151024-08:14:57.413(-7)? insertPerson reached
I20151024-08:14:57.434(-7)? phone is 2624908741
I20151024-08:14:58.248(-7)? textAddrAsEmailAddr is undefined
I20151024-08:14:58.255(-7)? null
I20151024-08:14:58.264(-7)? textAddrAsEmailAddr is undefined

...and this in the browser console:

reached addPerson.submit form
methods.js:25 insertPerson reached
methods.js:37 phone is 2624908741
methods.js:20 Error: Can't make a blocking HTTP call from the client; callback required.(…)
methods.js:46 textAddrAsEmailAddr is undefined
methods.js:47 undefined
methods.js:49 textAddrAsEmailAddr is undefined

NOTE: I get the same when changing the catch block to:

catch (error) {
  console.log(error)
  callback(error, null);
};

UPDATE 2

Trying to use this specific API may be a lost cause, anyway, because testing a bogus phone number in the browser emitted:

{"success":false,"error":"Too many requests during last 24h"}

UPDATE 3

I moved the Meteor methods from the \both\ folder to the \server\ folder, but apparently to little avail. I get only this in the browser console:

reached addPerson.submit form

...and this (still/again) in the command prompt console:

I20151024-13:02:23.539(-7)? insertPerson reached
I20151024-13:02:23.563(-7)? phone is 2624908743
I20151024-13:02:24.399(-7)? textAddrAsEmailAddr is undefined
I20151024-13:02:24.409(-7)? null
I20151024-13:02:24.410(-7)? textAddrAsEmailAddr is undefined

...but Jeroen Peeter's code differs from what I've got, and I don't know just how to incorporate it into my existing code; he says:

HTTP.get(apiUrl, function (error, data){
    console.log( 'http.get ::', error, data);
});

...and I've got:

var apiCall = function (apiUrl, callback) {
  try {
    var response = HTTP.get(apiUrl).data;
    callback(null, response);
  }
  catch (error) {
    if (error.response) {
      var errorCode = error.response.data.code;
      var errorMessage = error.response.data.message;
    }
    else {
      var errorCode = error.response.code;
      var errorMessage = error.response.message;
    }
    var myError = new Meteor.Error(errorCode, errorMessage);
    callback(myError, null);
  }

...so where does his code fit in with my (non-working) code?

UPDATE 4

After changing this:

var response = HTTP.get(apiUrl).data;

...to this:

var response = JSON.parse(HTTP.get(apiUrl).content;

...I get, in the command prompt console:

=> Errors prevented startup:

   While processing files with ecmascript (for target os.windows.x86_32):
   server/methods.js:4:54: server/methods.js: Unexpected token (4:54)

=> Your application has errors. Waiting for file change.

Line 4, char 54 is the final "t" in "content"...???


Solution

  • Something wrong with the way that you are using Meteor.wrapAsync to make an external API call.

    Here's how I would do it (in ecmascript2015)

    let wrappedGetTextAddrAsEmailAddr = Meteor.wrapAsync(function (phone, callback) {
      const apiUrl = `http://www.xminder.com/number.check.php?number=${phone}`;
    
      HTTP.get(apiUrl, callback);
    });
    
    Meteor.methods(
    {
      // Call like: Meteor.call('insertPerson', {firstname: 'Bob', ...});
      insertPerson(personData) 
      {
        let textAddrAsEmailAddr;
    
        console.log('insertPerson reached'); // TODO: Remove before deploying
        check(personData, Object);
        check(personData.firstname, String);
        . . .
        console.log(`phone is ${phone}`);
        textAddrAsEmailAddr = wrappedGetTextAddrAsEmailAddr(personData.phone);
        console.log(`textAddrAsEmailAddr is ${textAddrAsEmailAddr}`);
        . . .
      }
    });
    

    Make sure you put all the code above in server folder or just wrap them with if (Meteor.isServer) { ... }