I followed a few Nodejs tutorials ang got through a simple REST webservice. This webservices listens /api/accounts for an email and a password, then proceeds to a lookup in a Cassandra cluster. I use EventEmitter to avoid callback hell. Everything works fine until the emit('success') is attempted :
events.js:72
throw er; // Unhandled 'error' event
^
TypeError: Object SELECT * FROM accounts WHERE email_accounts=?,test@gmail.com,77ddcc8d868e1532f86f0e25f35d43e5,function _checkPassword(error, response) {
var rows=response.rows;
if (_checkForErrors(error, rows , 'password')) {
return false;
} else {
if ( rows.length == 1 ) {
console.log('got user profile with password_accounts ' + rows[0].pwd_accounts);
this.emit('success', rows[0] );
}
}
} has no method 'emit'
at Array._checkPassword (/home/scoulibaly/node/login.js:33:10)
at /home/scoulibaly/node/node_modules/node-cassandra-cql/index.js:220:16
at Connection.handleResult (/home/scoulibaly/node/node_modules/node-cassandra-cql/lib/connection.js:308:3)
at ResultEmitter.EventEmitter.emit (events.js:106:17)
at ResultEmitter.bufferAndEmit (/home/scoulibaly/node/node_modules/node-cassandra-cql/lib/streams.js:456:10)
at ResultEmitter.each (/home/scoulibaly/node/node_modules/node-cassandra-cql/lib/streams.js:440:17)
at ResultEmitter._write (/home/scoulibaly/node/node_modules/node-cassandra-cql/lib/streams.js:413:10)
at doWrite (_stream_writable.js:223:10)
at writeOrBuffer (_stream_writable.js:213:5)
at ResultEmitter.Writable.write (_stream_writable.js:180:11)
My main is a server.js. I use Express and router() to setup the routes :
// server.js
// BASE SETUP
// =============================================================================
// call the packages we need
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
var Login = require('./login');
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080; // set our port
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); // get an instance of the express Router
// middleware to use for all requests
router.use(function(req, res, next) {
// do logging
console.log('Something is happening.');
next(); // make sure we go to the next routes and don't stop here
});
router.route('/accounts')
// get the account with that id (accessed at GET http://localhost:8080/api/accounts/:account_id)
.get(function(req, res) {
var login = new Login(req.query.email, req.query.password);
login.on('error', function (error) {
res.writeHead(500);
res.end();
});
login.on('failure', function (reason) {
if (reason == 'email') {
res.end('Wrong email!');
} else if (reason == 'password') {
res.end('Wrong password!');
}
});
login.on('success', function (data) {
res.json(data);
});
login.perform();
});
// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
// more routes for our API will happen here
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);
// START THE SERVER
// =============================================================================
app.listen(port);
console.log('Magic happens on port ' + port);
My login.js extends EventEmitter :
// login.js
var util = require('util');
var eventEmitter = require('events').EventEmitter;
var cql = require('node-cassandra-cql');
function Login(email,password) {
// Error checking
function _checkForErrors(error, rows, reason) {
if (error) {
console.log('emit err');
this.emit('error', error);
return true;
}
if (rows.length < 1) {
console.log('emit fail');
this.emit('failure', reason);
return true;
}
return false;
}
// Check {email,pwd}
function _checkPassword(error, response) {
var rows=response.rows;
if (_checkForErrors(error, rows , 'password')) {
return false;
} else {
if ( rows.length == 1 ) {
console.log('got user profile with password_accounts ' + rows[0].pwd_accounts);
this.emit('success', rows[0] );
}
}
}
function perform() {
var client = new cql.Client({hosts: ['localhost', 'localhost'], keyspace: 'node'});
client.execute('SELECT * FROM accounts WHERE email_accounts=?', [ email , password ], _checkPassword );
}
this.perform = perform;
}
util.inherits(Login, eventEmitter);
module.exports = Login;
It looks like the 'this' in this.emit() lines refer to the CQL object. In my understanding, the cql drivers calls the callback whenever data is ready, so the 'this' object is not a Login object anymore. Am I wrong ? How can I have the emit() work properly ?
Thank you
Near the top of your Login object definition, create a handle to this
via:
var _this = this;
Then use _this
instead of this
when calling the emit
method. _checkPassword
is said to close over _this
(which it can't do over this
).
The this
keyword is a special snowflake which is dynamically bound when the enclosing function is invoked; if you pass a function that refers to this
off to other functions, make sure this
is what you think it is. :)