Start a new topic

Working Example of The Request Module Using "POST"

I'm trying to use "request" to actually generate a POST to a custom end point per this discussion : http://support.kinvey.com/hc/communities/public/questions/200358037-Accessing-Endpoint-from-Collection-Hook



Unfortunately, I can never get a POST to actually send. I keep getting errors that make no sense. I've gone through the https://github.com/mikeal/request docs as well. What I am doing seems correct. The Kinvey docs show a few examples of a GET using the request module, but no POSTs. Can you "post" an example?



If I use "url", I get this error:



"TypeError: Parameter 'url' must be a string, not undefined"



If I use "uri", I get this error:



"TypeError: Cannot call method 'split' of undefined"



Here is a sample of the code:



req.request(

{

url: urlString,

method: 'post',

auth: authorization,

body: { "mobile": request.body.mobile }

}, function( validateError, validateResponse, validateBody ) {



logger.info('ValidateError=');

logger.info(validateError);

logger.info('validateResponse=');

logger.info(validateResponse);

logger.info('validateBody=');

logger.info(validateBody);



if( validateError ) {

response.body = { "error" : "Failed to validate mobile number."};

response.complete(434);

} else {

var validationDetails = JSON.parse( validateBody );



if( validationDetails.valid !== true ) {

validationResults.push( {"attr" : "mobile", "valid" : false, "msg": ["Invalid mobile number."] } );

}



if( callback ) {

callback('validateMobile');

}



}

})



urlString is a string that looks like this with API key changed :



/rpc/MY_API_KEY/custom/ValidatePhoneNumber



authorization is what I extracted from the original onPreSave request per suggestion (again edited to hide details)



Kinvey d6e11887-XXXX-46ce-XXXX-26d1XXXX7f57.8C/XXXXJe/YtXXXX5V+XXXXC3ocXXXXJ1KsXXXXd61I=




With the errors you're getting, I don't think you've shared the relevant parts of your code. How are you defining urlString? Is it just a constant? Are you using URI builder? Is it a function? Is this in Node.js? If so it could be executing your code out of order, calling this function before urlString is defined.



Also, request follows this format:



request('url string', { options object}, responseCallback(response) {});



I'm not sure the url is ever included in the options.
Jeremy,



Thanks for responding. My example code above is after trying many iterations. The urlString is clearly explained in my post.



The request format in the Kinvey docs show that the URL can be included as part of the options. http://devcenter.kinvey.com/rest/reference/business-logic/reference.html#request-module



The Node.js 'request' package that Kinvey's 'request' is based on also shows that as an option. https://github.com/mikeal/request#request----simplified-http-request-method



No, it is not being called out of order. This is all in a Kinvey onPreSave collection hook.
I have SOME progress on this. I CAN succesfully make a POST to a Kinvey custom End-Point from my own custom Node.js script running on my localhost using the Node 'request' library. The request function looks like this:



var req = require('request');



req({

url: 'https://baas.kinvey.com/rpc/APP_KEY_HERE/custom/ValidatePhoneNumber',

method: 'POST',

json: {"mobile" : "5555551212", "cc": "US"},

auth: {

user: 'APP_KEY_HERE',

pass: 'APP_KEY_HERE',

sendImmediately: true

}

}, function ( err, res, bod) {

console.log('Got through!');

console.log(err);

console.log(res);

console.log(bod);

});



Now, I convert the WORKING code from above to the correct Kinvey style code using "modules" instead of "require" and integrate it with the rest of my onPreSave validation process.



/**

* Confirm a mobile number is valid.

*

* Can be blank.

*/

var validateMobile = function( callback ) {



if( request.body.mobile !== '' ) {



logger.info('In validate mobile: ' + request.body.mobile);



var req = modules.request;



req({

url: 'https://baas.kinvey.com/rpc/APP_KEY_HERE/custom/ValidatePhoneNumber',

method: 'POST',

json: {"mobile" : "555-555-1212", "cc": "US"},

auth: {

user: 'APP_KEY_HERE',

pass: 'APP_SECRET_HERE',

sendImmediately: true

}

}, function ( err, res, bod) {



if( err ) {

response.body.debug = {"error" : "Failed to validate mobile number"};

response.complete(400);

return;

}

logger.info('Got through!');

logger.info(err);

logger.info(res);

logger.info(bod);



if( callback ) {

callback('validateMobile');

}



});



} else {



// Nothing to check so let it know all done.

if( callback ) {

callback('validateMobile');

}



}



};





This is the response I get.



{

"error": "BLRuntimeError",

"description": "The Business Logic script has a runtime error. See debug message for details.",

"debug": "TypeError: object is not a function"

}



Clearly something needs to be different between the REAL 'request' library and the 'request' module that Kinvey is using.



Anyone have an idea what it might be?
Here is a working example. Sorry, I completely agree that the error messages are unhelpful. We will do better.



Endpoint foo:



function onRequest(request, response, modules){

response.body = {'hello':'from endpoint'};

response.complete();

}



Hook:



function onPreFetch(request, response, modules){

var endpointName = 'foo';

var uriString = 'https://baas.kinvey.com/rpc/' +

modules.backendContext.getAppKey() + '/custom/'+endpointName;

var opts = {

uri: uriString,

method: 'post',

headers: {

'Authorization': request.headers.authorization

},

json:true,

body: { "mobile": request.body.mobile }

};

modules.logger.info(opts);

modules.request.request(opts, function( err, resp, body ) {

if (err) {

response.body = err;

} else {

response.body = body;

}

response.complete();

});

}
SOLVED ! Here is the working solution.



NOTE : You MUST use "uri" instead of "url". Although the Node 'request' library shows several examples using 'url', the Kinvey docs only show 'uri'. So, stick with the Kinvey docs. Also, you SHOULD be able to use "req.post" or "req.get" and not have to enter the method in the options object. However, I was never able to get anything other than "req.request" to work. YMMV.



/**

* Confirm a mobile number is valid based on the mobile number and cc (country code) in the request.

*/

var validateMobile = function( callback ) {



if( request.body.mobile !== '' ) {



var req = modules.request;



req.request({

uri: 'https://baas.kinvey.com/rpc/APP_KEY_HERE/custom/ValidatePhoneNumber',

method: 'POST',

json: {"mobile" : request.body.mobile, "cc": request.body.cc},

auth: {

user: 'APP_KEY_HERE',

pass: 'MASTER_SECRET_HERE',

sendImmediately: true

}

}, function ( validateErr, validateResponse, validateBody) {



if( validateErr ) {

response.body.debug = {"error" : "Failed to validate mobile number"};

response.complete(400);

return;

}



if( validateBody.valid !== true ) {

validationResults.push( {"attr" : "mobile", "valid" : false, "msg": ["Mobile number is not valid"] } );

} else {

request.body.mobile = validateBody.e164;

}



if( callback ) {

callback('validateMobile');

}



});



} else {



// Nothing to check so let it know all done.

if( callback ) {

callback('validateMobile');

}



}



};



SOLVED ! Here is the working solution.



NOTE : You MUST use "uri" instead of "url". Although the Node 'request' library shows several examples using 'url', the Kinvey docs only show 'uri'. So, stick with the Kinvey docs. Also, you SHOULD be able to use "req.post" or "req.get" and not have to enter the method in the options object. However, I was never able to get anything other than "req.request" to work. YMMV.



/**

* Confirm a mobile number is valid based on the mobile number and cc (country code) in the request.

*/

var validateMobile = function( callback ) {



if( request.body.mobile !== '' ) {



var req = modules.request;



req.request({

uri: 'https://baas.kinvey.com/rpc/APP_KEY_HERE/custom/ValidatePhoneNumber',

method: 'POST',

json: {"mobile" : request.body.mobile, "cc": request.body.cc},

auth: {

user: 'APP_KEY_HERE',

pass: 'MASTER_SECRET_HERE',

sendImmediately: true

}

}, function ( validateErr, validateResponse, validateBody) {



if( validateErr ) {

response.body.debug = {"error" : "Failed to validate mobile number"};

response.complete(400);

return;

}



if( validateBody.valid !== true ) {

validationResults.push( {"attr" : "mobile", "valid" : false, "msg": ["Mobile number is not valid"] } );

} else {

request.body.mobile = validateBody.e164;

}



if( callback ) {

callback('validateMobile');

}



});



} else {



// Nothing to check so let it know all done.

if( callback ) {

callback('validateMobile');

}



}



};

Ivan,



Wow. You always answer while I'm posting MY answer. I managed to get this to work. See my "SOLVED" answer.



Y'all might want to do some testing on "req.post" and "req.get". I could only ever get "req.request" to work.



Thanks for taking the time though. I appreciate it.
Hi! I call a custom end point from a postSave like this:



```req.post({

uri: 'https://' + request.headers.host +'/rpc/'+context.getAppKey()+'/custom/generateAppointment',

headers: {'Authorization': request.headers.authorization/*'Basic '+encode(header)*/},

json:

{

"code": 1,

"entity": request.body,

"date" : request.body.chargeAt,

"accomplished" : request.body.payment != null && request.body.payment != undefined && request.body.payment != "" ? true : false

}

}, function(error, resp, body) {

if (error){

logger.info('error');

logger.error(error);

response.body = error;

return response.complete(400);

} else {

return response.complete(201);

}

});```



And work, very well.

1 person likes this
Login or Signup to post a comment