Hello I am new to building node js and express web applications and am trying to understand how to structure route handling + model code using MySQL and node.js. Here is an example of my current login.
router.post('/login', function (req, res, next) {
var email = req.body.email;
var password = req.body.password;
var sqlQuery = 'SELECT * FROM users WHERE email = ?';
checkUsername();
function checkUsername() {
connection.query(sqlQuery,[email], function (error, results, fields) {
if (error) return next(error);
if(results.length == 0) {
res.status(401).json({ message: 'invalid credentials' });
} else {
checkPassword(results[0].password, results[0]);
}
});
}
function checkPassword(hash, result) {
bcrypt.compare(password, hash, function(err, match) {
if (match) {
var userToken = jwt.sign({id: result.id}, config.JWTSecret, { expiresIn: '1h' });
delete result.password;
res.status(200).json({token: userToken, user_data: result});
} else {
res.status(401).json({ message: 'invalid credentials' });
}
});
}
});
as you can see currently all of the logic is being performed in the route handler. What is the advantage of extracting this code to a model object? Should the response be sent in the model or route handler? How would you structure your code for this particular example using a user object and more generally when using MVC with express and MySQL?
Thanks!
Related
What I am attempting to do is write a statement to check if email exists in my mysql database when a user registers. In postman it sends me the correct error message of "user already taken" however the server crashes after and displays "cannot set headers after they are sent to the client." I have read similar posts but did not help.
//The following code is in my user.service.js file:
const pool = require("../../config/database");
module.exports = {
//Create new user
createUser: (data, callBack) =>{
pool.query(
`insert into registration(name, email, password, confirm_password)
values(?,?,?,?)`,
[
data.name,
data.email,
data.password,
data.confirm_password
],
(error, results, fields) =>{
if(error){
return callBack(error);
}
return callBack(null, results);
}
);
}
}
//The following code is in my user.controller.js file:
const {
createUser,
} = require("./user.service");
const pool = require("../../config/database");
module.exports = {
createUser: (req, res) =>{
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
})
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
}
}
//The following code is from user.router.js file:
const {
createUser,
} = require("./user.controller");
const router = require("express").Router();
router.post("/signup", createUser);
module.exports = router;
In your createUser function that is executed on the post request you are doing two things. First you check whether a user with the provided email exists and, second, you create a user. However, those functions are not executed consecutively, instead they are running simultaneously and thus create a race condition.
So going off on your example, if the email check query SELECT email FROM registration WHERE email = ? is faster and the user already exists, it will respond with:
return res.status(400).json({
message: 'User already taken'
})
but the createUser function (below) is still running and once it is finished, it will try to also send a response. Therefore, you are presented with an application crash in the console even though in the postman you can see the response stating that the user already exists.
In order to fix this error you should execute the createUser function only if the results.length is 0 inside the callback provided to the email check query, like so:
createUser: (req, res) => {
const body = req.body;
const salt = genSaltSync(10);
pool.query('SELECT email FROM registration WHERE email = ?', [body.email], (error, results) =>{
if(error){
console.log(error);
}
if(results.length > 0){
return res.status(400).json({
message: 'User already taken'
})
}
createUser(body, (err, results) => {
if(err){
console.log(err);
return res.status(500).json({
success:0,
message:"Error in database connection"
});
}
return res.status(200).json({
success: 1,
message: `User ${results.insertId} signed up successfully`,
data: results
});
});
})
}
Now you execute the createUser function only if a user with the provided email doesn't exist, which effectively removes the race condition between the two functions.
I have a login form that authenticates using postgresql I'm trying to check if users exists then redirect the client to the other page. The code is:
app.post('/login', (req, res) => {
var Enteredusername = req.body.username;
var Enteredpassword = req.body.password;
pool.query("SELECT * FROM tbl_users WHERE username = $1 AND password = $2", [Enteredusername, Enteredpassword], (err, result) => {
if (err) return console.log('error in query', err);
// need to check if user exists
let user = (result.rows.length > 0) ? result.rows[0] : null;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}
res.redirect('/posts')
});
});
And I got the error:
_http_outgoing.js:470
throw new ERR_HTTP_HEADERS_SENT('set');
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
How Can I fix it?
It's the async behavior of javascript res.redirect('/posts') is executed before req.flash and res.render you can hack it like this :
req.session.userId = Enteredusername;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}else{
return res.redirect('/posts')
}
I'm actually creating a chat like Discord with servers and channels using Node.JS, MongoDB and Mongoose.
Actually, my structure is this one:
https://github.com/copostic/My-Simple-Chat/tree/master/models
But to get the conversations, I have to make so much nested functions and I would like to know if there was a better way to organize my code..
Here's the code with the nested functions, I'm trying to get the message list of each channel of each server:
"use strict"
const Server = require('../models/server'),
Channel = require('../models/channel'),
Message = require('../models/message'),
User = require('../models/user');
exports.getChannels = function (req, res, next) {
// Only return one message from each conversation to display as snippet
Server.find({members: req.session._id})
.select('_id')
.exec(function (err, servers) {
if (err) {
res.send({ error: err });
return next(err);
}
servers.forEach(function (server) {
Channel.find({ serverId: server })
.exec(function (err, channels) {
// Set up empty array to hold conversations + most recent message
let fullConversations = [];
channels.forEach(function (channel) {
Message.find({
'channelId': channel._id
})
.sort('creationDate')
.limit(1)
.populate({
path: "author",
select: "profile.firstName profile.lastName"
});
.exec(function (err, message) {
if (err) {
res.send({
error: err
});
return next(err);
}
fullConversations.push(message);
if (fullConversations.length === conversations.length) {
return res.status(200).json({
conversations: fullConversations
});
}
});
});
});
});
});
};
Thanks a lot
So i am new comer to keystone CMS and its looking awesome to me
i have setup the basic structure and using a default blog project provided by keystone so now i am trying to build the rest API for my admin
As the rest API working fine when i am loged in browser in keystone admin panel but when i am testing the same is postman even after setting the basic auth it giving me HTML page
I don't know what the wrong with that and how to setup this thing correctly.
Here is my code from index.js
var _ = require('underscore'),
keystone = require('keystone'),
middleware = require('./middleware'),
// restful = require('restful-keystone-onode')(keystone),
importRoutes = keystone.importer(__dirname);
// Common Middleware
keystone.pre('routes', middleware.initLocals);
keystone.pre('render', middleware.flashMessages);
// Import Route Controllers
var routes = {
views: importRoutes('./views'),
api: importRoutes('./api'),
};
// create a route that handles signin
function signin (req, res) {
if (!req.body.username || !req.body.password) return res.json({
success: false });
keystone.list('User').model.findOne({ email: req.body.username
}).exec(function (err, user) {
if (err || !user) {
return res.json({
success: false,
session: false,
message: (err && err.message ? err.message : false) || 'Sorry,
there was an issue signing you in, please try again.',
});
}
keystone.session.signin({ email: user.email, password:
req.body.password }, req, res, function (user) {
return res.json({
success: true,
session: true,
date: new Date().getTime(),
userId: user.id,
});
}, function (err) {
return res.json({
success: true,
session: false,
message: (err && err.message ? err.message : false) || 'Sorry,
there was an issue signing you in, please try again.',
});
});
});
}
// you'll want one for signout too
function signout (req, res) {
keystone.session.signout(req, res, function () {
res.json({ signedout: true });
});
}
// also create some middleware that checks the current user
// as long as you're using Keystone's session management, the user
// will already be loaded if there is a valid current session
function checkAuth (req, res, next) {
// you could check user permissions here too
if (req.user) return next();
return res.status(403).json({ error: 'no access' });
}
// Setup Route Bindings
exports = module.exports = function (app) {
// Views
app.get('/', routes.views.index);
app.get('/blog/:category?', routes.views.blog);
app.get('/blog/post/:post', routes.views.post);
app.get('/gallery', routes.views.gallery);
app.all('/contact', routes.views.contact);
// add an API endpoint for signing in _before_ your protected routes
app.post('/api/signin', signin);
app.post('/api/signout', signout);
// then bind that middleware in your routes before any paths
// that should be protected
app.all('/api*', checkAuth);
//
app.get('/api/post/list', keystone.middleware.api,
routes.api.posts.get);
app.get('/api/post/:id', keystone.middleware.api,
routes.api.posts.get);
};
and here is my route/api/post.js
/**
* Created by nikk on 11/5/17.
*/
var async = require('async'),
keystone = require('keystone');
var Post = keystone.list('Post');
/**
* List Posts
*/
exports.list = function(req, res) {
Post.Modal.find(function(err, items) {
if (err) return res.apiError('database error', err);
// res.apiResponse({
// posts: items
// });
res.json(items);
});
}
/**
* Get Post by ID
*/
exports.get = function(req, res) {
Post.model.findById(req.params.id).exec(function(err, item) {
if (err) return res.apiError('database error', err);
if (!item) return res.apiError('not found');
res.apiResponse({
post: item
});
// res.json(item);
});
}
I have been trying hard to get this thing done from last day but not able to do working till now
please guide me.
I have a node js/express.js server and I am attempting to create a login function for my website using passport.js. I am NOT using MongoDB to store user info. Instead, I want to use my own AmazonAWS RDS MySQL instance as my database. This means I cannot use Mongoose.
As a result, I need to query for users without Mongoose to verify their credentials in Passport.js. My RDS instance has a table called Users that contains users with just the userName and password. Each record (user) in the table is also assigned its own unique usersObjectID number. I am attempting to query the users based on the user-name that the end-user will input into the login form and matching that user-name with a record in the table.
This is my server-side code:
var connection = mysql.createConnection(
{
//database connection info
}
connection.connect(function(error)
{
if(error)
{
console.log('MySQL error: '+error);
}
else
{
console.log('Successfully connected to MySQL database');
}
});
passport.use(new LocalStrat({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
//query user from DB
var usersObjectID = -1;
var tableLength = connection.query("select count(*) from Users");
console.log("this is the length of the table: "+tableLength);
for(var i = 0; i < tableLength; i++)
{
if(connection.query("select userName from Users where Users.usersObjectID = '"+i+"'") == username)
{
console.log(connection.query("select userName from Users where Users.usersObjectID = '"+i+"'"));
usersObjectID = i;
}
}
if(usersObjectID == -1)
{
//user not found
return done(null, false, {message: "The user is not exist"});
console.log("user does not exist");
}
else if(usersObjectID != -1 && connection.query("select userName from Users where Users.usersObjectID = '"+usersObjectID+"'") != connection.query("select password from Users where Users.usersObjectID = '"+usersObjectID+"'"))
{
// if password does not match
return done(null, false, {message: "Wrong password"});
console.log("password incorrect");
}
else
{
// if everything is OK, return null as the error
// and the authenticated user
return done(null, user);
console.log("successfully logged in");
}
}
));
This is my post method:
app.post('/login', function (req, res, next) {
var uname = req.body.username;
var pass = req.body.password;
var rem = req.body.remember_me;
console.log(uname+", "+pass+", "+rem);
// ask passport to authenticate
passport.authenticate('local', function(err, user, info) {
if (err) {
// if error happens
return next(err);
console.log("err");
}
if (!user) {
return res.redirect('/login');
console.log("!user");
}
// if everything's OK
req.logIn(user, function(err) {
if (err) {
return next(err);
}
console.log("ok");
return res.redirect('/');
});
})(req, res, next);
});
Here are my questions:
When I query
var tableLength = connection.query("select count(*) from QCBIMS.Users");
console.log("this is the length of the table: "+tableLength);
I get this result:
this is the length of the table: [object Object]
Why?
Would this be a good way to go about using an RDS instance for my user info and login function?
Encountered the same problem and solved it using the FF steps:
Use alias in the SELECT STATEMENT, like: "SELECT COUNT(*) AS total from QCBIMS.Users" Note: Using alias will make it easier to find it later
Select the first [key:value] pair from list & stringify as JSON: let resultStr=JSON.stringify(tableLength[0]) Note: I Used 'tableLength' from the query above. After stringify, result can be like
this: [{"total":"1541"}]
parse result as JSON: let itemPair = JSON.parse(resultStr)
get value using the 'alias' from SQL query: let value = itemPair[0].total
IF YOU WANNA SEE THE CONTENT OF THE [object Object], you can do this:
Object.keys(queryResult).forEach(function (key) {
console.log(JSON.stringify(queryResult[key]));
});
Why not use the query following way
connection.query('select count(*) as total from Users',function(err,rows){
var tableLength = rows[0];
// do the other stuff here
});
try
const count = rows.item(0)["count(*)"];