can i connect to mysql database from sails without defining models? - mysql

I'm using sails.js and sails-MySQL and I want to connect to a MySQL database. I have tables, functions, and procedures in my workbench. now I want to know that is it necessary to create model files and define my schema again?

Yes, you can connect to your DB without defining models. However bare in mind that you will have to write raw queries every time. So first you need to define your DB connection in your datastores.js file. Then you can do the following in some of your controllers when you want to get something from your DB (say you have a table users and you want to get all of them):
var myDBStore = sails.getDatastore(); //gets the default datastore.
var query = "SELECT * FROM users;";
myDBStore.sendNativeQuery(query).exec(function (err, nativeResult) {
if (err) {
return res.send(err);
}
return res.send(nativeResult.rows);
});
or using the modern way in an async function:
var myDBStore = sails.getDatastore(); //gets the default datastore.
var query = "SELECT * FROM users;";
var nativeResult;
try {
nativeResult = await myDBStore.sendNativeQuery(query);
} catch (err) {
return res.send(err);
}
return res.send(nativeResult.rows);
More info here: https://sailsjs.com/documentation/reference/waterline-orm/datastores in section "Using datastores without a model"

Related

Use 1 single MySQL connection to provide result to multiple Users

I have used one signle connection object of MYSQL in node JS to serve for multiple users.
I mean to say that MySQL connection will be created upon starting the script & it will remain same until the life of the node script/server.
Practically, this is possible to do & i have done the same. Please take a look at below code of NodeJS/MySQL script.
#################################
var http = require('http');
var mysql = require('mysql');
var con = mysql.createConnection({
host: "192.168.1.105",
user: "root",
password: "XXXXXX",
database: "mydb"
});
con.connect(function(err) {
if (err) {
console.error('error: ' + err.message);
process.exit(1);
}
http.createServer(function (req, res) {
continueExecution(req,res);
}).listen(8082);
});
async function continueExecution(req,res){
res.write('calledddd\n');
for (let step = 0; step < 50; step++) {
// Runs 5 times, with values of step 0 through 4.
var bar = `Company Inc ${step}`;
var sql = `INSERT INTO customers (name, address) VALUES ('${bar}', 'Highway 37')`;
res.write(sql + "\n");
con.query(sql, function (err, result) {
if (err) throw err;
res.write("1 record inserted\n");
});
}
res.write('reached\n');
for (let ste = 0; ste < 50; ste++) {
res.write('started Update\n');
var bar = `Company Inc ${ste}`;
var sql = `UPDATE customers SET name = 'UPDATE RECORD' WHERE name = '${bar}'`;
con.query(sql, function (err, result) {
if (err) throw err;
res.write(result.affectedRows + " record(s) updated\n");
if(ste == 50) {
res.writeHead(200, {'Content-Type': 'text/html\n'});
res.write('Databse connected\n');
res.end();
}
});
}
}
#################################
I have several questions in my mind as i am technical expert. But i didn't find any resources over my questions. Please help me on this
Q1. Are there any type of consequences of using one single MySQL connection to provide response to multiple users?
Q2. Let's take an example.
100 users wants to access table name "users_data" at the same time. 25 users are updating their records on the same table with unique primary key. 50 users are selecting their records. another 25 users deleting their records.
All these operations are being done at the same time via parallel Node Script calls from remote device.
To complete all these MySQL transactions, system is using only 1 database connection.
What will happen in this case?
To answer your questions, one of the consequences of using a unique connection is that it can lead to slower request execution.
In fact, even if node will make the requests asynchronously, your database will execute all those requests synchronously, so one after the other in the order they came. As node makes the requests asynchronously, the order in which they are executed by your database is not granted, and the issue you are referencing to might happen.
One easy way to avoid this is to use a connection pool which will create a given number of connection, using the same db user. Here are some links that might help you with this :
using a connection pool with node.js
connect a mysql database with node.js

Validate existing email in mysql in a node.js server

I am having the following problem with node and mysql:
I have a function registerUser that takes the req.body with the user credentials and store them into a mysql db.
First of all I check that the email provided does not already exist. I have done this validation working with postgres in the following manner: if(user.rows.lenght!==0) return res.send("user already exist")
Then I pass to the next line of code that insrts the credentials into the db.
My problem is that using mysql, user.rows is undefined. I am having trouble extracting the data from the response which would allowme to perform some sort of validation.
My code is like this:
registerUser:async(req,res)=>{
const resolver=Resolver(res)
try {
//get data from req.body
const {userName, userEmail, userPassword}=req.body
//Check if user alreday exist on db by email
const user=db.query("SELECT * FROM users WHERE user_email=?",
[userEmail],(err,result)=>{
if(err) console.log(err)
else if(result.length!==0) return res.status(401).send('user already exist')
})
The callback function of the query does not stop the execution of the registeruser function. Also, the result comes with the user credentaials which is what I need, but I dont know how to extract it from the callback in order to use it in the scope of registerUser

Preventing SQL Injection from node.js using mysql driver with multipleStatements: true

I've been developing an "Employee leave management" web app project for our internal use using node.js with express and ejs template. Now, my employer wants me to make the app accessible through internet and I'm worried about SQL injection.
Let's say I have a button like this in html:
Edit
This will GET from index.js file:
const { edit } = require("./request");
app.get("/edit/:ReqID", edit);
This will then go to module edit in request.js file:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID="${ReqID}";
SELECT * FROM table2 WHERE ReqID="${ReqID}";`;
db.query(squery, function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
There might be two or more queries in there and I'm using mysql driver for node.js with multipleStatements: true and I'm aware of warning "Support for multiple statements is disabled for security reasons (it allows for SQL injection attacks if values are not properly escaped)." This will return something like http://localhost:port/edit/reqid on the browser address box. I saw a video from youtube that says SQL Injection can be done through the browser's address box like http://localhost:port/edit/reqid;";SELECT * FROM users; so I did that and for sure I can see that syntax being send to the server. So I follow the suggestion in the video to do a placeholder like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
let squery = `SELECT * FROM table1 WHERE ReqID= ?;
SELECT * FROM table2 WHERE ReqID= ?;`;
db.query(squery, [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I try the extreme http://localhost:port/edit/reqid;";DELETE FROM users; and http://localhost:port/edit/reqid;";DROP TABLE users; separately and it works! First it deletes data from users tble and for sure the second drop table command also worked. After the first attempt, I refresh the browser with the same sql injection syntax and I've got this message:
{"code":"ER_BAD_TABLE_ERROR","errno":1051,"sqlMessage":"Unknown table 'users'","sqlState":"42S02","index":1,"sql":"SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";SELECT * FROM table1 WHERE ReqID= "ReqID;";drop table users;";"}
So, the table users clearly have been dropped from the database.
Update:
I did further testing based on the information I gained from this answer and I did something like this:
module.exports = {
edit: (req, res) => {
let ReqID= req.params.ReqID;
db.query(`SELECT * FROM table1 WHERE ReqID= ?; SELECT * FROM table2 WHERE ReqID= ?;` , [ReqID, ReqID], function (err, result) {
if (err) {
return res.status(500).send(err);
}
res.render("edit.ejs", {
srecords1: result[0],
srecords2: result[1]
})
})
}
}
Then I re-test with multiple variation of http://localhost:port/edit/reqid;";DROP TABLE users; (double quote in between)
http://localhost:port/edit/reqid;';DROP TABLE users; (single quote in between) etc. and it doesn't seem to be dropping the table anymore. However, I still see the statement being sent to the server so I'm still wary of the DROP syntax being effective somehow.
Update 2:
Note: Fortunately, the deployment has been delayed and I have more time to sort out the issue.
After researching for a while, taking the comments into consideration and testing multiple method, I came up with this structure:
function(req, res) {
let dcode = [req.body.dcode];
let query1 =`SELECT col1, col2 FROM table1 WHERE DCode=?`;
db.query(query1, dcode, function(err, result_1) {
if (err) {
return res.status(500).send(err);
}
let query2 =`SELECT col1, col2 FROM table2 WHERE DCode=?`;
db.query(query2, dcode, function(err, result_2) {
if (err) {
return res.status(500).send(err);
}
res.render("login.ejs", {
result1: result_1,
result2: result_2
});
});
});
}
Which is simple enough and no major change to my current codes. Would this be sufficient to prevent SQL injection in node.js?
Allowing multi-statement strings, itself, invites SQL injection. So, avoid it.
Plan A:
Consider ending an array (perhaps in JSON) to the server; let it then execute each statement, and return an array of resultsets.
But it would be simpler to simply issue the statements one at a time.
(If the client and server are far apart, the one-statement-at-a-time method may cause a noticeable latency.)
Plan B:
Build suitable Stored procedures for any multi-statement needs. This, where practical, avoids multi-statement calls. And avoids latency issues (usually).
Here are a few suggestions that might help:
Never use template strings like this: Select * from table where id = ${value}. SQL injections will happen - 100%!. Instead you should use build in driver defense mechanism. Like this: query('Select * from table where id = ?', [value]). This should prevent SQL injection.
Use single statements per query. If you need to do multiple operations in one request to database - consider creating stored procedure. Stored procedures also have build in security mechanism.
Consider using query builder or ORM. They also have additional layer of security on top of build in driver one.
You could also explicitly escape SQL string with help of 3rd party library.

Hooks not triggering when inserting raw queries via sequelize.query()

I have the following Employee model for a MySQL database:
var bcrypt = require('bcrypt');
module.exports = (sequelize, DataTypes) => {
const Employee = sequelize.define(
"Employee",
{
username: DataTypes.STRING,
password: DataTypes.STRING,
}, {}
);
return Employee;
};
Seeding the database is done by reading a .sql file containing 10,000+ employees via raw queries:
sequelize.query(mySeedingSqlFileHere);
The problem is that the passwords in the SQL file are plain text and I'd like to use bcrypt to hash them before inserting into the database. I've never done bulk inserts before so I was looking into Sequelize docs for adding a hook to the Employee model, like so:
hooks: {
beforeBulkCreate: (employees, options) => {
for (employee in employees) {
if (employee.password) {
employee.password = await bcrypt.hash(employee.password, 10);
}
}
}
}
This isn't working as I'm still getting the plain text values after reseeding - should I be looking into another way? I was looking into sequelize capitalize name before saving in database - instance hook
Your hooks won't be called until you use model's function for DB operation , so if you are running raw query , hooks will never be fired,
Reason : You can write anything inside your raw query , select/insert/update/delete anything , how does sequelize.js know that
it has to fire the hooks. This is only possible when you use methods
like
Model.create();
Model.bulkCreate();
Model.update();
Model.destroy;
And as per DOC raw query doesn't have hooks option to add.
And for MODEL queries you can check that it has option to
enable/disable hook.

Connecting to RDS database from node.js

I am trying to learn node.js so that I can actually get started on working on my personal project. I have been trying to follow the examples in the book "Learning node.js" (by Marc Wandschneider). I, at this point, decided to forgo practicing his example, and go straight into using his code as framework for my practice code.
In my practice code, all I am trying to do is connect to my RDS database (no, I am not using Elastic Beanstalk, btw), and output contents of one of the tables. Seems simple enough, but when I whip up the code for it (based on the book), it seems to attempt connection, but get hung up in the process. This is my code:
var pool = require('generic-pool');
var async = require('async');
var mysql = require('mysql');
var host = "<database-name>.cu8hvhstcity.us-west-2.rds.amazonaws.com",
database = "<database-name>",
user = "<username>",
password = "<someLongPassword>";
var dbClient;
async.waterfall([
// 1. establish connection to database
function (callback) {
console.log("Connecting to database " + database + "...");
dbClient = mysql.createConnection({
host: host,
database: database,
user: user,
password: password,
port: 3306
});
dbClient.connect();
},
// 2. select all from a table (let's go for locations)
function (cb)
{
var query = "SELECT * FROM locations"
console.log("running query \"" + query + "\"...");
dbClient.query(query, cb);
},
function (rows, fields, callback)
{
console.log(fields);
for (var i = 0; i < rows.length; i++)
{
console.log(JSON.stringify(rows, null, '\t'));
}
}
], function (err, results) {
if (err)
{
console.log("An error occurred...");
console.log(err);
}
else
{
console.log("Everything successfully completed!");
}
dbClient.end();
})
This is better than first attempt, when I put a database member to the argument passed to mysql.createConnection(), and it complained that database was unknown. In neither case did either "An error occurred..." nor "Everything successfully completed!" output to the window.
Is there any async stuff going on that is resulting in some kind of non-terminating infinite loop or something? How do I fix this?
The book has an associated GitHub page
NOTE:
Neither my example nor the cited GitHub code make use of that pool variable, so it can simply be commented out. All you need to do to run this yourself is to say npm install async,npm install mysql (as well as creating a dummy RDS database to point to, that contains locations table) before copying, pasting, and running this code.
EDIT:
I fixed the issue with database. I realized that the name of the database used '_', not '-'. Same issue (code hangs) still persists...
I did the following:
In the second function in the array, I needed two parameters, not one.
I fixed thus:function(results, cb)
The third function simply needed to callback(null)