Why I'm always getting an Internal Server Error (code 500) after making a request to BackEnd - mysql

I'm having a little trouble with my site and I can't understand what is happening.
First of all I have to say that I was NOT having this behavior when developing on localhost, but now that my site is close to be completed I think that uploading my code to a hosting service and make some tests there would be a good idea.
The issue is that when I make a request to the database, most of the times the site keeps in an eternal loading state, until the error code 500: Internal Server Error appears (I said "most of the times" because it works nice sometime, but normally it remains in a pending state).
Given the fact that SOME TIMES the request work nice, makes me think that the issue is not on the server.js file (where I defined the endpoints), and also is not on my controllers files (where I have some logic and the requests itself).
I'll leave here some pics as example of what is happening but if you need some extra info just tell me:
A simple login example, I just fill the fields and send the request
And here you can see how the request remain as pending
Until it fails
EDIT: I'm using package Mysql2 to connect to the DB, and I was reading that this behavior may be because a bad use of connections (and I'm reading about "pools", but I'm kinda lost tbh)
Here is the connection file:
require("dotenv").config();
const mysql = require("mysql2");
const db = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
waitForConnections: true,
});
const connection = async () => {
db.connect((err) => {
if (err) throw err;
console.log("Successfully connected");
})
}
exports.db = db;
exports.connection = connection;
The first call to the DB (just to check the connection)
connection().then(() => {
app.listen(port, () => {
console.log(`Server running at ...`);
});
});
And the login logic
app.post("/dev-end/api/login", async (req, res) => {
await singleAccount(db, req.body.email)
.then(async (response) => {
if (response.code) {
res.render("templateLogin");
}
try {
if (await bcrypt.compare(req.body.password, response.password)) {
const user = { id: response._id, name: response.name };
await deleteTokenById(db, user.id.toString());
const accessToken = generateAccessToken(user);
const refreshToken = jwt.sign(
user,
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: "604800s" }
);
createToken(db, {
_id: user.id,
accessToken: accessToken,
refreshToken: refreshToken,
createdAt: new Date().toISOString().slice(0, 19).replace("T", " "),
}).then(
res
.cookie("access_token", accessToken, {
httpOnly: true,
maxAge: 60000 * 60 * 24 * 7,
})
.redirect("/dev-end/dashboard")
);
} else {
res.render("templateLogin");
}
} catch {
res.status(500).send();
}
})
.catch(console.log);
});
=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>
const singleAccount = async (conn, email) => {
return await read(conn).then((res) => {
if (!res.code) {
const result = res.find((e) => e.email.toString() === email);
if (!result) {
return {
code: 404,
msg: "No account was found with the provided id",
};
}
return result;
}
return res;
});
};
=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>=>
const read = async (conn) => {
const sql = `SELECT * FROM accounts`;
return await conn.promise().query(sql)
.then(([res, fields]) => res);
};

Related

Express session not saving after successful authentication

I am implementing a login system for my project. This project is divided in two, a server portion in NodeJS, and a client portion in ReactJS. Both of these are wrapped up in docker containers including a couple more containers for mySQL and PHPMyAdmin. Thus far, I've been able to connect to databases in the mySQL container and insert into a table for Users. Now, I'm trying to log in with a user, then save this user information if the login is successful, and return the session when asked. So I call the sign in get request as follows in the front-end:
export function signIn(table, userName, password) {
return axios.get(`http://localhost:8000/signin`, {
params: {
table,
userName,
password,
},
}, {withCredentials: true}).then((response) => {
if (response.data.length === 1) {
return "success";
}
return response;
});
}
Then in the server, I receive and work with the information like this:
const bcrypt = require('bcryptjs');
const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');
const multer = require('multer');
const mysql = require('mysql');
const nodeMailer = require('nodemailer');
const session = require('express-session');
const smtpTransport = require('nodemailer-smtp-transport');
const app = express();
const upload = multer();
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 7 * 24 * 60 * 60 * 1000,
secure: false,
}
}));
app.use(cors(({
credentials: true,
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const pool = mysql.createPool({
host: process.env.MYSQL_HOST_IP,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
});
app.get('/signin', (req, res) => {
const { table, userName, password } = req.query;
pool.query(`select * from ${table} where username = '${userName}'`, (err, results) => {
if (err) {
res.send(err);
} else {
if (bcrypt.compareSync(password, results[0].password)) {
req.session.userId = results[0].id;
req.session.name = results[0].name;
req.session.email = results[0].email;
req.session.sex = results[0].sex;
req.session.img = results[0].img;
req.session.userName = results[0].username;
req.session.about = results[0].about;
req.session.save(err => console.log(err));
res.send(results);
} else {
res.send([]);
}
}
});
});
Then I expect to call it with another request to get the information back and use to to modify a front end component's state like this (both of these requests are in the same file):
app.get('/loggeduser', (req, res) => {
if (req.session.userId) {
const {
userId,
name,
email,
sex,
img,
userName,
about,
} = req.session;
const userInfo = {
userId,
name,
email,
sex,
img,
userName,
about,
};
res.send(userInfo);
} else {
res.send({});
}
});
and the component calls it like this:
export function getLoggedUser(setUserInfo) {
axios.get(`http://localhost:8000/loggeduser`, {}, {withCredentials: true}).then((response) => {
setUserInfo(response.data);
});
}
But the information never gets sent back, because req.session.userId is always undefined. I tried adding a console.log to output req.session and whenever I refresh the page (at which time the component calls getLoggedUser) the server image outputs req.session with a created time that is just a few seconds ago from the moment I refresh the page, meaning it gets created anew whenever I refresh. Is it that this is not saving properly because it's a get request and not a route? Please let me know if I may be missing something vital for this to work.

express-http-context getting lost after calling mysql

Need your help. I am trying to implement global request id for my nodejs application. For that I am using express-http-context lib to set request id. But it's not working as expected when my code access data from mysql database. Seems like there is a compatibility issue in cls used by express-http-context and mysql library.
Sample code and the output is provided below:
const app = require('express')();
const httpContext = require('express-http-context');
const mysql = require('mysql');
const { INTERNAL_SERVER_ERROR, OK, NOT_FOUND } = require('http-status-codes');
const http = require('http');
const uuidv4 = require('uuid/v4');
const pool = mysql.createPool({
host: '127.0.0.1',
port: 3306,
user: 'root',
password: 'Redhat#123',
database: 'user',
connectionLimit: 10,
});
pool.query('SELECT 1 + 1 AS solution', (error) => {
if (error) {
console.log('Unable to connect to MySQL:- ', error.message);
process.exit(1);
}
console.log('MySQL PING is Working');
});
app.use(httpContext.middleware);
app.use((req, res, next) => {
httpContext.set('requestId', uuidv4());
console.log('request Id set is: ', httpContext.get('requestId'));
next();
});
const getUserDetailsRepository = (userId, callback) => {
console.log('inside getuserDetails repository for request id: ', httpContext.get('requestId'));
pool.getConnection((connectionError, connection) => {
if (connectionError) {
console.log('got ConnError getuserDetails repository for request id: ', httpContext.get('requestId'));
callback(connectionError);
} else {
const query = 'SELECT * from user where id = ?';
connection.query(query, [userId], (queryError, results) => {
connection.release();
if (queryError) {
console.log('got queryError getuserDetails repository for request id: ', httpContext.get('requestId'), queryError.message);
callback(queryError);
} else {
console.log('Got response inside getuserDetails repository for request id: ', httpContext.get('requestId'));
callback(null, results);
}
});
}
});
};
const userGetDetails = (req, res, next) => {
const { userId } = req.params;
console.log('inside getUserDetails controller for request id: ', httpContext.get('requestId'));
getUserDetailsRepository(userId, (error, result) => {
if (error) {
console.log('got Error in getuserDetails controller for request id: ', httpContext.get('requestId'))
res.sendStatus(INTERNAL_SERVER_ERROR);
} else if (result) {
console.log('Got response inside getuserDetails repository for request id: ', httpContext.get('requestId'));
res.status(OK).json(result);
} else {
res.sendStatus(NOT_FOUND);
}
});
};
app.get('/user/:userId', userGetDetails);
const server = http.createServer(app);
server.listen(3000, () => console.log('Http server started listening on port'));
Output:
Http server started listening on port
MySQL PING is Working
request Id set is: ea4895ab-8003-4d28-99aa-b03af7027ae8
inside getUserDetails controller for request id: ea4895ab-8003-4d28-99aa-b03af7027ae8
inside getuserDetails repository for request id: ea4895ab-8003-4d28-99aa-b03af7027ae8
Got response inside getuserDetails repository for request id: undefined
Got response inside getuserDetails repository for request id: undefined
i hope this solution solve your problem and save many hours for anyone that have this problem
you can just use bindEmitter to solve the problem
app.use((req, res, next) => {
httpContext.ns.bindEmitter(req);
httpContext.ns.bindEmitter(res);
var requestId = req.headers["x-request-id"] || uuidv4();
httpContext.set("requestId", requestId);
console.log('request Id set is: ', httpContext.get('requestId'));
next();
});

How to avoid deadlock in nodejs mysql with a lot of queries?

I have a lot of urls, for every url I call the function load(url), this function parse the html, extracts the needed data and builds a bulk insert query as you can see in my test.js code. The problem is, if I have to many urls (like 100+), I get a Error: ER_LOCK_DEADLOCK from mysql. I tried to use async.queue but this is somehow not working (I don't know why, maybe I am using is wrongly). How can I run many urls + queries one after another, avoiding parallel execution which I think resulted in a deadlock? Even using async.queue results to a DEADLOCK (not always).
test.js
const request = require('request');
const async = require('async');
const pool = require('./database');
const urls = [
'https://www.quora.com/What-is-the-best-way-to-have-delayed-job-queue-with-node-js',
'https://de.wikipedia.org/wiki/Reinhardt-Zimmermann-L%C3%B6sung',
'https://towardsdatascience.com/the-5-clustering-algorithms-data-scientists-need-to-know-a36d136ef68'
]
let load = function(url) {
request({url: url}, function(error, response, html) {
if(!error) {
console.log(html);
/**
* 1. Parse HTML
* 2. Create Array of Values
* 3. Call pool.query(sql, [values], function(error) { ... })
*/
let data = [{}];
let sql = "INSERT IGNORE INTO tbl_test (title, content) VALUES ?";
let values = [];
data.forEach((item) => { values.push(item) });
pool.query(sql, [values], function(error) {
if(error) throw error;
})
} else {
console.log("handle error...");
}
})
}
let jobs = []
/*urls.forEach((url) => {
//jobs.push(load(url)); // --> Works but fails if the urls list is to big -> mysql deadlock error!
jobs.push(function(callback) { callback(load(url)) });
})*/
let q = async.queue(function(task, callback) {
console.log("Task:", task.uri);
callback();
})
q.drain = function() {
console.log('all task completed');
pool.end();
}
urls.forEach((url) => {
q.push({uri: url}, function(err) {
console.log('finished processing ...')
});
});
databse.js
require('dotenv').config();
const mysql = require('mysql');
let pool = mysql.createPool(
{
connectionLimit: 10,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
}
);
pool.getConnection((err, connection) => {
if(err) {
if(err.code === 'PROTOCOL_CONNECTION_LOST') {
console.log('Database connection lost.')
}
if(err.code === 'ER_CON_COUNT_ERROR') {
console.log('Database has too many connections.')
}
if(err.code === 'ECONNREFUSED') {
console.log('Database connection refused.')
}
if(err.code === 'POOL_CLOSED') {
console.log('Pool is closed.')
}
}
if(connection) {
connection.release()
}
return;
});
module.exports = pool;
I have changed the code to use async.series instead of async.queue, beacuse the tasks would run in parallel in queue (see: https://caolan.github.io/async/docs.html#queue).
test.js
...
let tasks = [];
context.forEach((ctx) => {
tasks.push(function(callback) { load(ctx, callback) });
});
async.series(tasks, function(err) {
if(err) return next(err);
});

Node.js + mysql : no connection on UPDATE query

I'm trying to generate fake names on a MySQL database table for a project. So when I do a select query on the table in order to know the number of rows on which to do my loop of fake names generation, everything works fine. But then when I'm trying to do the actual update query, I don't know why it doesn't work. I do have the loop working as my console.log before and after the query are appearing, but it looks like it doesn't even try to connect to the database when querying an update.
Here after are my client.js and index.js ->
client.js
'use strict';
/**
* Module dependencies.
*/
const mysql = require('mysql');
const debug = require('debug')('db');
/**
* Export the MySQL client pre-handler.
*/
module.exports = {
assign: 'client',
method: (request, reply) => {
debug('Connecting the database')
const client = mysql.createConnection({
user: 'root',
password: 'root',
socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock', // Replaces host and port
database: 'db'
});
client.connect(function(err) {
if (err) {
console.error('Error Connecting: ' + err.stack);
return;
}
console.log('Connected as ID ' + client.threadId);
});
return reply(client);
}
};
index.js
'use strict';
/**
* Module dependencies.
*/
const os = require('os');
const joi = require('joi');
const debug = require('debug')('server');
const faker = require('faker');
/**
* New Hapi server
* (HTTP connection).
*/
debug('New HTTP server');
const Hapi = require('hapi');
const server = new Hapi.Server();
server.connection({
host: 'localhost',
port: 1337,
routes: {
cors: true
}
});
/**
* Fakes data.
*/
server.route({
method: 'PUT',
path: '/faker',
config: {
validate: {
},
pre: [
require('./client')
]
},
handler: (request, response) => {
let nb_rows;
let firstname;
let lastname;
request.pre.client.query('SELECT COUNT(*) as rows FROM clients', function (error, results, fields) {
if (error) throw error;
let nb_rows = results[0].rows;
for(let i = 1; i <= nb_rows ;i++) {
let firstname = faker.name.firstName();
let lastname = faker.name.lastName();
console.log(firstname);
request.pre.client.query('UPDATE clients SET firstname = ?, lastname = ? WHERE ID = ?', [firstname, lastname, i], function (error, results, fields) {
if (error) throw error;
console.log("HHHEEEEEEEEEEEEEEEYYYYYYYYYY");
});
console.log(i);
};
});
}
});
/**
* Start the server.
*/
debug('Start the HTTP server');
server.start(err => {
if (err) {
throw new Error(err)
}
console.log(`Server running at ${server.info.uri}`);
});
I've spend a lot of time looking for answers, and I've seen a lot of issued posts on node.js / mysql update problems. But I haven't find any matching mine, or maybe I didn't understood it.
If you have any ideas, that would be much appreciated.
Thanks.

Serverless with nodejs and mysql => Process exited before completing request

I'm developing some functions with serverless with the nodejs template. I have a service that connects to a mysql database and retrieves some data. Everything is fine when I make the first call, but when I repeat it I receive a "Process exited before completing request" error message.
If I try that same call again, I receive data. So the service is doing right on the odd calls and it's returning the error on the even calls (funny right?). This is the code of the handler function:
module.exports.getAll = (event, context, done) => {
deviceRepository.getAllDevices().then((response) => {
done(null, { response });
}).catch((error) => {
done(error);
});
};
and this is the code of the repository function:
const mysql = require('mysql');
const when = require('when');
const config = require('./config');
const conn = mysql.createConnection({
host: config.RDSHOST,
user: config.RDSUSER,
password: config.RDSPASS,
database: config.RDSDB,
port: config.RDSPORT
});
module.exports.getAllDevices = () => {
const deferred = when.defer();
conn.connect();
conn.query('SELECT * FROM device', (err, rows) => {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(rows);
}
conn.end();
});
return deferred.promise;
};
As you can see I use promises with the 'when' library. I call the 'done' callback in the handler, and there should be a response from the promise in every possible situation.
I can't see what is wrong with this and why is making the odd requests wrong. Anyone can help?
Thanks in advance!
Solved by myself...
The problem is that I was making the createConnection outside of the handler (when I declared the conn constant).
Moving the createConnection declaration inside the handler function works as expected in every call.
const mysql = require('mysql');
const when = require('when');
const config = require('./config');
module.exports.getAllDevices = () => {
const conn = mysql.createConnection({
host: config.RDSHOST,
user: config.RDSUSER,
password: config.RDSPASS,
database: config.RDSDB,
port: config.RDSPORT
});
const deferred = when.defer();
conn.connect();
conn.query('SELECT * FROM device', (err, rows) => {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(rows);
}
conn.end();
});
return deferred.promise;
};
Hope this helps. Thanks!