I am trying to replicate a login form's behaviour through koa.
The login form does:
<form id="loginForm" method="post" action="http://myaddress:3000/auth" enctype="multipart/form-data">
I'm using the koa request and form-data modules:
var form = new FormData();
form.append('identification', 'userId');
form.append('password', 'userPassword');
var options = {
url: DB_SERVER_URL + 'auth',
method: 'POST',
formData: form
};
var response = yield request(options);
console.log('response.statusCode: ' + response.statusCode);
But I always get a 400 response.
I've tried just using form.submit(DB_SERVER_URL + 'auth', function(err, res) { ... } which works, but I like koa's yield functionality and ideally I want to avoid having to deal with callbacks.
Any ideas?
Koa accepts multiple yield inputs that can be obtained from your current code more or less easily depending on your current setup:
a promise. As form-data doesn't seem to use them, we'll create one with Q
var Q = require('q');
var promise = Q.ninvoke(form, "submit", DB_SERVER_URL + 'auth');
var response = yield promise;
console.log('response.statusCode: ' + response.statusCode);
or a thunk, a wrapper function as you used in your answer, but there are libraries that can handle the wrapping for you (here, thunkify-wrap):
var thunkify = require('thunkify-wrap');
var submit = thunkify(form.submit, form); // the context is needed in this case
var response = yield submit(DB_SERVER_URL + 'auth');
console.log('response.statusCode: ' + response.statusCode);
I ended up using form.submit(DB_SERVER_URL + 'auth', function(err, res) { ... }, but wrapped the callbacks so I could use yield to maintain a synchronous control flow.
Here's my wrapper for the callback to the form.submit to receive the response:
function makeLoginRequest(formData) {
var form = new FormData();
form.append('identification', formData.identification);
form.append('password', formData.password);
var DB_SERVER_URL = 'http://myurl:3000/';
return function(callback) {
form.submit(DB_SERVER_URL + 'auth', function(error, response) {
callback(error, response);
});
}
}
And here's my wrapper for the callback for receiving the response body:
function getLoginResponseData(response) {
return function(callback) {
response.on("data", function(chunk) {
callback(null, chunk);
});
}
}
This lets me use yield to maintain a synchronous control flow:
var response = yield makeLoginRequest(this.request.body);
console.log('response.statusCode: ' + response.statusCode);
var chunk = yield getLoginResponseData(response);
console.log("BODY: " + chunk);
I'm a node and koa beginner, so if you have a better way please let me know!
If you are using koa-request I was able to do this.
const request = require('koa-request');
const response = yield request({
method: 'POST',
url: 'https://whatsever.com',
form: {
itema: 'vala',
itemb: 'valb',
},
headers: {
'Content-type': 'application/x-www-form-urlencoded'
}
});
this.body = response.body;
If you need multipart look here: https://www.npmjs.com/package/request#multipartform-data-multipart-form-uploads.
Remember that koa-request wraps therequest module
Related
I'm building a front-end application that makes HTTP requests to 2 separate API's.
http://greetings_api:3000/getGreeting
data {'language': 'es'}
Response: 'Hola'
http://users_api:3000/getUser
data {'userid': 1}
Response: 'Jose Smith'
I have a single route that makes a request to these API's and then returns those responses:
var http = require('http');
var request = require('request-promise');
var greetingOptions = {
uri: 'http://greetings_api:3000/getGreeting',
hostname: 'greetings_api',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'language': 'es'
}
};
var greeting = {
getGreeting: function() {
return request(greetingOptions);
}
}
function myGreeting() {
return greeting.getGreeting();
};
var userOptions = {
uri: 'http://users_api:3000/getUser',
hostname: 'users_api',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'id': 1
}
};
var user = {
getUser: function() {
return request(userOptions);
}
}
function myUser() {
return user.getUser();
};
function getWelcome(req, res) {
// How do you store this....
myUser().then(function(result) {
console.log('result')
return result;
})
// ...and this...
myGreeting().then(function(result) {
console.log('Greet ' + result);
return result;
});
/// ...and then send them with this?
res.send(greeting + ' ' + user);
}
module.exports = { getWelcome };
So with the current code I get the correct output in the console. The problem is that I need to be able to send the response from the route with the combination of both API responses. What is the simplest way to accomplish this?
You are sending the response before the promises resolving.
with async/await we can write asynchronous code that looks and behaves like synchronous.
async function getWelcome(req, res) {
// we can wrap our operation in a try/catch block to handle
// both asynchronous and synchronous errors
try {
// with the await keyword we can wait for all promises to resolve
// before we continue with our code
/* if one of the promises inside Promise.all rejects we move to the catch block */
const [user, greeting] = await Promise.all([
myUser(),
myGreeting()
]);
// send the response if no errors
res.send(greeting + ' ' + user);
catch(e) {
res.status(404).send();
}
}
You need to make 2 parallel requests and send response after completing both. Here Promise.all function can help you.
Promise.all([myUser(), myGreeting()]).then(function(result) {
// result[0] - user
// result[1] - greeting
res.send(result[0] + ' ' + result[1]);
});
I am trying to call an external rest API from node server by using request node module.
let request = require('request');
var options = {
method: 'POST',
url: 'https://somerestURI:3000',
qs: { msg: 'some|data|for|other|server' }
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
If I try to run the above code, query string value is being encoded to
some%7cdata%7cfor%7cother%7cserver
as a result I am not receiving correct response.
But if I fire the same request in POSTMAN. I am receiving the expected output(I think postman is not encoding query string).
So what I want is don't encode the query string value.
Any help would be greatly appreciated.
As answered here, you can disable encoding in qsStringifyOptions
var options = {
method: 'POST',
url: 'https://somerestURI:3000',
qs: { msg: 'some|data|for|other|server' },
qsStringifyOptions: {
encoding: false
}
};
You can use node-rest-client package. It allows connecting to any REST API and get results as javascript Object.
var HttpClient = require('node-rest-client').Client;
var httpClient = new HttpClient();
// GET Call
httpClient.get("http://remote.site/rest/xml/method", function (data, response) {
// parsed response body as js object
console.log(data);
// raw response
console.log(response);
});)
or for POST Call
var args = {
data: { test: "hello" },
headers: { "Content-Type": "application/json" }
};
//POST Call
httpClient.post("http://remote.site/rest/xml/method", args, function (data, response) {
// parsed response body as js object
console.log(data);
// raw response
console.log(response);
});
I'm trying to make a chain of promises functions which use HTTP requests in NodeJS with Kraken framework.
My code could work in 90% of cases, but if the distant requested server takes time to respond, the code will return an error with undefined values. So I think Q is a good solution to prevent that.
Here's the situation :
We access to a URL with a "code" parameter -> the route controller takes this param to use it in a HTTP POST request -> the response (a token) is stored in a variable and used in an other HTTP GET request -> the response (multiple JSON objects) is stored in variable too -> all variables are stored in a MongoDB.
If functions are not used in this order, of course it fails.
var Q = require('q');
module.exports = function (router) {
router.get('/', function (req, res) {
var codein = req.param('code');
if(codein){
console.log('Provided code: ' + codein+'\n');
getAccessToken(codein).then(function(token){
console.log('Provided AccessToken: ' + token + '\n');
getUsername(token).then(function(userdata){
console.log('Provided Username: ' + JSON.parse(userdata).username + '\n');
storeData(userdata).then(function(msg){
console.log(msg);
res.redirect('/dashboard/' + JSON.parse(userdata).username);
});
});
});
}
else{
console.log('Access Denied, redirecting...');
res.redirect('/');
}
});
};
This method works, but actually didn't resolve the problem, because sometimes variable are undefined again. I think it's my request functions which aren't well made...
Here's an example of the first function with POST request :
var getAccessToken = function(cod){
var def = Q.defer();
var data = querystring.stringify({
client_id:"1234567890",
client_secret:"******",
grant_type:"authorization_code",
redirect_uri:"http://localhost:8000/r/callback",
code:cod
});
var options = {
host: 'domain.server.com',
port: 443,
path: '/api/oauth2/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(data)
}
};
var response = "";
var req = https.request(options, function(res) {
res.setEncoding('utf8');
res.on('data', function (chunk) {
response += chunk;
});
res.on('end', function(){
var json = JSON.parse(response);
var acto = json.access_token;
def.resolve(acto);
});
});
req.write(data);
req.end();
return def.promise;
};
In this case the acto variable can be undefined... So am I using Q in a wrong way ?
EDIT
To understand my problem, let me show you what can I have in my output console (really rare but happens) :
Provided code: 12345678910
Provided Username: user543210
Instead of :
Provided code: 12345678910
Provided AccessToken: 9876543210
Provided Username: user
I think you need to account for 2 scenarios
Where the Twitch API takes time to respond.
The Twitch response cannot be parsed
The code
res.on('end', function(){
var json = JSON.parse(response);
var acto = json.access_token;
def.resolve(acto);
});
Should be modified as:
try {
var json = JSON.parse(response);
var acto = json.access_token;
//check if acto is undefined
if (acto === undefined) {
def.reject('Some error message');
} else {
def.resolve(acto);
}
} catch (error) {
//since the JSON could not be parse
def.reject(error);
}
I am trying to Connect my application to foursquare and I want to display a message when a user checks in to certain places. I am trying to use their real time api https://developer.foursquare.com/overview/realtime
Everything works fine until the very end, ( when I have to send a reply post request https://developer.foursquare.com/docs/checkins/reply) I am using express and node.js. Here is what my post request looks like.
app.post('/handlepush', function(req, res) {
var checkin_id =req.param('checkin');
console.log(checkin_id);
var obj = JSON.parse(checkin_id);
var id = obj.id;
res.end('It worked!');
var token = "********************************";
var post_data = querystring.stringify({text : "awesome"});
var options = {
host: 'api.foursquare.com',
path: '/v2/checkins/' + id + '/reply?oauth_token=' + token,
port: 443,
method: 'POST'
};
var req2 = https.request(options, function(res2) {
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
req2.on('error', function(e) {
console.log('problem with request: ' + e.message);
});
});
req2.write(post_data);
req2.end();
});
this is the error I get, for some reason I am not able to add parameters for my post:
BODY: {"meta":{"code":400,"errorType":"other","errorDetail":"Must provide parameter text"},"response":{}}
You need to actually send your request. See: How to make an HTTP POST request in node.js?
var req2 = http.request(options, function(res2) {
res2.setEncoding('utf8');
res2.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req2.end();
I'm trying to implement https://developers.podio.com/doc/items/add-new-item-22362 Podio API addItem call in a nodejs module. Here is the code:
var _makeRequest = function(type, url, params, cb) {
var headers = {};
if(_isAuthenticated) {
headers.Authorization = 'OAuth2 ' + _access_token ;
}
console.log(url,params);
_request({method: type, url: url, json: true, form: params, headers: headers},function (error, response, body) {
if(!error && response.statusCode == 200) {
cb.call(this,body);
} else {
console.log('Error occured while launching a request to Podio: ' + error + '; body: ' + JSON.stringify (body));
}
});
}
exports.addItem = function(app_id, field_values, cb) {
_makeRequest('POST', _baseUrl + "/item/app/" + app_id + '/',{fields: {'title': 'fgdsfgdsf'}},function(response) {
cb.call(this,response);
});
It returns the following error:
{"error_propagate":false,"error_parameters":{},"error_detail":null,"error_description":"No matching operation could be found. No body was given.","error":"not_found"}
Only "title" attribute is required in the app - I checked that in Podio GUI. I also tried to remove trailing slash from the url where I post to, then a similar error occurs, but with the URL not found message in the error description.
I'm going to setup a proxy to catch a raw request, but maybe someone just sees the error in the code?
Any help is appreciated.
Nevermind on this, I found a solution. The thing is that addItem call was my first "real"-API method implementation with JSON parameters in the body. The former calls were authentication and getApp which is GET and doesn't have any parameters.
The problem is that Podio supports POST key-value pairs for authentication, but doesn't support this for all the calls, and I was trying to utilize single _makeRequest() method for all the calls, both auth and real-API ones.
Looks like I need to implement one for auth and one for all API calls.
Anyway, if someone needs a working proof of concept for addItem call on node, here it is (assuming you've got an auth token beforehand)
_request({method: 'POST', url: "https://api.podio.com/item/app/" + app_id + '/', headers: headers, body: JSON.stringify({fields: {'title': 'gdfgdsfgds'}})},function(error, response, body) {
console.log(body);
});
You should set content-type to application/json
send the body as stringfied json.
const getHeaders = async () => {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json; charset=utf-8',
};
const token = "YOUR APP TOKEN HERE";
headers.Authorization = `Bearer ${token}`;
return headers;
}
const createItem = async (data) => {
const uri = `https://api.podio.com/item/app/${APP_ID}/`;
const payload = {
fields: {
[data.FIELD_ID]: [data.FIELD_VALUE],
},
};
const response = await fetch(uri, {
method: 'POST',
headers: await getHeaders(),
body: JSON.stringify(payload),
});
const newItem = await response.json();
return newItem;
}