I coded a minimalist server to manage REST services. The only coded route is supposed to retrieve data from mongo, then to send it to the client. Code is below.
var kr = require('koa-route');
var koa = require('koa');
var app = koa();
var MongoClient = require("mongodb").MongoClient;
var events = function * ()
{
var _this = this;
MongoClient.connect("mongodb://localhost/eventdata", function(error, db) {
if (error) throw(error);
console.log("Connecté à la base de données");
db.collection('events').find().toArray(function(err, array)
{
console.log('returning %d objects', array.length);
db.close();
_this.body = {eventsArray : array};
});
});
}
app.use(kr.get('/events', events));
app.listen(3000);
Koa is version 1.2.4
When I try to reach it, I get the error message "Can't remove headers after they are sent". I managed to understand that the program (sort of) finishes to send back response before the find() is over. So when the _this.body = { ... } is called, this causes the error.
Now question is : how to fix that ?
I just began to practice Koa, so there are a lot of stuff I'm not used to.
I tested solution proposed here : Can't remove headers after they are sent but it doesn't work. I get the following error message :
eventsArray = yield db.collection('events').find({})
^^
SyntaxError : unexpected identifier
Thanks in advance for your help.
it looks like you need to wrap db req to promise. Try in this way
var kr = require('koa-route');
var koa = require('koa');
var app = koa();
var MongoClient = require("mongodb").MongoClient;
var eventsPromise = function(){
return new Promise(function(resolve, reject){
MongoClient.connect("mongodb://localhost/eventdata", function(error, db) {
if (error){
return reject(error);
}
console.log("Connecté à la base de données");
db.collection('events').find().toArray(function(err, array)
{
console.log('returning %d objects', array.length);
db.close();
return reslove({eventsArray : array});
});
});
});
}
var events = function * ()
{
var event = yiled eventsPromise();
this.body = event;
}
app.use(kr.get('/events', events));
app.listen(3000);
Related
I'm guessing this is related to not understanding promises and execution order, but I'm currently stumped why this Firebase Function (repackaged Google Cloud Functions) code runs recursively.
Currently the function executes once successfully (fetches data, writes database entry, writes file in storage), and then repeats every 15-30 seconds until it reaches the '402' error state. It is intended to only execute once.
Any help would be appreciated.
exports.add = functions.https.onRequest((req, res) => {
cors(req, res, () => {
if (req.query.idToken) {
// there's a query param
var idToken = req.query.idToken;
admin.auth().verifyIdToken(idToken)
.then(function(decodedToken) {
var uid = decodedToken.uid;
var userRef = database.ref('users/' + uid);
var feedCountRef = database.ref('users/' + uid).child('feeds');
var plansRef = database.ref('plans')
userRef.once('value', function(snapshot){
var feedsCount = snapshot.val().feeds;
var currentPlan = snapshot.val().membership;
var planRef = database.ref('plans/' + currentPlan);
planRef.once('value', function(snapshot) {
console.log(snapshot.val());
var allowedFeeds = snapshot.val().feeds;
if(feedsCount < allowedFeeds) {
fetchFeed(req.body.feedSource, function(feedData) {
var defaultFeedName = 'Untitled';
var defaultUpdateFrequency = 'Weekly';
var feedsdatabaseRef = database.ref('feeds/' + uid);
var newFeedDatabaseRef = feedsdatabaseRef.push();
var feedKey = newFeedDatabaseRef.key;
writeFeedStorage(feedKey, feedData, function(response) {
console.log(response);
newFeedDatabaseRef.set({
// write data
})
});
feedCountRef.transaction(function(feeds){
return (feeds || 0) + 1;
});
return;
});
} else {
console.log('over quota');
res.status(402).send({error: 'You are at the maximum number of feeds your plan allows.'});
}
});
})
}).catch(function(error) {
res.status(401);
});
} else {
res.status(401);
}
})
})
From your code snippet, a potential reason that it would be running repeatedly is that you are not returning an ok status if things worked out correctly, e.g.
res.status(200).send('ok');
According to the Firebase documentation, this is something you should be doing for HTTP Functions.
I have a weird problem where my callback is never published and the message goes to timeout, even though the method runs in the queue. This happens in some specific queues and after it happens once, i cannot make any other requests from client which even previously worked, they all timeout. Have to restart the client and sever to make it working again.
This is the code, where its happening, and i cant seem to understand whats wrong.
Server.js file where i am creating the queues. I have several such queues, this is one of them.
var amqp = require('amqp');
var util = require('util');
var cnn = amqp.createConnection({host:'127.0.0.1'});
var getCart = require('./services/getCart');
cnn.on('ready', function() {
cnn.queue('getCart_queue', function(q){
q.subscribe(function(message, headers, deliveryInfo, m){
// util.log(util.format( deliveryInfo.routingKey, message));
// util.log("Message: "+JSON.stringify(message));
// util.log("DeliveryInfo: "+JSON.stringify(deliveryInfo));
getCart.handle_request(message, function(err,res){
cnn.publish(m.replyTo, res, {
contentType:'application/json',
contentEncoding:'utf-8',
correlationId:m.correlationId
});
});
});
});
});
Here, the handle request function is completed successfully, but the callback never goes through and its always timeout on the other end
var cart = require('../models/cart');
function handle_request(msg, callback) {
var user_id = msg.id;
cart
.find({id:user_id})
.populate('users ads')
.exec(function(err, results){
// This works, just the callback doesnt
if(!err){
console.log(results);
callback(null, results);
} else {
console.log(err);
callback(err, null);
}
});
}
exports.handle_request = handle_request;
this is how i am calling the request
var msg_payload = {"id":id};
mq_client.make_request('getCart_queue', msg_payload, function(err, results){
console.log(results); // never prints
//stuff that is never reached
});
These are my rpc files, i dont think there should be anything wrong with these, as some other queues work fine.
And this is the error shown on client
GET /getCart - - ms - -
Error: timeout 6ee0bd2a4b2ba1d8286e068b0f674d8f
at Timeout.<anonymous> (E:\Ebay_client\rpc\amqprpc.js:32:18)
at Timeout.ontimeout [as _onTimeout] (timers.js:341:34)
at tryOnTimeout (timers.js:232:11)
at Timer.listOnTimeout (timers.js:202:5)
Hope the information is not vague, if you need more, please let me know. Thanks!
I Think the error is in this file, because i tried debugging and from the rabbitmq server, the callback is being called and it has the correlation id as well as the reply to variable, so the request is not getting picked up here.
var amqp = require('amqp')
, crypto = require('crypto');
var TIMEOUT=8000;
var CONTENT_TYPE='application/json';
var CONTENT_ENCODING='utf-8';
var self;
exports = module.exports = AmqpRpc;
function AmqpRpc(connection){
self = this;
this.connection = connection;
this.requests = {};
this.response_queue = false;
}
AmqpRpc.prototype.makeRequest = function(queue_name, content, callback){
self = this;
var correlationId = crypto.randomBytes(16).toString('hex');
var tId = setTimeout(function(corr_id){
callback(new Error("timeout " + corr_id));
delete self.requests[corr_id];
}, TIMEOUT, correlationId);
var entry = {
callback:callback,
timeout: tId
};
self.requests[correlationId]=entry;
self.setupResponseQueue(function(){
self.connection.publish(queue_name, content, {
correlationId:correlationId,
contentType:CONTENT_TYPE,
contentEncoding:CONTENT_ENCODING,
replyTo:self.response_queue});
});
};
AmqpRpc.prototype.setupResponseQueue = function(next){
if(this.response_queue) return next();
self = this;
self.connection.queue('', {exclusive:true}, function(q){
self.response_queue = q.name;
q.subscribe(function(message, headers, deliveryInfo, m){
var correlationId = m.correlationId;
if(correlationId in self.requests){
var entry = self.requests[correlationId];
clearTimeout(entry.timeout);
delete self.requests[correlationId];
entry.callback(null, message);
}
});
return next();
});
};
This is the code for your make_request() in client.js file:
var amqp = require('amqp');
var connection = amqp.createConnection({host:'127.0.0.1'});
var rpc = new (require('./amqprpc'))(connection);
function make_request(queue_name, msg_payload, callback){
rpc.makeRequest(queue_name, msg_payload, function(err, response){
if(err)
console.error(err);
else{
console.log("response", response);
callback(null, response);
}
});
}
exports.make_request = make_request;
Look at what happens when you have an err on rpc.makeRequest():
rpc.makeRequest(queue_name, msg_payload, function(err, response){
if(err)
console.error(err);
//
//HERE: should be a callback call here.
//
else{
console.log("response", response);
callback(null, response);
}
});
This could be why you are getting a timeout. I hope it helps.
There wasn't a problem with rabbitMQ but with my queries in the handle request and after responding to the request.
For others coming with this problem, check and double check every statement, as the error will not show in the console, but will only show a timeout
This is my app.js file
var http = require('http');
var url = require('url');
var mysql = require('mysql');
var requestListener = function(request, response){
var urlParse = url.parse(request.url,true);
var path = urlParse.pathname;
var query = urlParse.query;
var jsonString;
if(path === "/getArticleById"){
var conn = mysql.createConnection({
host:'localhost',
port:'3306',
user:'root',
password:'root123',
database:'food'
});
conn.connect(function(err){
if(err){
console.log('Error connecting to database');
return;
}
response.writeHead(200, {'Content-Type': 'text/plain' });
});
var id = query.id;
conn.query('select * from article where id=?', id,function(err,rows){
if(err){
console.log(err);
}
jsonString = rows;
});
conn.end();
console.log(jsonString);
}
};
var server = http.createServer(requestListener);
server.listen(8080);
In jsonString which is inside conn.query, I am getting value of rows printed.
The last console.log is not printing anything, it is also jsonString.
Sorry, I am a newbie in node.js not aware of how to use objects.
Javascript is async in nature.
conn.query('select * from article where id=?', id,function(err,rows){
if(err){
console.log(err);
}
jsonString = rows;
});
Try printing the jsonString within the callback. So, your code should look like
conn.query('select * from article where id=?', id,function(err,rows){
if(err){
console.log(err);
}
jsonString = rows;
console.log(jsonString);
});
How your code is getting executed
Assignment to var id is getting done.
A query to db gets hit. This is time consuming process (async task), your callback function gets called when this query will get resolved. Note this will take some time, that means after sometime this callback will be called, where you will get the rows.
Just after calling the query() ( but before getting the result/callback), your next line of code, i.e conn.end() will get executed. Then you are trying to print the jsonString, Note, till now you haven't got the results back from DB (as it is async operation). That's why jsonString doesnot holds any value yet. Hence you didnt get result printed.
Solution:
print the jsonString after retrieving the result. that means, in the callback.
I'm playing around with Node, Socket.IO and BDD by creating a chat application. During one of the tests, I get a timeout error stating:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
The affected test is
it('#must be able to receive a message', function(done)
{
chatterServer.on('chatterMessage', function(data)
{
console.log('Incoming message!');
expect(data).to.have.property('message');
expect(data.message).to.be('Hello, world!');
done();
});
console.log('Sending message!');
chatterClient.send('chatterMessage', { message: 'Hello, world!' });
console.log('Sent!');
});
I found that the cause of this issue is that the chatterMessage event is not being caught by the server. Whilst I did specify it.
The console's output is:
Sending message!
Sent!
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
I'm probably doing something wrong. I'm not too familiar with Node and Socket.IO, so I'm sorry if this question is very obvious.
I looked around Google with the search terms 'socket.io server not receiving from client', but from what I found, nothing helped me to solve my issue so far.
I did however try the solution in this question, but that didn't fix it for me.
I'm using Mocha and expect.js
The complete test is:
var util = require('util');
var Chatter = require('../src/index');
var ChatterServer = Chatter.Server;
var ChatterClient = Chatter.Client;
var express = require('express');
var expect = require('expect.js');
var socketIO = require('socket.io');
var socketIOClient = require('socket.io-client');
var host = 'http://localhost';
var port = 8080;
describe('Chatter', function()
{
'use strict';
var chatterServer;
var chatterClient;
var server;
before(function()
{
var app = express();
server = app.listen(port);
});
beforeEach(function()
{
chatterServer = new ChatterServer(socketIO(server));
chatterClient = new ChatterClient(socketIOClient, util.format('%s:%s', host, port.toString()));
});
...
it('#must be able to receive a message', function(done)
{
chatterServer.on('chatterMessage', function(data)
{
console.log('Incoming message!');
expect(data).to.have.property('message');
expect(data.message).to.be('Hello, world!');
done();
});
console.log('Sending message!');
chatterClient.send('chatterMessage', { message: 'Hello, world!' });
console.log('Sent!');
});
});
My Client (ChatterClient) is:
(function()
{
'use strict';
function Client(socketIO, url)
{
this.socketIO = socketIO(url);
}
Client.prototype.send = function(event, data)
{
this.socketIO.emit(event, data);
};
Client.prototype.on = function(event, callback)
{
this.socketIO.on(event, callback);
};
if (module !== undefined && module.hasOwnProperty('exports')) {
module.exports = Client;
} else {
window.Chatter = {
Client: Client,
};
}
}());
The Server (ChatterServer) is:
(function()
{
'use strict';
function Server(socketIO)
{
this.socketIO = socketIO;
this.connectedUsers = {};
this.on('connection', (function(user)
{
var userID = user.client.id;
this.connectedUsers[userID] = user;
user.emit('chatterConnectionAcknowledged', { id: userID });
}).bind(this));
}
Server.prototype.on = function(event, handler)
{
this.socketIO.on(event, handler);
};
module.exports = Server;
}());
You need to change your code in two sides.
First side, you will need to listen incoming socket connections on the socketIO object. (see the emphasized code below)
//.. some code
function Server(socketIO)
{
this.socketIO = socketIO;
this.connectedUsers = {};
this.socketIO.on('connection', (function(user)
{
var userID = user.client.id;
this.connectedUsers[userID] = user;
user.emit('chatterConnectionAcknowledged', { id: userID });
}).bind(this));
}
//.. some code
Second side, when you are adding new events to listen on the server, you need to bind those events to the sockets since they are ones that are going to listen when events are emitted from the socket clients.
Server.prototype.on = function (event, handler) {
Object.keys(this.connectedUsers).map(function (key) {
this.connectedUsers[key].on(event, handler);
}.bind(this));
};
i did this tutorial node.js eventEmitter, it worked nicely. I added a method that uses http.request to get data, which works and emit the data.
the problem is that the listener doesn't catch the event !
can someone help ?
code :
var events = require('events');
var util = require('util');
var http = require('http');
//http request options, it query the twitter api and get the public timeline, works!
var options = {
hostname : 'api.twitter.com',
port : 80,
method : 'get',
path : '/1/statuses/public_timeline.json?count=3&include_entities=true'
}
// The Thing That Emits Event
Eventer = function(){
events.EventEmitter.call(this);
//tutorial examples
this.kapow = function(){
var data = "BATMAN"
this.emit('blamo', data);
}
//tutorial examples
this.bam = function(){
this.emit("boom");
}
//my method
this.GetTweetList = function(){
var tweets = "";
var req = http.request(options, function(response){
var body = "";
response.on('data',function(data){
body += data;
});
response.on('end', function(){
tweets = JSON.parse(body);
this.emit("tweets", tweets);
util.puts('!!!!!!!!!! got some data !!!!!!!!!! \n');
});
});
req.end();
}
};
util.inherits(Eventer, events.EventEmitter);
// The thing that listens to, and handles, those events
Listener = function(){
//tutorial examples
this.blamoHandler = function(data){
console.log("** blamo event handled");
console.log(data);
},
//tutorial examples
this.boomHandler = function(data){
console.log("** boom event handled");
}
//my listener method
this.GetTweetListHandler = function(data){
console.log("** tweets event handled");
util.put(data);
util.puts('!!!!!!!!!! got some data in listener !!!!!!!!!! \n');
}
};
// The thing that drives the two.
//instanciating the object and liking the methodes
var eventer = new Eventer();
var listener = new Listener(eventer);
eventer.on('blamo', listener.blamoHandler);
eventer.on('boom', listener.boomHandler);
eventer.on('tweets', listener.GetTweetListHandler);
//calling the methodes
eventer.kapow();//works
eventer.bam();//works
setInterval(eventer.GetTweetList, 2000);
//eventer.GetTweetList();// still waiting but the eventer display that he got the data
Hard one to spot ...
The problem is the this pointer from this.emit("tweets", tweets);. You are doing this call from within an anonymous callback passed to response.on, so this does not represent the Eventer object that you created.
To solve it, you need to "save" the this pointer (a common practice).
var tweets = "";
var self = this;
....
self.emit("tweets", tweets);