How to handle async request at client nodejs? - javascript

I am new to NodeJS. I am using NodeJS on server side. I am trying to make a simple REST based GET request which takes search parameter searchvar from user and returns the matching records found in a JSON array. My server code is as follows:
var express = require('express');
var app = express();
var MongoClient = require('mongodb').MongoClient;
var assert = require('assert');
var ObjectId = require('mongodb').ObjectID;
var url = 'mongodb://localhost:27017/mydb';
app.get('/search', function(req, res) {
res.set({ 'content-type': 'application/json; charset=utf-8' });
res.header("Content-Type", "application/json; charset=utf-8");
res.charset = 'utf-8';
var searchvar = req.query.searchvar;
if(searchvar != null) {
var recordsArray = new Array();
console.log("searchvar :: " + searchvar );
MongoClient.connect(url, function(err, db) {
var cursor = db.collection('myCollections').find({"name": new RegExp(searchvar)});
cursor.each(function(err, doc) {
//assert.equal(err, null);
if (doc != null) {
var tempObject = {"name": doc.name, "cat": doc.cat};
recordsArray.push(tempObject);
console.dir(doc);
} else {
db.close();
}
});
if(recordsArray.length > 0) {
console.log("if........");
res.json({"status": "SUCCESS", "message": "The request processed successfully.", "records":recordsArray});
} else {
console.log("else........");
res.json({"status": "FAILURE", "message": "No records found corresponding to given search query."});
}
});
} else {
res.json({"status": "FAILURE", "message": "The searchvar is missing."});
}
//res.send('id: ' + req.query.id);
});
app.listen(3000);
When I call it with RESTClient in a browser with following URL:
http://localhost:3000/search?searchvar=aa
I get following response:
{"status": "FAILURE", "message": "No records found corresponding to given search query."}
It is an async call. I want to know the process in server using which i can return the response when the processing is done and handle this async response on client side.
Thanks in advance.

The response occurs because the if check:
if(recordsArray.length > 0) {
console.log("if........");
is executed before the callback passed to the cursor.each method has completed its processing. As a result, the array is empty.
Proposed solution
The cursor.forEach method takes a callback function that is executed asynchronously. This means you can access the results of the callback's execution only within the callback itself (except you return a Promise object). A fix would be:
cursor.forEach(function(doc) {
if (doc != null) {
var tempObject = {"name": doc.name, "cat": doc.cat};
recordsArray.push(tempObject);
console.dir(doc);
}
}, function(err) {
if(recordsArray.length > 0) {
console.log("if........");
res.json({"status": "SUCCESS", "message": "The request processed successfully.", "records":recordsArray});
} else {
console.log("else........");
res.json({"status": "FAILURE", "message": "No records found corresponding to given search query."});
}
db.close();
});

I think you're missing the callback parameter to the MongoClient Collection find() method. You should move your handling logic into the callback.
See these examples in the MongoDB documentation.
Your code would look something like this:
db.collection('myCollections').find({"name": new RegExp(searchvar)}).toArray(function(err, docs) {
// iterate through docs; perform your logic
db.close();
res.json({...});
});

I think your best bet is to go through a Nodejs/MongoDB tutorial to understand how everything works. From a quick look at your code your response is not asynchronous. Any time the server responds right aways it's not async. Async is achieved on the server side when you implement something like Websockets or HTTP Callbacks.
I agree with Jim that the problem is probably in your client (please post the code if you still have questions). Generally a JS client achieves async by using callbacks or promises, but an async client does not require an async server. The reason it is common to make async client calls is so you don't lock up your GUI while waiting for a response from the server.

Related

How to get results from MySql DB using and send them back to API.ai

I need some assistance in figuring out the syntax for SQL queries while using them through an api.ai webhook & connecting to a Google cloud Mysql database.
Although the query is working, the 'request gets timed out'
'use strict';
const mysql = require('mysql');
exports.name = (req, res) => {
let action = req.body.result['action'];
if (action === 'apple') {
callDB().then((output) => {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(output));
}).catch((error) => {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(error));
});
}
};
function callDB() {
return new Promise((resolve, reject) => {
try {
var connection = mysql.createConnection({
host: "<host>",
user: "<user>",
password: "<pass>",
database: "<DB>"
});
connection.query("SELECT description AS solution FROM mtable WHERE title LIKE '%Breakfast%'", function (error, results, fields) {
if (!error) {
let response = "The result is: " + results[0].solution;
response = response.toString();
let output = {'speech': response, 'displayText': response};
console.log(output);
resolve(output);
} else {
let output = {'speech': 'Error. Query Failed.', 'displayText': 'Error. Query Failed4.'};
console.log(output);
reject(output);
}
});
connection.end();
} catch (err) {
let output = {'speech': 'try-catch block error', 'displayText': 'try-catch block error3'};
console.log(output);
reject(output);
}
}
);
}
If i substitute the query with this, it works :
'SELECT description AS solution FROM mtable WHERE id LIKE 1001'
id is a column name with only id`s
title is a column name with titles such as Breakfast wrap, etc.
this is part of the error shown on webhook json:
"metadata": {
"intentId": "<id>",
"webhookUsed": "true",
"webhookForSlotFillingUsed": "false",
"webhookResponseTime": 5000,
"status": {
"code": 206,
"errorType": "partial_content",
"errorDetails": "Webhook call failed. Error: Request timeout.",
"webhookTimedOut": true
},
I referenced the following thread for the code,
How to get results from MySql DB using node.js MySQL and send them back to API.ai
There seems to be a typo in your query string declaration (near '%Breakfast%'') :
connection.query('SELECT description AS solution FROM mtable WHERE title LIKE '%Breakfast%'', function (error, results, fields) {
When assigning your query string to a variable, 'SELECT description AS solution FROM mtable WHERE title LIKE '%Breakfast%'' is interpreted as a number (because of the % operator).
Does fixing your single quotes help in any way?
connection.query("SELECT description AS solution FROM mtable WHERE title LIKE '%Breakfast%'", function (error, results, fields) {
Problem has to be on your server side with mysql. I do this daily and it works just fine with mysql in under 5 seconds for queries.
Could be your where clause is creating full table scans, thus timing out back to Diagflow (>5 seconds) or the db connection is falling out.
You need to set timers in front of the routine and the end cycle, look at your durations. Run your query from a bash script alone and see how long it takes. You'll find where the timeout is happening. Having search parms (%) on both sides of the criteria will definitely take longer than a search string start only (meaning search starting with) vs. %search% (find any substring that contains).
Good luck.

How does this javascript object modification work? I don't understand the scope, It doesn't persist

I'm using Express, Node.js and a Mongo DB. I've simplified this code somewhat from my actual code to try to cut out the noise, I hope it makes sense.
My basic problem is that I'm modifying an object and I am expecting the data change to persist across client-server calls, but it isn't. The comments explain more.
// server side routes.js
var LM = require('./modules/login-manager'); // database accessing functions
var DM = require('./modules/data-manager'); // database accessing functions
module.exports = function (app) {
app.post('/', function (req, res) {
// LM.manualLogin does a MongoDB call that looks fine, returns the expected data
LM.manualLogin(req.body['user'], req.body['pass'], function (e, o) {
if (!o) {
res.status(400).send(e);
} else {
req.session.user = o;
// calculateRank adds arbitrary fields to the user object
// this change persists to POST /dashboard
calculateRank(req.session.user);
// DM.getListOfData() does a Mongo DB call that looks fine
// and returns the expected data in o
DM.getListOfData(function (e, o) {
if (!o) {
res.status(400).send(e);
} else {
// availableData doesn't already exist. This assignment
// operation looks good
// in the debugger, but "availableData" is NOT in the
// req.session when we get to POST /dashboard
req.session.availableData = o;
}
});
res.status(200).send(o);
}
});
});
app.get('/dashboard', function (req, res) {
if (req.session.user == null) {
res.redirect('/');
}
else {
// "availableData" is not there!
// but changes to req.session.user are
res.render('dashboard', {
user: req.session.user,
data: req.session.availableData
});
}
});
}
// client side
$('#login').ajaxForm({
beforeSubmit: function (formData, jqForm, options) {
formData.push({
name: 'remember-me',
value: $('.button-rememember-me-glyph').hasClass('glyphicon-ok')
});
return true;
},
success: function (responseText, status, xhr, $form) {
if (status == 'success') window.location.href = '/dashboard';
},
error: function (e) {
// login error
}
});
So I click my login button that drives the client-side ajax function. Server side, I get the POST / and when that returns, client side redirects to /dashboard. The problem is that server side, in POST /dashboard some of my data is missing. The thing I really don't understand is that the data set by "req.session.user = o;" and "calculateRank(req.session.user)" persists. The data set by "req.session.availableData = o;" doesn't, in POST /dashboard it is as if the operation never happened (but the other two did). When I debug that assignment operation it looks fine and I can see the data in the req object.
Can anyone help explain this to me please? Thanks!
You're sending the response, res.status(200).send(o);, before req.session.availableData = o; has had time to run, since it's an async operation.
Sending the response inside the getListOfData callback would fix your problem.

Node.js Express and Parse.com

I've set up a node.js server-app that I want to do some parse.com requests.
I basically want it to return the parse-object JSON-representation.
My route:
var blog = require('./models/model');
app.get('/api/article/:permalink', function(req, res) {
res.json(blog.getArticle(req.params.permalink));
});
And my model:
var Parse = require('parse/node').Parse, // load the parse for node package
keys = require('../../config/keys'); // keys config-file for hosted services
Parse.initialize(keys.app, keys.js);
module.exports = {
getArticle: function(permalink) {
"use strict";
var Article = Parse.Object.extend('Article');
var query = new Parse.Query(Article);
query.include('category');
query.include('profile');
query.equalTo('permalink', permalink);
query.find().then(function(results) {
return results;
}, function(error) {
return error;
});
}
};
The thing is, this returns nothing when I call an article with a permalink that I know to exist (example: http://localhost/api/article/testFoo). I don't get any errors either.
My browser console flashes a message for a split second that reads:
Resource interpreted as Document but transferred with MIME type application/json: "http://localhost/api/article/testFoo"
Any suggestions to what I am doing wrong?
You are trying to use the return value of an async function. This can't work, you need to pass a callback (or the res object) to your getArticle function, which will then use it to send the data back.
With a callback:
app.get('/api/article/:permalink', function(req, res) {
blog.getArticle(req.params.permalink, function(data) {res.json(data)});
});
...
getArticle: function(permalink,callback) {
...
query.find().then(function(results) {
callback(results);
}, function(error) {
callback({error: error});
});

How to get attributes of a Neo4j node

I am making a connection to the neo4j in Nodejs to get the attribute of ServiceConsumer node. But not sure, how to do it. here is my code which connect to the neo4j. Suppose ServiceConsumer has some attributes like city, state, name, userId and I have to retrieve the name of ServiceConsumer. I am getting the userId from the frontend and on the basis of this userId querying the neo4j database to get the node information.How would i get the name of ServiceConsumer? Any help ll be appreciated.
var user = this.userId;
var request = require("request");
var host = 'localhost';
port = 7474;
var httpurlforconnection ='http://' + host + ':' + port + '/db/data/transaction/commit';
/*Let’s define a function which fires the cypher query.*/
function runCypherQuery(query, user, callback) {
request.post({
uri: httpUrlForTransaction,
json: {statements: [{statement: query, parameters: user}]}
},
function (err, res, body) {
callback(err, body);
})
}
// Let’s fire some queries below
runCypherQuery(
'MATCH (n:ServiceConsumer {userId : {} }) RETURN n', {
userId: 'user',
}, function (err, resp) {
if (err) {
console.log(err);
} else {
console.log(resp);
}
}
);
Take a look at How to return all properties of a node with their name and their value using Cypher
You can do the same thing using nodejs, simple POST can return the whole node to you and then simply cast it to an object using JSON.
By the way, your code is working fine, you can simply take the "resp" object which should contain the result JSON.
One obvious thing that I see is that you're not specifying the userId parameter. You Cypher should look something like this:
MATCH (n:ServiceConsumer {userId: {user_id}}) RETURN n
Does that help?

Meteor: Handling Facebook Realtime API Requests via Callback-Url with iron-router

I'm trying to use the Facebook Realtime API with a Meteor app. Basically you can subscribe to an object like page or user and get update notifications so that no polling is needed (publish/subscribe pattern).
So I need to setup a callback URL and send a post request with my callbackurl, the object & fields that I want to subscribe and a verify string. A FB server is sending a get request with my verify string and a random string (challenge) and the Meteor server has to respond to this with the challenge string. (If you're interested in details you can read here and here).
I'm getting the following error serverside:
[ec2-11-11-1-123.eu-west-1.compute.amazonaws.com] Exception while invoking method 'createSubscription' Error: error while subscribing to object page - failed [400] {"error":{"message":"(#2200) callback
verification failed: Operation timed out after 6000 milliseconds with
0 bytes received","type":"OAuthException","code":2200}}
at subscription (packages/fbuilder/lib/server/methods.js:20)
at Meteor.methods.createSubscription (packages/fbuilder/lib/server/methods.js:110)
at maybeAuditArgumentChecks (packages/livedata/livedata_server.js:1487)
at packages/livedata/livedata_server.js:643
at _.extend.withValue (packages/meteor/dynamics_nodejs.js:56)
at packages/livedata/livedata_server.js:642
at _.extend.withValue (packages/meteor/dynamics_nodejs.js:56)
at _.extend.protocol_handlers.method (packages/livedata/livedata_server.js:641)
at packages/livedata/livedata_server.js:541
So i guess something is wrong with the router part listening to the given URL...
Router.map(function () {
// Callback URL of the server for verification and receiving data via FB Realtime API
this.route('fbCallbackURL', {
path: Meteor.settings.fbCallbackPath,
where: 'server',
action: function() {
var req = this.request;
var res = this.response;
switch(req.method){
"GET" :
var hub = req.hub;
console.log("got something ...........");
if(hub && hub.verify_token === Meteor.settings.fbVerifyString
&& hub.mode==='subscribe' && hub.challenge){
res.writeHead(200);
res.end({hub: {challenge: hub.challenge}});
}
else{
res.writeHead(404);
res.end("");
}
break;
"POST":
console.log("todo");
/*
The HTTP request will contain an X-Hub-Signature header which contains the SHA1 signature of the request payload,
using the app secret as the key, and prefixed with sha1=. Your callback endpoint can verify this signature
to validate the integrity and origin of the payload.*/
break;
default: res.writeHead(404);
}
}
})
});
Here's the code where I create the subscription:
function subscription(object,fields,active){
if(object){
var url = 'https://graph.facebook.com/'+Meteor.settings.fbAppId+'/subscriptions',
res,
params = {
access_token : Meteor.settings.fbAppId + '|' + Meteor.settings.fbAppSecret,
callback_url : Meteor.settings.fbAppURL+""+Meteor.settings.fbCallbackPath,
object : object,
verify_token : Meteor.settings.fbVerifyString,
fields : fields
};
try {
res = HTTP.post(url , { params: params } );
} catch (err) {
throw _.extend(
new Error("error while subscribing to object " + object + " - " + err.message + " ",{response: err.response}));
}
return res;
}
else
new Error("subscription for invalid object requested!");
};
So I'm calling this function via a method on the server: (just for testing)
Meteor.methods({
createSubscription: function() {
return subscription("page","name",true);
}
});
Any ideas or suggestions? :(
Thanks in advance!

Categories