I am working on really simple node.js projects to better understand its callback functioning.
Suppose I have a login "system" like this one here:
if( req.query["username"] == "john" && req.query["password"] == "smith" ) {
req.session.gatekeeper = req.query["username"];
res.end( "succesfully logged in" );
} else { res.end( "wrong username or password" ); }
so far, so easy. Now suppose that instead of simply having "john:smith", I'd have it stored on redis. With PHP I'd have done it this way:
if( $r->get("$usernameIn") == $passwordIn ) {
$_SESSION['gatekeeper'] = $usernameIn;
echo "succesfully logged in";
}
but now, by having a look at the redis documentation (https://github.com/mranney/node_redis/) for node, I see that the get command is like this:
client.get("foo", function(err, reply) {
console.log(reply);
});
It is really complicated for me to understand how to "structure" the first code I provided with this last one.
Any help? Thanks in advance.
Nearly everything in node.js is asynchronous. So, nearly every method that is called, you must specify a callback method to handle the result of the method call. Normally, the callback function takes two parameters: error, result. So it is up to you to check for the error and then handle the result. There are other node modules that will allow for cleaner code (node promises), but that is out of scope for your question.
As a disclaimer I have not used redis before, so my code example below is based on your code example from above and a quick overview of the redis node.js module.
var redis = require("redis"),
client = redis.createClient();
client.on("connect", function () {
client.get(userName, function(err,returnedPassword){
if(password === returnedPassword) {
req.session.gatekeeper = req.query["username"];
res.end( "succesfully logged in" );
} else {
res.end( "wrong username or password" );
}
});
});
Related
I've read the feathersjs documentation, but after doing a find method in a service I realized that if I don't give any query parameters, the service returns all the data, which is something I don't want. How can I define a hook to validate that there are at least one query parameter in order to proceed; otherwise, send back a 403 error (bad request).?
I have doubts in the way to do it I tried this:
app.service('myService')
.before(function(hook) {
if (hook.params.query.name === undefined){
console.log('There is no name, throw an error!');
}
})
.find({
query: {
$sort: {
year: -1
}
}
})
And I tried in hook file on hooks this (that seemed really desperate & | stupid):
function noparams (hook) {
if (hook.params.query.name === undefined){
console.log('There is no name, throw an error!');
}
}
module.exports = {
before: {
find: [ noparams(this) ] ...
}
}
but it does not compile (I don't know what to send as a parameter there), and the examples seemed to be for pre 2.0 version and on top of that the code I found seemed to be in the app.js, but all is differently coded using feathers-cli, so the examples, even in the book, aren't against the scaffolded version, which is confusing because they shows the code in a different file were should be.
Thanks.
I ended using a before hook, so the code used is this:
const errors = require('feathers-errors');
module.exports = function () {
return function (hook) {
if(hook.method === 'find'){
if (hook.params.query.name === undefined || hook.params.query.length == 0){
throw new errors.BadRequest('Invalid Parameters');
}else{
return hook;
}
}
}
};
If have used feathers-cli to generate your application (feathers v2.x) you don't need to do anything else. If is an earlier version you maybe need to add the Express error handler and it is pointed out in the documentation|Errors|REST.
Thanks.
Followup from this question > Stopping response if document isn't found since it was recommended I use Promise.
So basic premise, I want node to return "Can't find ID" message if we can't find the id in our database.
v1.post("/", function(req, res) {
// If the project_id isn't provided, return with an error.
if ( !("project_id" in req.body) ) {
return res.send("You need to provide Project ID");
}
// Check if the Project ID is in the file.
helper.documentExists( ProjectsData, {project_id: req.body.project_id} )
.then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
var gameDataObj = req.body;
GameData.addGameId(gameDataObj, function (err, doc) {
if (err) {
if (err.name == "ValidationError") {
return res.send("Please send all the required details.");
}
throw err;
};
res.json(doc);
})
};
});
});
And helper.documentExists
module.exports = {
documentExists: function(collection, query) {
return collection.count( query ).exec();
},
};
But the script continues to run after this and prints the "required data not found".
Output:
required data not found
1
I am using native ES6 Promises.
var mongoose = require("mongoose");
mongoose.Promise = global.Promise;
EDIT: Included the entire get route. (will fix those throw err later)
#######POINT 1#########
ProjectsData.count( {project_id: req.body.project_id} )
.then(function(c) {
#######POINT 3#########
if ( c == 0 ) {
console.log("1");
return res.send("The provided Project Id does not exist in our database.");
console.log("2");
}
});
#######POINT 2#########
//some other logic
console.log("required data not found");
Following async workflow: after POINT 1, the promise is created and your handler is attached. Now POINT 2 will continue, while (at some future clock the promise is resolved and you reach POINT 3.
With my limited understanding of your workflow/purpose I'd say simply put POINT 2 code in the else{} of the if at POINT 3 (as you rightly guessed in the comments).
EDIT: thanks to #jfriend00 for pointing out a serious mistake in the previous version of my answer.
Your code essentially results in this:
ProjectsData.count().then(...);
console.log("required data not found");
So, of course the second console.log() is going to run and print. Nothing that happens in the .then() handler runs until long after the console.log() has already run. And, even then, it can't stop other code from running. Promises don't make the interpreter "wait". They just provide structure for you to coordinate your asynchronous operations.
If you want to branch with promises, then you have to branch inside the .then() handler, not after it.
You don't show enough of the rest of what you're doing to know how to recommend a complete solution. We need to see the rest of your request in order to help you with the proper branching based on asynchronous results.
You probably need something like this:
ProjectsData.count( {project_id: req.body.project_id} ).then(function(c) {
if ( c == 0 ) {
return res.send("The provided Project Id does not exist in our database.");
} else {
// put other logic here
}
}).catch(function(err) {
// handle error here
});
I am having difficulty using Fibers/Meteor.bindEnvironment(). I tried to have code updating and inserting to a collection if the collection starts empty. This is all supposed to be running server-side on startup.
function insertRecords() {
console.log("inserting...");
var client = Knox.createClient({
key: apikey,
secret: secret,
bucket: 'profile-testing'
});
console.log("created client");
client.list({ prefix: 'projects' }, function(err, data) {
if (err) {
console.log("Error in insertRecords");
}
for (var i = 0; i < data.Contents.length; i++) {
console.log(data.Contents[i].Key);
if (data.Contents[i].Key.split('/').pop() == "") {
Projects.insert({ name: data.Contents[i].Key, contents: [] });
} else if (data.Contents[i].Key.split('.').pop() == "jpg") {
Projects.update( { name: data.Contents[i].Key.substr(0,
data.Contents[i].Key.lastIndexOf('.')) },
{ $push: {contents: data.Contents[i].Key}} );
} else {
console.log(data.Contents[i].Key.split('.').pop());
}
}
});
}
if (Meteor.isServer) {
Meteor.startup(function () {
if (Projects.find().count() === 0) {
boundInsert = Meteor.bindEnvironment(insertRecords, function(err) {
if (err) {
console.log("error binding?");
console.log(err);
}
});
boundInsert();
}
});
}
My first time writing this, I got errors that I needed to wrap my callbacks in a Fiber() block, then on discussion on IRC someone recommending trying Meteor.bindEnvironment() instead, since that should be putting it in a Fiber. That didn't work (the only output I saw was inserting..., meaning that bindEnvironment() didn't throw an error, but it also doesn't run any of the code inside of the block). Then I got to this. My error now is: Error: Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.
I am new to Node and don't completely understand the concept of Fibers. My understanding is that they're analogous to threads in C/C++/every language with threading, but I don't understand what the implications extending to my server-side code are/why my code is throwing an error when trying to insert to a collection. Can anyone explain this to me?
Thank you.
You're using bindEnvironment slightly incorrectly. Because where its being used is already in a fiber and the callback that comes off the Knox client isn't in a fiber anymore.
There are two use cases of bindEnvironment (that i can think of, there could be more!):
You have a global variable that has to be altered but you don't want it to affect other user's sessions
You are managing a callback using a third party api/npm module (which looks to be the case)
Meteor.bindEnvironment creates a new Fiber and copies the current Fiber's variables and environment to the new Fiber. The point you need this is when you use your nom module's method callback.
Luckily there is an alternative that takes care of the callback waiting for you and binds the callback in a fiber called Meteor.wrapAsync.
So you could do this:
Your startup function already has a fiber and no callback so you don't need bindEnvironment here.
Meteor.startup(function () {
if (Projects.find().count() === 0) {
insertRecords();
}
});
And your insert records function (using wrapAsync) so you don't need a callback
function insertRecords() {
console.log("inserting...");
var client = Knox.createClient({
key: apikey,
secret: secret,
bucket: 'profile-testing'
});
client.listSync = Meteor.wrapAsync(client.list.bind(client));
console.log("created client");
try {
var data = client.listSync({ prefix: 'projects' });
}
catch(e) {
console.log(e);
}
if(!data) return;
for (var i = 1; i < data.Contents.length; i++) {
console.log(data.Contents[i].Key);
if (data.Contents[i].Key.split('/').pop() == "") {
Projects.insert({ name: data.Contents[i].Key, contents: [] });
} else if (data.Contents[i].Key.split('.').pop() == "jpg") {
Projects.update( { name: data.Contents[i].Key.substr(0,
data.Contents[i].Key.lastIndexOf('.')) },
{ $push: {contents: data.Contents[i].Key}} );
} else {
console.log(data.Contents[i].Key.split('.').pop());
}
}
});
A couple of things to keep in mind. Fibers aren't like threads. There is only a single thread in NodeJS.
Fibers are more like events that can run at the same time but without blocking each other if there is a waiting type scenario (e.g downloading a file from the internet).
So you can have synchronous code and not block the other user's events. They take turns to run but still run in a single thread. So this is how Meteor has synchronous code on the server side, that can wait for stuff, yet other user's won't be blocked by this and can do stuff because their code runs in a different fiber.
Chris Mather has a couple of good articles on this on http://eventedmind.com
What does Meteor.wrapAsync do?
Meteor.wrapAsync takes in the method you give it as the first parameter and runs it in the current fiber.
It also attaches a callback to it (it assumes the method takes a last param that has a callback where the first param is an error and the second the result such as function(err,result).
The callback is bound with Meteor.bindEnvironment and blocks the current Fiber until the callback is fired. As soon as the callback fires it returns the result or throws the err.
So it's very handy for converting asynchronous code into synchronous code since you can use the result of the method on the next line instead of using a callback and nesting deeper functions. It also takes care of the bindEnvironment for you so you don't have to worry about losing your fiber's scope.
Update Meteor._wrapAsync is now Meteor.wrapAsync and documented.
I'm having alot of trouble getting couchdb's response handlers to do anything useful with node.js and now.js. My aim is to call back to a client or group with certain information that's been found and also sent from a client.
everyone.now.login = function(){
var username = this.now.lusername;
var password = this.now.lpassword;
var test;
db.get(this.now.lusername, function (err, doc, test, username) {
if (err) {
console.log(+ username + " doesn't exist!");
} else {
console.log('Found user!');
console.log(doc);
test = 1;
}
});
console.log(test);
}
I'm using test as an example here. I declare it outside db.get, assign a value inside to feed back but when console.log is run outside it doesn't have a value.
Am I simply not getting it or is something wrong?
(I do release I can use doc.(whatever value) but there is a particular variable i wanted to pass on back to the client outside of this call)
The test variable will not have a value in the console.log because the CouchDB response has not arrived yet. Your login function executes the following steps.
Set some variables (username, password, and test is undefined)
Begin a CouchDB fetch (for this.now.lusername) with a function assigned to run when it is complete
Run console.log(test) which is still undefined
The CouchDB response completes, and your function from step 2 runs
What I do for things like this is use function calls.
everyone.now.login = function(){
var username = this.now.lusername;
var password = this.now.lpassword;
db.get(this.now.lusername, function (err, doc, test, username) {
if (err) {
console.log(+ username + " doesn't exist!");
} else {
console.log('Found user!');
console.log(doc);
login_complete(doc);
}
});
function login_complete(doc) {
console.log('The login finished! Doc is ' + doc)
}
}
This way the code still looks correct (the "story" of the code mostly flows from top to bottom), but it also executes correctly (the login_complete() function runs only after the login is actually complete).
I've been reading about node.js recently (like many others). I find interesting for some use cases, but am a bit struggling to understand the inner workings, specifically the interaction between closure functions and the process flow of the code.
Let's say I have a function which accepts a key-value array. The function must check that the values follow certain data-quality guidelines (for example some keys must have a value, other keys must have numbers as values etc) before storing the data somewhere (for the purpose of this question let's assume data validation has to be done in the application itself).
In "regular" developments models I'd write something like this:
resultName = validateName(data.name)
resultAddress = validateAddress(data.address)
resultID = validateID(data.id)
if (resultName && resultAddress && resultID) {
store(data)
else {
sendErrorToUser(data)
}
Get the results of the validations, and either explain the error(s) to the user or store data and return some kind of confirmation. The flow is very clear.
The way I understand node.js, the way to do this would be to delegate the validations to a different function (to avoid waiting for each validation to finish), and supply two callback functions to the functions which validate the chunks of data:
* a callback to call when validation is successful
* a callback to call when validation fails
It's easy to now return to the user with a "please wait" message, but I have to wait for all validations to clear (or fail) before storing the data or explaining the problem to the user. As a simple way to figure out if all the validations are done I thought of using a variable that counts the number of functions that called the callback, and emitting a "validation complete" event to store the validated data (or get back to the user with any errors). Or, alternatively, emit an event after each validation is complete and in that event's code check if all validations are complete before emitting the "store" / "error" events.
My question is -- am I approaching this correctly? Or is there a more suitable way to do these kinds of things with node.js (or similar event-based systems).
Thank you!
Alon
Are your validations asynchronous? If they are not you can use the code you posted, the "regular" one.
If the validations are asynchronous (checking uniqueness of an email for instance), you need to provide callbacks:
var validateUniqueEmail = function (data, callback) {
db.find({email: data.email}, function (err, result) {
callback(err, result === null);
})
};
var validateAndStore = function (data, callback) {
asyncValidation(data, function (err, is_valid) {
if (err) {
callback(err, null);
} else if (!is_valid) {
callback('Email is not unique', null);
} else {
db.store(data, callback);
}
});
}
The code above can be simplified a lot by using some validator or ORM modules already existing
example: mongolia validator module.
Let's go. Basically, what you want to do is something along the lines of :
var validate(data, cb){
var allOk = true;
for(var key in data){
allOk = allOk && validate[key](data.key); // validator depends on the key
}
if (allOk) cb(null, data); else cb(new Error "bleh");
}
This could be done the following way (note how we pass the failed keys as the first (error) argument to the callback):
var validate(data, cb){
var status = {true:[], false:[]},
total = Object.keys(data).length,
done = 0;
for (var key in data)
(function(key){
validate[key](data[key], function(ok){
status[ok].push(key);
if (++done == total){
status[false].length ? cb(status[false]) : cb(null);
}
});
})(key);
}
Which you can use this way :
validate(data, function(failures){
if (failures){
// tell the user the input does not validate on the keys stored in failures
} else {
// all ok
store(data);
}
});
Correct me if I'm wrong, but I think what you're asking is how to handle the response from multiple asynchronous calls.
Here's how I do it (using your validation example):
var result = {};
function isAllDataAvailable() {
return result.name !== undefined
&& result.address !== undefined
&& result.id !== undefined;
}
function callback(error) {
if (error) {
showError(error);
// terminate here (?)
return;
}
if (isAllDataAvailable()) {
showOutput();
}
}
validateName(data, callback);
validateAddress(data, callback);
validateEmail(data, callback);
The key here is the result object, which starts out as empty. As each field gets validated, it gets added to the result object (by the validation functions, which I've left out in the above snippet). I've used a single callback method, but you could have multiple, say callbackName, callbackAddress, etc. The validation results are processed only if and when the result object has been fully populated, which is checked in isAllDataAvailable.
Hope this helps.
Consider using: https://github.com/asaf/nodejs-model
It will make your life much easier when dealing with validators.