Using NodeJS/ES6 I have created a MongoDB connector class.
class DBClient {
constructor(host, port) {
this.host = host;
this.port = port
this.dbConnection = null;
}
buildConnectionString() {
return 'mongodb://' + this.host + ':' + this.port;
}
connect() {
var connectionString = this.buildConnectionString();
console.log('[MongoDB] - Connecting to instance # ' + connectionString);
var DBConnection = MongoClient.connect(connectionString, function(error, db) {
if (error) {
console.log('[MongoDB] - Error connecting to instance');
console.log(error);
}
else {
console.log('[MongoDB] - Connection Successful');
this.dbConnection = db;
}
});
}
}
Which is then being created in a different file like so
var client = new DBClient('127.0.0.1', '1337');
client.connect();
When the database is connected to, NodeJS crashes when it reaches this.dbConnection = db;, stating TypeError: Cannot set property 'dbConnection' of undefined.
I'm pretty sure it has something to do with being used in a callback, which is screwing up the scope. How can I get around this though? Wouldn't any operation from the callback scope be isolated and unable to reference this?
Also, as a side question, is this a bad code practice to initialize a null property like I'm doing in the constructor? If so, what would be a more proper way of doing it?
Indeed if you want to keep your scope use lambda instead like :
var DBConnection = MongoClient.connect(connectionString, (error, db) =>
{
...
});
if you have to keep your function because of your transpilation settings or the lib does not support lambda, save your scope in a variable like :
var self = this;
var DBConnection = MongoClient.connect(connectionString, function(error, db)
{
... self.dbConnection = db;
});
Related
I am writing an AWS Lambda function in JavaScript (Node.js) that interacts with CodeCommit through the AWS SDK.
The communication between the services works as expected, I am getting data within the CodeCommit function, but the issue I am facing appears when I want to use this data outside of the function.
I have tried two approaches:
1. Global Variable
Code:
var aws = require('aws-sdk');
var codecommit = new aws.CodeCommit({ apiVersion: '2015-04-13' });
var repoName = ''; // Declared my global variable here
exports.handler = function(event, context) {
var commitId = "69a5f8eeba340d71ba41b8f20d77cc20b301ff52"
var repository = "my-repository"
var params = {
repositoryName: repository
};
codecommit.getRepository(params, function(err, data) {
if (err) {
console.log(err);
var message = "Error getting repository metadata for repository " + repository;
console.log(message);
context.fail(message);
} else {
console.log('Repository Name:', data.repositoryMetadata.repositoryName); // Shown with data
repoName = data.repositoryMetadata.repositoryName; // Setting the variable
console.log('Account Id:', data.repositoryMetadata.accountId); // Shown with data
}
});
console.log(repoName); // Shown as blank in the output
};
Output:
The last written "console.log" is the first to print in the execution results, but shows blank. The two other console.log (within the functions) are then printed, and they show the data.
2. Function
Code:
var aws = require('aws-sdk');
var codecommit = new aws.CodeCommit({ apiVersion: '2015-04-13' });
exports.handler = function(event, context) {
var commitId = "69a5f8eeba340d71ba41b8f20d77cc20b301ff52"
var repository = "my-repository"
var repoData = getRepository(repository)
console.log('Repository Name:', repoData.repositoryName);
console.log('Account Id:', repoData.accountId);
};
function getRepository(repository) {
var params = {
repositoryName: repository
};
codecommit.getRepository(params, function(err, data) {
if (err) {
console.log(err);
var message = "Error getting repository metadata for repository " + repository;
console.log(message);
context.fail(message);
} else {
var repoData = {};
repoData.repositoryName = data.repositoryMetadata.repositoryName;
repoData.accountId = data.repositoryMetadata.accountId;
console.log(repoData); // Shows output in execution results when lines 11 & 12 are commented
return repoData;
}
});
}
Output:
{
"errorType": "TypeError",
"errorMessage": "Cannot read property 'repositoryName' of undefined",
"trace": [
"TypeError: Cannot read property 'repositoryName' of undefined",
" at Runtime.exports.handler (/var/task/index.js:57:46)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}
Conclusion
None of those approaches worked. The data is always visible within the function but never outside of it. I suspect that the code outside of the function executes before the function itself, and I wonder if I could make the code to wait that the function has been executed before doing the console.log (and other actions after it). Or maybe I am wrong on another level?
You are using a callback model, in which case the console.log in the first example is being hit before the code in the callback. A better option would be to use async/await.
var aws = require('aws-sdk');
var codecommit = new aws.CodeCommit({ apiVersion: '2015-04-13' });
var repoName = ''; // Declared my global variable here
exports.handler = async function(event, context) {
var commitId = "69a5f8eeba340d71ba41b8f20d77cc20b301ff52"
var repository = "my-repository"
var params = {
repositoryName: repository
};
var data = await codecommit.getRepository(params).promise();
console.log('Repository Name:', data.repositoryMetadata.repositoryName); // Shown with data
repoName = data.repositoryMetadata.repositoryName; // Setting the variable
console.log('Account Id:', data.repositoryMetadata.accountId); // Shown with data
console.log(repoName);
};
Notice that I'm not catching the error here, but if you wanted to you can use a try/catch block. Just be sure you throw a new error in that case if you want the function to fail.
Im trying to send an answer to my websocket-server from a component which does not contain the websocket. My Websocket server looks like this:
componentDidMount() {
var ws = new WebSocket('ws:// URL');
ws.onmessage = this.handleMessage.bind(this);
...
}
How can I pass the "var ws" to another class or component. Or is it possible to make the websocket globally accessable?
Thank you very much for any help!
I found a solution with help from this question in stackoverflow:
visit:
React native: Always running component
I created a new class WebsocketController like this:
let instance = null;
class WebsocketController{
constructor() {
if(!instance){
instance = this;
}
this.ws = new WebSocket('ws://URL');
return instance;
}
}
export default WebsocketController
And then in my other class where I need my websocket I just called it like this:
let controller = new WebsocketController();
var ws = controller.ws;
websocket connection
keep this code in some file, name it with .js extenstion. ex: websocket.js
var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer({port:8100});
wss.broadcast = function broadcast(msg) {
console.log(msg);
wss.clients.forEach(function each(client) {
client.send(msg);
});
};
wss.on('connection', function connection(ws) {
// Store the remote systems IP address as "remoteIp".
var remoteIp = ws.upgradeReq.connection.remoteAddress;
// Print a log with the IP of the client that connected.
console.log('Connection received: ', remoteIp);
ws.send('You successfully connected to the websocket.');
ws.on('message',wss.broadcast);
});
In your app/website side. create .js file. Ex: client.js
var SERVER_URL = 'ws://127.0.0.1:8100';
var ws;
function connect() {
//alert('connect');
ws = new WebSocket(SERVER_URL, []);
// Set the function to be called when a message is received.
ws.onmessage = handleMessageReceived;
// Set the function to be called when we have connected to the server.
ws.onopen = handleConnected;
// Set the function to be called when an error occurs.
ws.onerror = handleError;
}
function handleMessageReceived(data) {
// Simply call logMessage(), passing the received data.
logMessage(data.data);
}
function handleConnected(data) {
// Create a log message which explains what has happened and includes
// the url we have connected too.
var logMsg = 'Connected to server: ' + data.target.url;
// Add the message to the log.
logMessage(logMsg)
ws.send("hi am raj");
}
function handleError(err) {
// Print the error to the console so we can debug it.
console.log("Error: ", err);
}
function logMessage(msg) {
// $apply() ensures that the elements on the page are updated
// with the new message.
$scope.$apply(function() {
//Append out new message to our message log. The \n means new line.
$scope.messageLog = $scope.messageLog + msg + "\n";
});
}
Please let me know if you face any issue with this code
I'm buidling an app with Node anb Mongodb Native. I'm working on a db module which i can require and call in other modules so that I end up using just one connection. The module db.js started out with this code:
var _db = null;
var getDb = module.exports.getDb = function(callback) {
if (_db) {
console.log('_db returned');
return callback(null, _db);
}
MongoClient.connect('mongodb://localhost:' + config.db.port + '/' + config.db.name, {native_parser: true}, function (err, db) {
if (err) return callback(err);
console.log('_db created');
_db = db;
callback(err, _db);
});
};
In my other modules that need a db connection I do this
db.getDb(function (err, connection) {
// Do something with connection
});
It works fine. But an unpleasant problem is that if my code would call getDb multiple times in a very short time span, I would end up with several copies of a connection. Like if I do my db.js requirements and getDb calls at the very beginning of all modules that need a db connection
I'm now thinking about controlling the calls to getDb by queuing them, so that only the absolute first call will create a connection and save it in _db. All later calls will get the created connection _db in return. I believe Async queue will help me with this...
The problem is that i dont understand how I write this with Async queue. The documentation is a little bit vague, and i dont find any better examples online. Maybe you can give me some hints. This is what i got so far...
var dbCalls = async.queue(function (task, callback) {
if (_db) {
console.log('_db returned');
return callback(null, _db);
}
MongoClient.connect('mongodb://localhost:' + config.db.port + '/' + config.db.name, {native_parser: true}, function (err, db) {
if (err) return callback(err);
console.log('Connected to mongodb://localhost:' + config.db.port + '/' + config.db.name);
_db = db;
callback(null, _db);
});
}, 1);
// I guess this .push() must be the exposed (exported) API for other modules to get a connection, but how do I return it to them,
dbCalls.push(null, function (err) {
console.log('finished processing foo');
});
dbCalls.push(null, function (err) {
console.log('finished processing bar');
});
I dont understand the object passed as first argument to .push() What should i use if for? Right now its null How do I pass on the connection and possible error all the way out to the module that made the call?
A quick and dirty solution without async.queue:
var _db = null;
var _err = null;
var _queue = [];
var _pending = false;
var getDb = module.exports.getDb = function(callback) {
if (_err || _db) {
console.log('_db returned');
return callback(_err, _db);
} else if (_pending) { // already a connect() request pending
_queue.push(callback);
} else {
_pending = true;
_queue.push(callback);
MongoClient.connect(..., function (err, db) {
_err = err;
_db = db;
_queue.forEach(function(queuedCallback) {
queuedCallback(err, db);
});
});
};
I have a basic Node JS server which is designed to be used as an API, I've created a log and database module and I've started adding other modules to deal with different request types.
I'm using Express.js and node-mysql
When I visit /v1/group I get the following error -
TypeError: Cannot read property 'database' of undefined
at Group.getAll (C:\code\javascript\node\api\api\v1\groups.js:12:23)
at callbacks (C:\code\javascript\node\api\node_modules\express\lib\router\index.js:161:37) ...
So I guess after recieving a request and calling group.getAll() that this is undefined but I don't understand why, is there a way to set this or have I structured my application all wrong?
sever.js
"use strict";
var Express = require('express');
var Log = require('./database/log');
var Database = require('./database/database');
var dbConfig = require('./dbconfig.json');
var Group = require('./api/v1/groups');
//Init express
var app = new Express();
//Init log and database
var log = new Log();
var database = new Database(dbConfig, log);
var initCallback = function() {
//Init routes
var group = new Group(database, log);
//Group routes
app.get('/v1/group', group.getAll);
app.get('/v1/group/:id', group.getByID);
app.listen(3000);
log.logMessage("INFO", "Listening on port 3000");
};
//Test database connection
database.getConnection(function(err, connection) {
if (err) {
log.logMessage("FATAL", "Error connecting to database, check database is running and the dbconfig.json file is present and correct.");
process.exit(1);
}
connection.end();
initCallback();
});
database.js
"use strict";
var mysql = require('mysql');
var Database = function(dbConfig, log) {
this.connected = false;
this.log = log;
this.log.logMessage("INFO", "Connecting to database with: Host - " + dbConfig.dbhost + ", Database port - " + dbConfig.dbport + ", Database name - " + dbConfig.dbname + ", User " + dbConfig.dbuser + ", Password length - " + dbConfig.dbpass.length);
this.pool = mysql.createPool({
host : dbConfig.dbhost,
user : dbConfig.dbuser,
port: dbConfig.dbport,
password : dbConfig.dbpass,
database: dbConfig.dbname
});
};
Database.prototype.getConnection = function() {
var args = arguments;
return this.pool.getConnection.apply(this.pool, arguments);
};
module.exports = Database;
groups.js
"use strict";
var Group = function(database, log) {
this.database = database;
this.log = log;
};
Group.prototype.getAll = function(req, res) {
console.log(this); // --> undefined
var query = 'SELECT * FROM invgroups WHERE published = 1';
this.database.getConnection(function(err, connection) { // --> error line
if (err) { res.send(500, "Database error"); }
connection.query(query, function(err, results) {
if (err) { res.send(500, "Database error"); }
res.send(results);
});
connection.end();
});
};
Group.prototype.getByID = function(req, res) {
console.log(this);
res.send({name: "Group Item 1"});
};
module.exports = Group;
You need to properly bind the function.
app.get('/v1/group', group.getAll);
only passes the getAll function as a handler, but the function itself has no concept of this. this is decided based on the context that is bound, or based on how the function is called. This blog post is useful for understanding how function context works.
app.get('/v1/group', group.getAll.bind(group));
I'd like to create a model to handle everything related to users, starting with a findOne() function.
app.js:
var u = new User(client);
u.findOne(function(error, user) {
console.log(error, user);
});
models/User.js:
var User = function (client) {
this.client = client
};
User.prototype.findOne = function (id, callback) {
client.connect();
client.get('testkey', function(error, result) {
var test = "hello#world.com";
callback(null, test);
client.close();
});
};
module.exports = User;
node.js complains findOne() would be undefined.
What's the correct way of creating such models and providing them with objects, like database pools etc.?
Your code contains various errors:
You do not use new when creating the instance
You mixed a function with the object literal syntax:
var User = function (client) {
client: client
};
You want this.client = client; instead. Right now the function body does nothing as it just defines a label called client does nothing with the variable client.
I would suggest you to search for an existing ORM for node.js instead of trying to write one on your own.