node.js: Asynchronous Callback Values - javascript

I'm pretty confused. I would love to learn how I can pass the values I get in an async function on.
I have a module with basic auth functions. In the login I ask the User model to search for a user with a given username.
login: function(req){
var username = req.body.username,
password = req.body.password;
user.find(username);
}
Then the User model goes on and does that.
exports.find = function(username){
console.log(User.find({username: username}, function(error, users){
// I get nice results here. But how can I pass them back.
}));
}
But how can I pass that user object back to the login function?

You need to pass a callback function to the method. Node.js requires a very callback-driven programming style.
For example:
// in your module
exports.find = function(username, callback){
User.find({username: username}, function(error, users){
callback(error, users);
});
}
// elsewhere... assume you've required the module above as module
module.find(req.params.username, function(err, username) {
console.log(username);
});
So you don't return values; you pass in functions that then receive the value (rinse, repeat)
Your login method on the User class would then look something like this:
login: function(req, callback){
var username = req.body.username,
password = req.body.password;
user.find(username, function(err, user) {
// do something to check the password and log the user in
var success = true; // just as an example to demonstrate the next line
callback(success); // the request continues
};
}

You can't pass it back (because the function os asynchronous and login would already have returned when it's done). But you can pass it ahead to another function.

Related

Directly authenticate(login) after successful signup in Node.js

How can i directly authenticate the user after signup.
Below are the the deatail of serializeUser and deserializeUser.
passport.serializeUser(function(user, done) {
done(null, {tutorId: user.tutorId, userType: user.userType});
});
passport.deserializeUser(function(userData, done) {
Tutor.getTutorById(userData.tutorId, (err, user) => {
if (err) {
try {
logger.silly(`message: POST inside passport.deserializeUser; file: index.js; error: ${err}; user: ${JSON.stringify(user)}`);
logger.error(`message: POST inside passport.deserializeUser; file: index.js; error: ${err}; user: ${JSON.stringify(user)}`);
} catch (e) {
You can use req.login() after successful registration.
From official Passport documentation:
Note: passport.authenticate() middleware invokes req.login()
automatically. This function is primarily used when users sign up,
during which req.login() can be invoked to automatically log in the
newly registered user.
A sample registration code might look like this:
router.post("/register",(req,res) => {
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
//define other things here
//create hash and salt here
user.save().then(user => {
//on successfult registration
//login user here, using req.login
req.login(user ,err => {
if(!err){
//redirect to logged-in page
//or user page
res.redirect('/')
}
})
})
})
Read about req.login() in official passport documentsation
I hope this helps you out.
you can create token just after successful registration and send it back in registration response

NodeJs Express app.get that handles both query and params

I'm trying to create a REST Service. The route below will execute a stored procedure that will return json results
app.get('/spparam', function (req, res) {
var sql = require("mssql");
// config for your database
var id=0;
var config = {
user: 'username',
password: 'password',
server: 'hostname',
database: 'databasename'
};
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
if(!mylib.isEmptyObject(req.query)){
id=req.query.id;
}else if(!mylib.isEmptyObject(req.params)){
id=req.params["id"];
}
// Executing Stored Prcoedure
request.input('requestid', sql.Int, id)
.execute("Request_Get_ById").then(function(recordSet) {
//console.dir(recordsets);
//console.dir(err);
res.send(recordSet);
sql.close();
}).catch(function(err) {
console.log(err);
});
});
});
I want to minimise my code by creating one route that will handle both query (/spparam?id=1) and params (/spparam/:id/). Is this possible? Is there a better way to handle what I need?
Yup, you can do that with Express like this:
app.get('/spparam/:id?', function (req, res) {
const id = req.params.id || req.query.id;
// the rest of your function, and use id without caring about whether
// it came from params or query
// change order if you want to give preference to query
}
The Express.js docs say it uses path-to-regexp for route matching purposes. There you can see this quote:
Parameters can be suffixed with a question mark (?) to make the
parameter optional.
In javascript, the construct var a = b || c assigns the value of b to a if b is not false-y, and otherwise it assigns the value of c to a.

How do I use Angular Controller to get data from Mongo database

I am trying to use controllers to modify a page according to the user.
I am using this:
$http.get('/someUrl').then(function(response){
$scope.firstname = response.data;
});
What I am trying to do is, get each of the fields from my database and use them accordingly.
If this is my schema and I already have a person stored in the database:
var personSchema = new mongoose.Schema({
firstname: {type: String, required: true, },
lastname: {type: String, required: true}
});
var person = mongoose.model('People', personSchema);
module.exports = person;
What URL will I use in '/someUrl' to get the firstname of the person that is currently signed in?
Also, am I missing any functionality that I need to execute this?
EDIT:
My route
router.post("/update-profile", function(req, res) {
if (!req.session.user) {
return res.status(401).send();
}
var firstname = req.body.firstname;
var lastname = req.body.lastname;
var newProfile = new UserProfile();
newProfile.firstname = firstname;
newProfile.lastname = lastname;
console.log(newProfile);
newProfile.save(function(err, savedProfile) {
if (err) {
console.log(err);
return res.status(500).send();
} else {
res.render("profile");
return res.status(200).send();
}
});
});
Get a person object from the url.
$http.get('/user?id=5').then(function(response){
$scope.user = response.data;
});
Where the id is the user id.
Then in your html you can use {{user.firstName}}.
You could also pass in req.user to the page when it loads, so you wouldn't need to do another get.
res.render('page',{user:req.user});
Then (in ejs)
$scope.user = <%=user%>;
What I feel is, you should have an end-point /api/users/:id that should expect an _id as request parameter.
So your get request would be like :
$http.get('/someUrl/' + current_logged_in_user_id).then(function(response){
$scope.firstname = response.data;
});
And your backend code would be something like:
app.get('/someUrl/:id', function(req, res){
User.findById(req.params.id, function (err, user) {
if(user) res.json(user.firstName);
});
});
Now from the information that you provided, that's the best I can tell you. You can use DaftMonk's Angular Fullstack Generator. It creates the whole Authentication Boilerplate for you. It automatically get user details based on session_id and adds the user to req.user. So in the request handler, you can simply get the requesting user's details by using req.user

Passing data from model to router in node js

I am trying to pass some data from my db to the router which then passes the data to the view.
My model code :
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
console.log(res); // This prints out everything I need
});
module.exports = {
data: result // should contain the query result (= 2 objects in this case)
}
Now to my route file :
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
console.log(Users.data);
/* GET home page. */
router.get('/users', function(req, res) {
res.render('api', { data: Users.data, title: "Test API Output" });
});
module.exports = router;
When I console.log Users or Users.data I get undefined. I don't really get why this is the case. How else am I supposed to pass data along the files.
All help is gladly read :) Thank you.
module.exports are being evaluated the second you require and variables are not passed by reference in this case.
What that means for your code is the following:
var result; // result is "undefined" because it does not contain a value here
// You are doing your DB queries here...
module.exports = {
data: result // ...and because the query has not finished here yet, result
// is still undefined.
// This is also a good example of a so called "race condition", because there is a
// slight (improbable) chance that the query might have already finished.
// Hence, it could happen that sometimes result is already filled.
}
When you now require the above file in another file of your code, the above is being evaluated and saved straight away (result is undefined at that point in time, hence it is also undefined when it exports).
Your query is being executed and written into the result variable, but at that point in time you can not modify the exported variable anymore – because it is it's own variable and not merely a reference to result).
What you could do is the following:
function getData(callback) {
connection.query('SELECT * FROM users', function(err, res, fields) {
callback(err, res);
});
}
module.exports = {
getData: getData
}
and then in your other file:
var Users = require('../models/users');
Users.getData(function(err, result) {
// TODO: Error handling.
console.log(result);
});
That's exactly why it's so easy with JavaScript to end up in callback hell, because of it's asynchronous nature.
The above is the exact same situation as if you, f.e., want to get some data via AJAX from a server and then fill tables with it. When you start creating the table before you have the data (so the AJAX request is not yet complete), you end up with an empty table. What could do is:
you create a variable that holds your data and
a function that creates the table
when you then ask the server for the data (via AJAX) you wait until you get the data (completion callback) and only then you start creating the table: filling your variable and calling the function to fill the table with the data.
Server-Side JavaScript is the same as client-side. Never forget this.
As a little homework for the reader: the way to get out of callback hell is by reading up on promises – a pattern/architecture which reduces indents and saves lots of headaches :)
(update: Lucas' answer is basically telling the same thing as I did)
(update 2: wrong way of handling err)
I suggest realize the consult in the route file, some like this:
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
/* GET home page. */
router.get('/users', function(req, res) {
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
res.render('api', { data: res.data, title: "Test API Output" });
});
});
module.exports = router;
But you can configure the connection with database in another file, in libs/mysql_connect.js.
The undefined is caused because the response of connection.query don't works out of the connection.query.
If you really want the query to run only once and then just re-use the already queried data, I think you are after something like this for your model:
...
var data;
var mymodel = {};
...
mymodel.getData = function(callback) {
if(data) {
callback(data);
} else {
db.query('select * from users', function(err,res,fields) {
// error checking and such
data = res;
callback(data);
});
}
}
module.exports = mymodel
In your router, you'd then use it like this:
...
router.get('/users', function(req, res) {
Users.getData(function(mydata) {
res.render('api', { data: mydata, title: "Test API Output" });
});
});
The first time you call getData, you'll get a fresh result, and on subsequent calls you get the cached result.
While you could expose data in mymodel directly, and only use the callback in case it is still undefined, that'd make your code in the router more convulated.

Need help on how to actually use Passport authentication strategy in Express

Suppose I have a script like this, which uses a Passport authentication strategy with an Express backend. How would I use this script to actually make API function calls? I don't see any explicit examples in the linked project's documentation nor can I find anything in Passport.js's documentation. Thanks.
After passport user serialization done, every request has user field, which contains information passed to done callback of passport.serializeUser method.
app.get('/userID', function (req, res) {
if (req.isAuthenticated()) {
res.json(req.user.id);
}
res.redirect('/login');
}
Also, you have access to session
app.get('/auth/fitbit/callback',
passport.authenticate('fitbit', { failureRedirect: '/login' }),
function(req, res) {
req.session.loggedInAt = Date.now();
res.redirect('/');
});
Information stored in session available in all requests, while user is authenticated
app.get('/someroute', function (req, res) {
// call to another service
var url = 'http://superservice.com/' + req.user.id + '/' + req.session.loggedInAt
http.get(url, function (_res) {
res.send(_res.data)
});
});
I'm supposing that you know how to use passport, and you will figure it out what's the right Fitbit API endpoint (honestly, I'm don't know it). Said that, let me give an idea that might help you solve your problem:
// An awesome npm module (https://github.com/mikeal/request)
var request = require('request');
//
//
//
// An express route.
app.get('/activities', function (req, res) {
if (req.user !== null) {
// User is authenticated.
getUserActivities(req.user.id, res);
} else {
// Redirect to login the user isn't authenticated.
res.redirect('/login');
}
});
// This function will make API calls to Fitbit
// using the User ID we got from the PassportJS
// authentication process.
function getUserActivities(id, res) {
// It will request from Fitbit User activities.
request('https://api.fitbit.com/1/user/'+ id +'/activities/',
function (error, response, body) {
if (!error && response.statusCode == 200) {
// If everything goes well.
return res.send(body);
} else {
// If something wrong happens.
return res.send(error);
}
);
}
The goal of this example is to show you that you need to use PassportJS to get fitbit users ID, then use that id to make API calls to fitbit.

Categories

Resources