nodejs res.end not working in callback - mysql

I'm trying to retrieve data from a database and send it back to the user, but since mysql queries work asynchronously, I can't just put the code that sends the response after the query code, I have to send the response within the callback function of the query.
var express = require('express');
var fs = require('fs');
var mysql = require('mysql')
var app = express();
var con = mysql.createConnection({
host:"localhost",
user:"root",
password:"",
database:"tempdb"
})
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT,
PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-
type');
res.setHeader('Access-Control-Allow-Credentials', true);
next();
});
app.get('/',function(req,res,next){
function foo(res){
con.query('SELECT * FROM `data` WHERE 1',function(err,result){
if (err) throw err;
res.end('weee',function(err){
if (err) throw err;
)}
})
}
foo(res)
next();
})
app.listen(1001)
Even with something as simple as this, when the data from the database isn't processed or used at all, the res.end function doesn't send any data.
I've tried many variations of this, using waterfall callbacks, putting the res.end in a function outside of the query and calling it, but nothing seems to work. The only time it works it when I put it outside the query, but since the query is async I can't get any data out of it that way. Is there a way to work around this or am I just missing something?
Thanks in advance!

Remove the call to next() in your app.get() handler.
You only want to do that when you are NOT handling the request and sending a response and you want to let other handlers have a crack at the request.
The way you have it, you are calling next() BEFORE the con.query() finishes so that lets the default handler in express handle the request and thus your res.end() doesn't actually do anything because a response has already been sent for this request.
Here's what I would suggest:
app.get('/', function(req, res){
con.query('SELECT * FROM `data` WHERE 1',function(err, result){
if (err) {
console.log(err);
res.status(500).end();
} else {
res.send('weee');
}
});
});
So, you only call next() if you are not going to be sending a response and you want someone else in the handler chain to send the response.

When using res in middlewares throw a return before res.
app.get('/',function(req,res,next){
function foo(res){
con.query('SELECT * FROM `data` WHERE 1',function(err,result){
if (err) throw err;
return res.end('weee',function(err){
if (err) throw err;
)}
})
}
foo(res)
next();
})
Hopefully this works...

Related

Express.js using await with passport

I'm trying to add mySQL to passport.js to authenticate users in express.js, but can't seem to get await working.
Server.js:
initializePassport(
passport,
function(email) {
pool.getConnection(function(err, connection) {
if (err) throw err;
console.log("Connected!");
pool.query("SELECT * FROM users WHERE email = ?", email, function (err, result) {
if (err) throw err;
return result[0];
connection.release();
});
});
},
)
The passport config
function initialize(passport, getUserByEmail) {
const authenticateUser = async (email, password, done) => {
try {
const user = await getUserByEmail(email);
console.log(user)
} catch (e) {
return done(e)
}
Right now it just prints undefined for user, and then prints Connected. I'm not sure why the await user isn't working.
Well if that's getUserByEmail(), then it doesn't return a promise that is connected to when it's asynchronous operations are done, therefore, doing await getUserByEmail() doesn't wait for anything.
await ONLY does something useful if you are awaiting a promise that is connected to the operation you want to await for. Since you aren't even awaiting a promise, that await does nothing useful. You would need to change getUserByEmail() so that it returns a promise that is connected to the asynchronous operation you're trying to wait for.
For a function to return a promise that is connected to the asynchronous operations, you need to use promise-based asynchronous operations, not plain callback asynchronous operations, everywhere in that function. These are all database operations and all modern databases have a promise-based interface now so what you really want to do is to switch .getConnection(), .query() and .release() to all use promise-based operations. This will also make it a lot simpler to implement proper error handling and proper communication back to the caller of errors.
I don't know mysql particularly well myself, but here's a general idea. The promise interface comes from the module mysql2/promise:
const mysql = require('mysql2/promise');
const pool = mysql.createPool({...});
initializePassport(passport, async function(email) {
let connection;
try {
connection = await pool.getConnection();
console.log("Connected!");
let result = await pool.query("SELECT * FROM users WHERE email = ?", email);
return result[0];
} catch(e) {
// log the error and the rethrow so the caller gets it
console.log(e);
throw e;
} finally {
if (connection) {
connection.release();
}
}
});

send list of database rows like object to view in nodejs

I'm new in nodejs and expressjs I want to do some query on my database table and show the result in view but i cant do it:
var express = require('express');
var router = express.Router();
var mysql = require('mysql');
var con = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "imanweb"
});
/* GET home page. */
router.get('/', function(req, res, next) {
let objects=['a','b']
con.connect(function(err){
if(err) throw err;
con.query("select * from tags", function (err, result) {
if (err) throw err;
result.forEach(function(element) {
objects.push(element);
console.log("Result: " + element.title);
}, this);
});
})
console.log("Array: " + objects);
res.render('index', { title: 'Express' ,objs:objects});
});
module.exports = router;
actually the log of this code is like this:
Arr:a,b
Get 200 /410.25 ms --196
Result:php
Result:.net
so it seems the last line of my code is running sooner as the past lines(query)!
it's some thing like asynchronous code running!. so when i give objects to objs parameter. there is no query result inside that.
as solution I tried to use then() but i got > undefined then error in my code..
How can I handle that ??
You need to put your res.render function inside the callback. Like this:
con.connect(function(err){
if(err) throw err;
result.forEach(function(element) {
objects.push(element);
}
res.render('index', { title: 'Express' ,objs:objects});
}, this);
If you send the response outside the callback, your response will be send immediately - it will not wait for the async function.
This works? If not try use async module to do this forEach method in your code.
I've this issue one time and the problem was on forEach.

HTML is being sent instead of JSON Data

I'm trying to retrieve data from a SQL database and display that said data on a Reactjs web app. However, all the calls I make to the database results in the HTML of the webpage in focus. I have set the headers, and I've tried to change the way the response from the express call is being handled.
Here is the expressjs script I am using right now:
const express = require('express');
const sql = require('mssql/msnodesqlv8');
const bodyParser = require('body-parser');
const path = require('path');
const cors = require('cors');
const db = require('./db.js');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use('/counselling/triageadmin/', express.static(path.join(__dirname, '/build')));
app.use(cors());
app.get('/getTable', function (req, res, next){
var request = new sql.Request(db);
request.query('select * from Counselling order by TicketID desc', (err, result) =>{
if (err) { return next(err); }
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result["recordset"]));
});
});
From there, my axios calls look like this:
componentWillMount(){
let self = this;
axios.get("/getTable")
.then(function (response){
console.log(response.data);
self.setState({
data: response.data,
});
})
.catch(function (error){
console.log(error);
})
}
I added the console.log to check what was being returned, and as said, it was the HTML code of the current page of focus.
I made some changes to reflect what steps I took to get the 500 issue out. The current code, however, results in a 404.
If you move your get on top of your put it should work. The problem seems to be that the static clause resolves your request before it gets to your endpoint, so if you do this:
app.get('/counselling/triageadmin/getTable', function (req, res, next){
var request = new sql.Request(db);
request.query('select * from Counselling order by TicketID desc', (err, result) =>{
if (err) { return next(err); }
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(result["recordset"]));
});
});
app.use('/counselling/triageadmin/', express.static(path.join(__dirname, '/build')));
the path to the get will attempt to be matched before you're routed to your static files.
Ideally you would want to have your rest endpoints under a different namespace, i.e. /api but if you decide to keep your setup, this should help.
I think your routes might be conflicting with each other. From the express documentation at: http://expressjs.com/en/4x/api.html#app.use
// this middleware will not allow the request to go beyond it
app.use(function(req, res, next) {
res.send('Hello World');
});
// requests will never reach this route
app.get('/', function (req, res) {
res.send('Welcome');
});
Thus, your route '/counselling/triageadmin/getTable' will never be reached, because your route '/counselling/triageadmin/' is intercepting it, responding with static resources.
To solve this, try organizing your routes in a way that puts all of your API requests at a different subfolder, like '/api'. So your getTable endpoint would be located at: '/api/counselling/triageadmin/getTable/' or something like that.
I'm also learning the MEAN stack and I stumbled upon your question since I had the opposite problem. I wanted it to respond with an HTML instead of a JSON
this line of code MAKES it respond with an HTML
res.send(JSON.stringify(result["recordset"]));
(I tried res.send("<h3 HTML T_T </h3>");) and it did send and HTML
however, if you try
res.json(String(req.params.id)); <= Notice the res.json instead of res.send
It responds with a JSON :)
I hope this helped

passport send error by json

I'm making an app with express + passport and angularJS; I want to be able to send any errors produced from passport (such as username taken or no email provided) by json so my angularJS app can receive these errors in a json response. More specifically right now I want to have a json response to my signup POST method that outputs any errors. I have tried to do this for myself and I've search all over the web and stack overflow I just cannot work this out!
Here is my users route file in express:
var express = require('express');
var router = express.Router();
var isAuthenticated = require('../config/isAuthenticated');
module.exports = function(passport){
router.get('/loggedin', function(req, res){
res.send(req.isAuthenticated() ? req.user : '0');
});
router.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/',
failureRedirect : '/signup',
failureFlash: true
}));
router.post('/login', passport.authenticate('local-login'), function(req, res){
res.send(req.user);
});
router.post('/signout', function(req,res){
req.logout();
res.json({redirect: '/'});
});
router.get('/authtest', isAuthenticated, function(req, res){
res.render('authtest', {user: req.user});
});
return router;
};
This is my passport signup strategy:
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, username, password, done){
process.nextTick(function(){
User.findOne({'local.username' : username}, function(err, user){
if(err) return done(err);
if (user) { //username already exists
return done(null, false, {message: 'Username already exists'});
} else if(!req.body.email) { //no email address provided
return done(null, false, {message: 'You must provide an email address!'});
} else {
var newUser = new User();
newUser.local.username = username;
newUser.generateHash(password, function(err, hash){
if(err) return done(err);
newUser.local.password = hash;
});
newUser.email = req.body.email;
newUser.servers = [];
newUser.save(function(err){
if(err) throw err;
return done(null, newUser);
});
};
});
});
}
));
I know looking at my code right now it looks like I haven't tried to solve this myself at all but this is just my latest working code; I have been stuck at this for the past few days!
Any help would be greatly appreciated :)
According to the current code of passport this is probably achievable by passing custom callback to handle all results of authentiction yourself. This callback is given after options or instead of those.
passport( "local-signup", { ... }, callbackFn );
or
passport( "local-login", callbackFn );
This callback is used in all resulting situations of trying to authenticae. It is thus invoked on processing errors like this:
callbackFn( err )
If (all configured) authentications have failed it is called with
callbackFn( null, false, challenge(s), status(es) )
On successfully having authenticated user the callback is invoked like so:
callbackFn( null, user, infos )
with infos optionally provided by strategies.
Now comes the bottom-side: In either situation passport.authenticate() skips usual processing but instantly invokes provided callback to care for the rest. This includes processing of any options passed in call for passport.authenticate() like flashing messages, preparing session and request for containing authenticated user etc.
Since options given passport.authenticate() are never passed into callback there is actually no obvious reason to use both.
When I was stumbling over the very same problem (linking passport-service with angular-js POST request) I declined to consider use of callback a proper solution. This callback isn't documented. And it doesn't even look quite useful for it isn't passing req, res and next to pass any actual request in callback. Thus it makes very little sense to use it at all and I'd expect it to vanish soon or to change its behaviour quite much.
So the second approach was about trying to figure out why there is a problem in AngularJS. Passport is sending plain text Unauthorized in response with status code 401. AngularJS is trying to parse this as JSON and produces Syntax error. The text Unauthorized results from passprt ending response very simply by invoking
res.statusCode = 401;
res.end(http.STATUS_CODES[res.statusCode]);
Thus a proper workaround might try to replace
either text in http.STATUS_CODES though this is having impact on processing further requests and thus isn't preferrable
or res.end() by an overloaded method acting differently if res.statusCode is 401.
Due to affecting any current request, only, I tried the latter. Replaced res.end() might be used to send any text you want:
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
return _end('{"status":"Unauthorized"}');
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Alternatively the replaced method might add previously missing response header information on content type, for this was actually causing issues in AngularJS processing that response as JSON by default.
router.post('/login',
function(req, res, next) {
var _end = res.end;
res.end = function() {
if (res.statusCode === 401) {
res.set("Content-Type", "text/plain");
}
return _end.apply(this, arguments);
};
next();
},
passport.authenticate('local-login'),
function(req, res) {
res.send(req.user);
}
);
Finally, either approach is really just a workaround. I think passport is in the need for revising this annoying limitation.

Node.js, Express and Mysql. How is correct way

What i'am trying to achieve is to make DB query inside closure. Return data and then send stuff to user. I understand that best practice is to use database pooling. Problem is that query is not sync.
Simplified code:
server.js
var express = require('express'),
app = express(),
server = require('http').createServer(app),
mysql = require('mysql');
app.set('DB:pool', mysql.createPool(process.env.DATABASE_URL));
var myClosure = require('./closure.js')(app));
app.get('/somepage', function(req, res) {
var data = myClosure.myquery();
res.send(data);
});
app.get('/anotherpage', function(req, res) {
var data = myClosure.myquery();
res.send(data);
});
app.listen(3000);
closure.js
function myClosure(app) {
var pool = app.get('DB:pool');
return {
myquery: function(inp) {
pool.getConnection(function(err, db) {
if (err) throw err;
db.query('SELECT * FROM table', function(err, rows, fields) {
if (err) throw err;
data = rows[0]
db.release();
});
});
return data;
}
};
}
module.exports = myClosure;
In examples i found all DB related stuff were made in route callback and response was sent in query callback. But way i'm trying to do it is not working as myquery returns undefined because sql query is not done there.
So what is correct way to handle querys ?
Make your query-function handle a callback too:
// server.js
app.get('/somepage', function(req, res) {
myClosure.myquery(function(err, data) {
// TODO: handle error...
res.send(data);
});
});
// closure.js
...
myquery: function(callback) {
pool.getConnection(function(err, db) {
if (err) return callback(err);
db.query('SELECT * FROM table', function(err, rows, fields) {
// release connection before we return anything, otherwise it
// won't be put back into the pool...
db.release();
if (err) return callback(err);
callback(null, rows[0]);
});
});
}
(I left out the inp argument because that didn't seem to be used)