Bluebird.js in Node and asynchronous api calls - javascript

So I'm trying to build my first webapp with Facebook integration (using facebook-node-sdk). I have it making simple calls to the api, but now it's time to put this all in a simple server and make the calls upon request (this isn't going to be the webapp, itself, but more of an API server).
The problem I'm running into is that, even though I've (presumably) used bluebird to Promisify the Facebook sdk and my makeCall method, I'm still getting "hi" printed and then "undefined" - console.log is getting called before makeCall can return anything.
Here's my app.js:
var Promise = require('bluebird')
, http = require('http')
, Facebook = Promise.promisifyAll(require('facebook-node-sdk'))
, config = require('./config')
, fb = new Facebook({ appId: config.fb.api, secret: config.fb.secret });
var makeCall = new Promise.method(function (username) {
return fb.api(username, function(err, data) {
console.log('hi')
if (err) return err;
return data;
});
});
http.createServer( function (req, res) {
makeCall('/me').then(console.log)
}).listen(8001);

new Promise.method doesn't make sense here (or anywhere since it's a function and not a constructor) nor does makeCall.
Try this:
var Promise = require('bluebird')
, http = require('http')
, Facebook = require('facebook-node-sdk')
, config = require('./config')
, fb = new Facebook({ appId: config.fb.api, secret: config.fb.secret });
Promise.promisifyAll(Facebook.prototype);
http.createServer( function (req, res) {
fb.apiAsync('/me').then(function (data) {
console.log(data)
})
}).listen(8001);
Don't create wrappers when promisifyAll does it for you :)

The problem was that I was neither returning a Promise nor was I resolving said un-returned promise. Here's the fixed code (that works!)
var Promise = require('bluebird')
, http = require('http')
, Facebook = Promise.promisifyAll(require('facebook-node-sdk'))
, config = require('./config')
, fb = new Facebook({ appId: config.fb.api, secret: config.fb.secret });
var makeCall = new Promise.method(function (username) {
return new Promise(function (resolve) {
// resolve
console.log('resolve')
fb.api(username, function(err, data) {
console.log('err: ' + err)
console.log('data: ' + data)
if (err) reject(err);
resolve(data);
});
});
});
http.createServer( function (req, res) {
makeCall('/me').then(function (data) {
console.log(data)
})
}).listen(8001);
Where the output looks like:
resolve
err: null
data: [object Object]
{ id: ... }

Related

node.js: The POST method in my RESTAPI does not work

I start learning Node.js and Express.js and I'm trying to create a simple API to list data from JSON file (using the GET method) and add a new user using the POST method.
the GET method works fine but the POST method does not work
when I request http://127.0.0.1:8080/listusers the API sends all users in a JSON file.
when I request http://127.0.0.1:8080/adduser the API has to add new User Info and send the new data back to the browser.
NOTE: I read all the questions on Stackoverflow about this problem but
non of them help me so I have to ask again.
the problem is when I request http://127.0.0.1:8080/adduser I get the following error
Cannot GET /adduser
here is the server.js:
var express = require('express');
var app = express();
var fs = require('fs');
var user = {
"user4" : {
"name" : "mounir",
"password" : "password4",
"profession" : "teacher",
"id": 4
}
};
app.post('/adduser', function (req, res) {
// First read existing users.
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
data = JSON.parse( data );
data["user4"] = user["user4"];
console.log( data );
res.end(JSON.stringify(data) );
});
});
app.get('/listusers', function (req, res) {
fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
console.log(data);
res.end(data);
});
});
var server = app.listen(8080, function () {
var host = server.address().address;
var port = server.address().port;
console.log("listening at http://%s:%s", "0.0.0.0", port)
});
The answer is in the error. Cannot GET /adduser. Keyword GET! If you are making a post request, be sure you include the appropriate headers and that you are making a POST request, with a body, and not a GET request. For instance if you are using fetch:
const myInit = {
method: 'POST',
headers: myHeaders,
body: {
...
}
};
fetch("http://127.0.0.1:8080/adduser", myInit)
.then(res => {
...
});

NodeJS - 'Error: Invalid URI "/"'

I'm using request-promise to request two JSON data files which exist locally in my local project directory folder.
ie:
However, I am getting a 500 internal server error, when trying to pass the data to the view and my node console outputs 'Error: Invalid URI "/"',
Please see below:
server.js
let express = require('express');
let app = express();
let path = require('path');
const rp = require("request-promise");
//STORE PATH for local JSON files on variables
let guest = require('./public/data/Companies');
let hotel = require('./public/data/Guests');
app.set("port", process.env.PORT || 5000);
//GET JSON
//Question: Is it okay to pass uri:guest
app.get('/data', function(req, res) {
Promise.all([rp({uri: guest, json: true}), rp({uri: hotel, json: true})]).then(function([hotels, guests]) {
//res.json({hotels, guests});
res.send({hotels, guests});
console.log(hotels, guests);
}).catch(function(err) {
console.log(err);
res.status(500).end();
});
});
//CATCHALL
app.get("/*", function(req,res){
let file = req.params[0] || "/views/index.html";
res.sendFile(path.join(__dirname, "/public/", file));
});
//SET PORT
app.listen(app.get("port"), function(){
console.log("Listening on port: " , app.get("port"));
});
then on client.js:
$(function() {
$.ajax({
type: "GET",
url: "/data",
success: function (res) {
console.log(res);
}
});
});
Why do you use request to get the data? Why don't you use the filesystem module from Node.js (fs) to get the data? When you call rp(), you should pass an absolute URI and not a local path.
To use it in your code, you need to "promisify" the readFile function:
let readFileAsAPromise = function(filename){
return new Promise(function(resolve, reject) {
fs.readFile(filename, (data, err) => {
if(err) reject(err);
resolve(data)
})
})
}
You can then you Promise.all.
Why aren't you simply returning the variables?
I mean:
app.get('/data', function(req, res) {
res.send({hotels, guests});
});

API with Node & Express gives back old cached response until newer is retrieved

I cannot find how to prevent this behavior. I have a very simple Express API that works well and gives the intended response. It´s connected to a database storing press articles.
The problem comes when I request article with id 1. It shows up. Then I try to request article with id number 2, and instead I am getting the cached response of the previous article. This goes on for as long as it takes for the database to actually deliver the article 2.
This is what I have:
var sql = require('mssql');
var express = require('express');
var path = require('path');
var response;
var dbConfig = {
//connection details
};
function getArt(an) {
var conn = new sql.Connection(dbConfig);
conn.connect().then(function() {
var req = new sql.Request(conn);
req.query(/* query to the DB */).then(function(recordset) {
response = recordset;
conn.close();
})
.catch(function(err) {
response = err;
conn.close();
});
})
.catch(function(err) {
response = err;
conn.close();
})
return response;
}
var app = express();
app.get('/sourcedeletion/api/v2.0/article/:an', function(req, res) {
res.send(getArt(req.params.an.toString()));
});
app.listen(3000);
What part am I missing, that could help me to get the real response on every request, rather than the previous cached one until the new one arrives from the server?
You're using a global variable response that is not enclosed in the scope of the request. This is prone to error! getArt returns the value of the global variable response without waiting for the promise to execute, so it returns an old value. You should do:
const sql = require('mssql');
const express = require('express');
const path = require('path');
const dbConfig = {
//connection details
};
const getArt = (an) => {
const conn = new sql.Connection(dbConfig);
return conn.connect()
.then(() => {
const req = new sql.Request(conn);
return req.query(/* query to the DB */)
})
.catch(err => err)
.then(response => {
conn.close();
return response;
});
});
};
const app = express();
app.get('/sourcedeletion/api/v2.0/article/:an', (req, res) => {
getArt(req.params.an.toString())
.then(response => {
res.send(response);
});
});
app.listen(3000);
con.connection()
.. is an async call, so before the new response is set, the function returns what ever was there before.
If you want to make sure that the function waits for the connection to finish before returning a value try to change to the following:
return conn.connect().then(function() {
var req = new sql.Request(conn);
return req.query(/* query to the DB */).then(function(recordset) {
response = recordset;
return response;
})
.catch(function(err) {
response = err;
return response;
});
})

Express promise chain error

I've written a simple express.js server that handles REST API requests and fetches data from a MongoDB database. When I make a GET request to a specific endpoint ("localhost:8081/api/getUserData"), the promise chain doesn't work the way I want it to, and I still don't understand.
This is the error I get:
"[TypeError: Cannot read property 'db' of undefined]"
var MongoClient = require('mongodb').MongoClient;
var express = require('express');
var app = express();
var rp = require("request-promise");
var cors = require('cors');
// use it before all route definitions
app.use(cors({ origin: '*' }));
/********************** REST API FUNCTIONS **********************/
app.get('/api/getUserData', function (req, res, next) {
var context = {};
console.log("in api getUserData")
context.db_url = 'mongodb://localhost:27017/test';
openDatabaseConnection(context)
.then(getAllUserLocations)
.then(closeDatabaseConnection)
.then(function (context) {
res.send(context.userLocations)
})
.catch(function (error) {
console.log("ERROR :");
console.log(error);
})
})
/********************** END REST API FUNCTIONS **********************/
function getAllUserLocations(context) {
context.db.collection("test").find().toArray().then(function (err, result) {
console.log("Received from db: " + result.length + " objects");
context.userLocations = result;
return context;
});
}
function openDatabaseConnection(context) {
console.log("Opening DB connection...");
return MongoClient.connect(context.db_url)
.then(function (db) {
console.log("DB connection opened.");
context.db = db;
return context;
})
}
function closeDatabaseConnection(context) {
console.log("Closing DB connection");
return context.db.close()
.then(function () {
console.log("DB connection closed");
return context;
})
}
/********************** STARTING SERVER **********************/
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Githex server listening at http://%s:%s", host, port)
})
Any help would be appreciated, and even more with an explanation because I don't understand what I've done wrong.
Thanks!
Just like #adeneo mentioned on the first comment, you are missing the db property. Look at your first function:
app.get('/api/getUserData', function (req, res, next) {
var context = {};
console.log("in api getUserData")
context.db_url = 'mongodb://localhost:27017/test';
openDatabaseConnection(context)
.then(getAllUserLocations)
.then(closeDatabaseConnection)
.then(function (context) {
res.send(context.userLocations)
})
.catch(function (error) {
console.log("ERROR :");
console.log(error);
})
});
Now going through the lines within this function:
You setup context as an empty object
You added a db_url property onto the object, so far you have
context = {db_url: "mongodb://localhost:27017/test}
You pass the context object into the openDatabaseConnection function
Within the openDatabaseConnection function, you return a context object. This new returned object doesn't get set anywhere, it just gets returned and never used. You want to call the getuserlocation() function with that returned value.
So instead of just calling
.then(getAllUserConnection)
I would do
.then(function(context){getAllUserConnection(context);})
That should make use of the returned value and make sure you are using it.

express unit testing, calling close on server results in `call of undefined`

I am attempting to test drive an node.js application based on express. I want to return a simple 404.html, which I can successfully do, but afterward, calling close on the node http server gets this error:
Fatal error: Cannot call method 'call' of undefined
I am having a hard time tracking down what is undefined because the same method works beautifully when called elsewhere.
Here is my express code:
function Server() {
this.port = 9000;
this.staticDir = '/public';
}
function handleHomeRequest(req, res) {
var body = '<html><body>Home Page.</body></html>';
res.send(body);
}
Server.prototype.start = function () {
expServer = express();
expServer.get('/', function (req, res) { handleHomeRequest(req, res); });
expServer.use(function (req, res) {
res.status(404).sendfile('./src/public/404.html');
});
runningServer = expServer.listen(this.port);
};
Server.prototype.stop = function (cb) {
runningServer.close(cb);
};
Here is my nodeunit test code:
var ROOT_URL = 'http://localhost',
PORT = 9000,
URL = ROOT_URL + ':' + PORT + '/',
http = require('http'),
Server = require('./server.js'),
server;
exports.setUp = function(done) {
server = new Server();
done();
};
exports.tearDown = function (done) {
server = null;
done();
};
exports['Requesting a page that does not exist results in a 404.'] = function (test) {
server.start();
httpGet(URL + 'guaranteedNotToExistPage', function(res, data) {
test.equal(404, res.statusCode, 'Requesting a page that dne did not return with a status code of 404.');
test.ok(data.indexOf('404 Page Not Found') > -1, 'The 404 page was not returned.');
//test.done();
server.stop(test.done);
});
};
function httpGet(url, callback) {
var request = http.get(url),
receivedData = '';
request.on('response', function (response) {
response.setEncoding('utf8');
response.on('data', function (chunk) {
receivedData += chunk;
});
response.on('end', function () {
callback(response, receivedData);
});
});
}
The result of the http get request come back, the failure only occurs when I call server.stop(test.done); however, stopping the server is required to ensure my unit tests can be run in any order and independent.
First, where runningServer is defined? I can't see a
var runningServer;
anywhere in the first peace of code.
So, if you write a value in prototype.start I doubt you can access it on prototype.stop that is a different scope.
Second, {expressListener}.close() in node 0.6 was just synchronous, they added the callback on the 0.8. So, check the node.js version to be sure that the {cb} is correctly handled.

Categories

Resources