I am very new to Node Js and Concept of Callback Mechanism , I have working code to authenticate the User based on the LDAP using ldapjs but I wanted to know the mechanism how its working with respect to data flow and callbacks.
In the below code I have few doubts, Can someone help me clarifying
What does it means with cb(err === null, err, res);
When I do console.log with fake_res it shows as true Why its true?
I see some post referring we need use to error as first callback, Is it so?
And Finally I wanted to understand why is the res used in output and authDN are same
And finally how generally callbacks works in NodeJS
Before asking this question I have gone through many forums but couldn't relate with the below code
Link 1
Link 2
Link 3
Link 4
var express = require('express');
var util = require('util');
CircularJSON = require('circular-json');
var router = express.Router();
var ldap = require('ldapjs');
var bodyParser = require('body-parser');
var userNT;
var password;
var app = express();
function authDN(dn, password, cb, res) {
var client = ldap.createClient({
url: 'ldap://localhost:389'
});
client.bind(dn, password, function(err) {
client.unbind();
cb(err === null, err, res);
});
}
function output(fake_res, err, res) {
if (fake_res) {
console.log('success');
res.status(200).send('{"status":"success"}');
} else {
console.log('failure');
res.status(401).send('{"status":"failure"}');
}
}
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({
extended: true
})); // support encoded bodies
router.post('/login', postData);
function postData(req, res) {
userNT = req.body.ntid;
password = req.body.password;
authDN(userNT, password, output, res);
};
module.exports = router;
Okay let us go try to do it step by step :
Here if you see authDN has third parameter cb this is your callback function. Now to trace it back check the value of argument provided to this function authDN when it is called inside postData function, here cb = function output
Now first param of your output is fake_res which is either true or false, this depends on response of client.bind
If it fails you will get some error hence it will go on to be false. Here comes the answer to your question 2 because your credentials seem to be correct always this err is equal to null thus your fake_res is always true.
Answering question 4 it is because it is passed on as param to send response back to the API call you made using router.post
About number 3 it is just more readable and better to use an error first callback, but not necessary.
Related
I've posted an approximation of my node web application below. The original problem I had is that I want to be able to let the client know on post to createentity whether the insert was successful and the id of the inserted entity. However, connection.query having a callback rather than running synchronously, I can't use the entityservice how I'd expect in another language, ie simply returning the result synchronously. There are several solutions, and I'm curious which is the best/common practice re node.js or if there is another I'm not thinking of.
Passing res down to the service, and responding within a callback; seems poor practice
Similarly, passing functions to execute after success/failure to the service; also seems poor practice
Returning a promise from the service and setting res based on resolution/failure; seems like services shouldn't return promises but I'm new to node
Some better method using appropriate features of node.js of which I'm unaware
trying to change the service such that it runs synchronously and just returning the result Other questions/answers have made me leery that this is possible or wise
structure the application some other way
something else?
//app.js
var express = require('express');
var bodyParser = require('body-parser')
var path = require('path');
var EntityService = require('./entityService.js');
var app = express();
var urlencodedParser = bodyParser.urlencoded({ extended: true })
app.post('/createentity', urlencodedParser, function(req, res){
EntityService.createEntity(req.body.name);
res.status(200).json(null);
});
app.listen(3000);
//entityService.js
var mysql = require('mysql');
EntityService = function(){
var connection = mysql.createConnection({
host : CONNECTION_IP,
user : 'root',
password : 'password',
database : 'entitydb'
});
connection.connect();
this.createEntity = function(name){
var record = {name: 'name'};
var query = connection.query('INSERT INTO entity set ?', record, function(error, results, fields ){
//want to return the results.insertId from the service
});
}
}
module.exports = new EntityService();
The correct approach here is option 3 - have your service return a Promise
this.createEntity = name => new Promise((resolve, reject) => {
const query = connection.query('...', { name }, (err, results) => {
if (err) return reject(err);
return resolve(results.map(r => r.insertId));
});
})
If you're on the latest version of Node, I'd go with the asynch/await syntax.
Your service returns a promise,then your calling code can do:
app.post('/createentity', urlencodedParser, async function(req, res){
const entity = await EntityService.createEntity(req.body.name);
res.status(200).json(entity);
});
Nothing important
My first question here on stackoverflow. I've used it for years to find answers, but now I need a bit of guidance. I'm new to node and express and the async way of structuring an app.
Goal - A REST interface with validation and neDB database
I got the following code working. POST a new user is the only route. It's based on many answers and tuts mixed together. I find it hard to scaffold out the logic, to get a structure you can build on.
I'm not sure at all whether this structure is crap or not. Any advice would be appreciated.
Main file is initializing the database, middleware validator, and starting the app.
// rest.js
var express = require('express'),
bodyParser = require('body-parser'),
validator = require('express-validator'),
db = require('./database/db'),
userRouter = require('./routers/users');
db.init();
var app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(validator());
app.use('/api/users', userRouter);
var port = process.env.PORT || 8080;
app.listen(port);
Database
This question/answer made me create the small database module with alterations.
How do you pass objects around node express application?
It doesn't have much attention. Maybe because it's very obvious or maybe not a good answer.
The idea is that whem i get multiple collections, they all get initialized on startup, but I can request a single collection if that's all the module needs, or I can get the entire db object back if another module would require that.
// database/db.js
var nedb = require('nedb');
var db = {};
db.init = function() {
db.users = new nedb({ filename: './database/data/users', autoload: true });
db.users.ensureIndex({ fieldName: 'username', unique: true }, function (err) {});
db.users.ensureIndex({ fieldName: 'email', unique: true }, function (err) {});
};
db.get = function(collection) {
if (collection && db[collection])
return db[collection];
return db;
}
module.exports = db;
Router
I require the User Model here and use the express-validator and sanitizes the request before passing it on to the model, based on a minimalist key schema in the model. I don't have any controllers. If I had (or when I do), I would put the validation there. The router is supposed to send the response and status right?
// routers/users.js
var express = require('express'),
_ = require('lodash'),
User = require('../models/user');
var userRouter = express.Router();
userRouter.route('/')
.post(function(req, res) {
req.checkBody('username', 'Username must be 3-20 chars').len(3,20);
req.checkBody('email', 'Not valid email').isEmail();
req.checkBody('password', 'Password must be 6-20 chars').len(6,20);
var err = req.validationErrors();
if (err) {
res.status(422).send(err);
return;
}
var data = _.pick(req.body, _.keys(User.schema));
User.create(data, function (err, newData) {
if (err) {
res.status(409).send(err);
} else {
res.status(201).send(newData);
}
});
});
module.exports = userRouter;
Model
The model requires the database module and gets the "connection". Is this OK?
// models/user.js
var db = require('../database/db');
var User = function (data) {
this.data = data;
};
User.schema = {
_id: null,
username: null,
email: null,
password: null
};
User.create = function (data, callback) {
db.get('users').insert(data, callback);
};
module.exports = User;
Thanks for reading this far. Now, my question is:
Is there something fundamentally wrong with this setup, concerning the database usage and the validation logic. I know the model looks stupid :)
Bellow is the "users" module of my nodeJS application.
/**
* record.js
*/
var express = require('express'),
router = express.Router();
// Config.
var config = require('../config/config');
// Validator class.
var validator = require('validator');
// User module.
var User = require('../models/user');
// Error middleware.
var errors = require('../middleware/errors');
/**
* --- Crypto. -------------------------------------------
*/
var crypto = require('crypto'),
key = config.secretKey;
var encrypt = function (data) {
var cipher = crypto.createCipher('aes-256-cbc', key);
var crypted = cipher.update(data, 'utf8', 'base64');
crypted += cipher.final('base64');
return crypted;
};
var decrypt = function (data) {
var decipher = crypto.createDecipher('aes-256-cbc', key);
var decrypted = decipher.update(data, 'base64', 'utf8');
decrypted += decipher.final('utf-8');
return decrypted;
};
/**
* --- Register user. ----------------------------------------
*/
router.post('/user', function (req, res, next) {
var email = validator.toString(req.body.email),
password = validator.toString(req.body.password);
if (!validator.isEmail(email)) {
res.locals.validationError = 'isEmail';
return next();
}
if (!validator.isLength(password, 3, 20)) {
res.locals.validationError = {
'type': 'isLength',
'minLength': 3,
'maxLength': 20
};
return next();
}
// Generate a hash, from the passowrd.
var hash = encrypt(password);
User.create({
email: req.body.email,
password: hash
}, function (err, user) {
if (err) {
res.locals.dbError = err;
return next();
}
// Prevent exposing sensitive data.
var data = {
'id': user._id,
'email': user.email
};
// HTTP 201 - Created.
return res.status(201).send(data);
});
}, errors);
module.exports = router;
I believe in best practices, which leads me to the following questions.
As you can see, I use dependency injection to require several other modules which the "router" requires. (config, validator, etc.).
I will basically need to require the **validator module in every section of the app, and I am wondering if I can require it once globally and just reference it where I need it.**
Basically, is requiring a module inn different modules of an application the way to go?
I use a "error" middleware, you can see it in action after the POST request.
If I'll have several HTTP requests in this module, is there a way I can inform express to use the middleware after each HTTP request, from a single place?
Something like the following, just that I want the middle ware to execute after the request, not before:
app.all('*', errors)
Right not the middleware executes before.
What do you think of the coding style of the module? How would you refactor it? What would you change? I am just learning NodeJS, this is my first server side API and I want to get good habits.
I will basically need to require the validator module in every section of the app, and I am wondering if I can require it once globally and just reference it where I need it.
Yes, you can, with global.validator = require('validator');. However, this is not a best practice, it's a worst practice. The node practice of explicit dependency declaration is favored as it is more readable, easier to track during refactoring, and fails earlier when things go wrong. We have a huge body of browser code as evidence that splitting a project into module files makes things better and declaring each module's dependencies makes things better, so don't revert to the old browser style of a single concatenated file that gets dependencies as globals.
I use a "error" middleware, you can see it in action after the POST request. If I'll have several HTTP requests in this module, is there a way I can inform express to use the middleware after each HTTP request, from a single place?
express has a built-in error handling middleware pattern you should read up on and use instead of this ad-hoc scheme you have built. To use it, pass an error to next:
if (err) {
return next(err);
}
And code your error middleware as a 4-parameter middleware added after your main routes:
app.use(function error(err, req, res, next) {...});
I'm using a basicAuth middleware in my app, and it works.
But then, in my routes functions, I would like to get the login that was used by the user to authenticate. Assuming req is my request variable, this was supposed to be in req.remoteUser (and later in req.user).
But currently both are set to 'true'. I check that the middleware is used before calling app.use(app.router), so the req request should be populated ! I also use a bodyParser on the line right after basicAuth, and it populates the request correctly.
Nothing much on google, only one issue in express github saying that now it works and both req.user and req.remoteUser have the value.
One needs to provide the username to the callback function (even if the calling code obviously has it in it's context) if we want req.user to be set.
So intead of doing this (as the great tutorial I followed said) :
var express = require('express');
var app = express();
// Authenticator
app.use(express.basicAuth(function(user, pass, callback) {
var result = (user === 'testUser' && pass === 'testPass');
callback(null /* error */, result);
}));
app.get('/home', function(req, res) {
res.send('Hello World');
});
app.listen(process.env.PORT || 8080);
One must change the function into :
app.use(express.basicAuth(function(user, pass, callback) {
if (user === 'testUser' && pass === 'testPass') {
callback(null /* error */, user);
} else {
callback(null, null);
}
}));
And for those wondering, yes that means we can't have a user whose name is the empty String (else it will be interpreted as false by express), which seems a shame.
I am using express to make a web app in node.js. This is a simplification of what I have:
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
app.get('/', function(req, res) {
// Prepare the context
res.render('home.jade', context);
});
app.post('/category', function(req, res) {
// Process the data received in req.body
res.redirect('/');
});
My problem is the following:
If I find that the data sent in /category doesn't validate, I would like pass some additional context to the / page. How could I do this? Redirect doesn't seem to allow any kind of extra parameter.
There are a few ways of passing data around to different routes. The most correct answer is, of course, query strings. You'll need to ensure that the values are properly encodeURIComponent and decodeURIComponent.
app.get('/category', function(req, res) {
var string = encodeURIComponent('something that would break');
res.redirect('/?valid=' + string);
});
You can snag that in your other route by getting the parameters sent by using req.query.
app.get('/', function(req, res) {
var passedVariable = req.query.valid;
// Do something with variable
});
For more dynamic way you can use the url core module to generate the query string for you:
const url = require('url');
app.get('/category', function(req, res) {
res.redirect(url.format({
pathname:"/",
query: {
"a": 1,
"b": 2,
"valid":"your string here"
}
}));
});
So if you want to redirect all req query string variables you can simply do
res.redirect(url.format({
pathname:"/",
query:req.query,
});
});
And if you are using Node >= 7.x you can also use the querystring core module
const querystring = require('querystring');
app.get('/category', function(req, res) {
const query = querystring.stringify({
"a": 1,
"b": 2,
"valid":"your string here"
});
res.redirect('/?' + query);
});
Another way of doing it is by setting something up in the session. You can read how to set it up here, but to set and access variables is something like this:
app.get('/category', function(req, res) {
req.session.valid = true;
res.redirect('/');
});
And later on after the redirect...
app.get('/', function(req, res) {
var passedVariable = req.session.valid;
req.session.valid = null; // resets session variable
// Do something
});
There is also the option of using an old feature of Express, req.flash. Doing so in newer versions of Express will require you to use another library. Essentially it allows you to set up variables that will show up and reset the next time you go to a page. It's handy for showing errors to users, but again it's been removed by default. EDIT: Found a library that adds this functionality.
Hopefully that will give you a general idea how to pass information around in an Express application.
The easiest way I have found to pass data between routeHandlers to use next() no need to mess with redirect or sessions.
Optionally you could just call your homeCtrl(req,res) instead of next() and just pass the req and res
var express = require('express');
var jade = require('jade');
var http = require("http");
var app = express();
var server = http.createServer(app);
/////////////
// Routing //
/////////////
// Move route middleware into named
// functions
function homeCtrl(req, res) {
// Prepare the context
var context = req.dataProcessed;
res.render('home.jade', context);
}
function categoryCtrl(req, res, next) {
// Process the data received in req.body
// instead of res.redirect('/');
req.dataProcessed = somethingYouDid;
return next();
// optionally - Same effect
// accept no need to define homeCtrl
// as the last piece of middleware
// return homeCtrl(req, res, next);
}
app.get('/', homeCtrl);
app.post('/category', categoryCtrl, homeCtrl);
I had to find another solution because none of the provided solutions actually met my requirements, for the following reasons:
Query strings: You may not want to use query strings because the URLs could be shared by your users, and sometimes the query parameters do not make sense for a different user. For example, an error such as ?error=sessionExpired should never be displayed to another user by accident.
req.session: You may not want to use req.session because you need the express-session dependency for this, which includes setting up a session store (such as MongoDB), which you may not need at all, or maybe you are already using a custom session store solution.
next(): You may not want to use next() or next("router") because this essentially just renders your new page under the original URL, it's not really a redirect to the new URL, more like a forward/rewrite, which may not be acceptable.
So this is my fourth solution that doesn't suffer from any of the previous issues. Basically it involves using a temporary cookie, for which you will have to first install cookie-parser. Obviously this means it will only work where cookies are enabled, and with a limited amount of data.
Implementation example:
var cookieParser = require("cookie-parser");
app.use(cookieParser());
app.get("/", function(req, res) {
var context = req.cookies["context"];
res.clearCookie("context", { httpOnly: true });
res.render("home.jade", context); // Here context is just a string, you will have to provide a valid context for your template engine
});
app.post("/category", function(req, res) {
res.cookie("context", "myContext", { httpOnly: true });
res.redirect("/");
}
use app.set & app.get
Setting data
router.get(
"/facebook/callback",
passport.authenticate("facebook"),
(req, res) => {
req.app.set('user', res.req.user)
return res.redirect("/sign");
}
);
Getting data
router.get("/sign", (req, res) => {
console.log('sign', req.app.get('user'))
});
we can use express-session to send the required data
when you initialise the app
const express = require('express');
const app = express();
const session = require('express-session');
app.use(session({secret: 'mySecret', resave: false, saveUninitialized: false}));
so before redirection just save the context for the session
app.post('/category', function(req, res) {
// add your context here
req.session.context ='your context here' ;
res.redirect('/');
});
Now you can get the context anywhere for the session. it can get just by req.session.context
app.get('/', function(req, res) {
// So prepare the context
var context=req.session.context;
res.render('home.jade', context);
});
Here s what I suggest without using any other dependency , just node and express, use app.locals, here s an example :
app.get("/", function(req, res) {
var context = req.app.locals.specialContext;
req.app.locals.specialContext = null;
res.render("home.jade", context);
// or if you are using ejs
res.render("home", {context: context});
});
function middleware(req, res, next) {
req.app.locals.specialContext = * your context goes here *
res.redirect("/");
}
You can pass small bits of key/value pair data via the query string:
res.redirect('/?error=denied');
And javascript on the home page can access that and adjust its behavior accordingly.
Note that if you don't mind /category staying as the URL in the browser address bar, you can just render directly instead of redirecting. IMHO many times people use redirects because older web frameworks made directly responding difficult, but it's easy in express:
app.post('/category', function(req, res) {
// Process the data received in req.body
res.render('home.jade', {error: 'denied'});
});
As #Dropped.on.Caprica commented, using AJAX eliminates the URL changing concern.
Update 2021:
i tried url.format and querystring and both of them are deprecated, instead we can use URLSearchParams
const {URLSearchParams} = require('url')
app.get('/category', (req, res) =>{
const pathname = '/?'
const components ={
a:"a",
b:"b"
}
const urlParameters = new URLSearchParams(components)
res.redirect(pathname + urlParameters)
})
I use a very simple but efficient technique
in my app.js ( my entry point )
I define a variable like
let authUser = {};
Then I assign to it from my route page ( like after successful login )
authUser = matchedUser
It May be not the best approach but it fits my needs.
app.get('/category', function(req, res) {
var string = query
res.redirect('/?valid=' + string);
});
in the ejs you can directly use valid:
<% var k = valid %>