How do format exports of multiple modules in NodeJS - mysql

I have the following code in my db.js file:
const mysql2 = require('mysql2/promise');
// Connect to server (locally for development mode) ----- NEW VERSIN
const pool = mysql2.createPool({
host : "ENDPOINT",
user : "admin",
password : "PASSWORD",
port : "3306",
database : "DATABASE",
waitForConnections: true,
connectionLimit: 10,
queueLimit: 0
});
module.exports = pool;
And in my app.js file I have:
const pool = require('./db');
async function checkUser(username) {
const result = await pool.query('SELECT * from users WHERE username = ?', [username]);
if (result[0].length < 1) {
throw new Error('Row with this username was not found');
}
return result[0][0];
}
async function check() {
let user = await checkUser("username");
console.log(user);
}
check();
But I'm getting the error:
(node:42037) UnhandledPromiseRejectionWarning: TypeError: pool.query is not a function
This is weird, because when I run all the code in the db.js file it works fine, so I'm probably messed up the export/require bit of it, please help!
ANSWER: HOW TO EXPORT MULTIPLE FUNCTIONS
In the document you are exporting, write as follows:
module.exports = {FunctionName1, FunctionName2, FunctionName3};
In the document you are importing to, write the following:
const {FunctionName1, FunctionName2, FunctionName3} = require('./whereyouareimportingfrom');

I had done the exports the wrong way, I tried exporting two functions by doing: "module.exports = ConnectDb, pool;" And that didn't work. When I removed the first function and only exported "pool" it worked.

Related

MYSQL returns error when a query is made through an endpoint

I'm developing a simple GET endpoint using NodeJS, express and MySql, but whenever i use the mysql.query('select * from table'), through an service, the server is shutdown. The same query is successful when i declare it in the database.js file, but not when integrated with the rest of my system.
My database.js is as follows:
const mysql = require("mysql");
const con = mysql.createConnection({
host: "localhost",
user: "root",
password: "rootpwd",
port: 3306,
database: "blog",
});
con.query("select * from post", (er, row) => {
if (er) throw er;
console.log(row);
return row;
});
This con.query function is only declared for test purpose, and deleted when endpoint is called. When i run my server and this query is declared, it logs in my console all the content in this table.
When i run node database.js all my entries in posts table are shown in console.
But when i call, in another file,
database.query('select * from post', (er, rows) => { if (er) throw er return rows })
The localhost is shutdown with the following message:
-> starting at object with constructor 'Query'
| property '_timer' -> object with constructor 'Timer'
--- property '_object' closes the circle
at JSON.stringify (<anonymous>)
at stringify (/home/guilherme/Documentos/Projects/rest_api/node_modules/express/lib/response.js:1150:12)
at ServerResponse.json (/home/guilherme/Documentos/Projects/rest_api/node_modules/express/lib/response.js:271:14)
at /home/guilherme/Documentos/Projects/rest_api/server/route/postsRoute.js:7:19
at processTicksAndRejections (internal/process/task_queues.js:95:5)
I've installed body-parser in my project and i'm using express.json() as a middleware in my app:
const express = require("express");
const app = express();
app.use(express.json());
app.use("/", require("./route/postsRoute"));
app.listen(process.env.PORT || 3000, () =>
console.log(`Server running on port ${process.env.PORT || 3000}`)
);
My route file:
const express = require("express");
const router = express.Router();
const postService = require("../service/postService");
router.get("/posts", async (req, res) => {
const posts = await postService.getPosts();
res.status(200).json(posts);
res.end();
});
My Service file:
exports.getPosts = async () => {
const test = await postsData.getPosts();
console.log(test, "this is what is returned");
return test;
};
And finally, my data file:
exports.getPosts = () =>
database.query("select * from post;", (er, rows) => {
if (er) throw er;
return JSON.stringify(rows);
});
this JSON.strinfy was inserted for test purpose, and the error is returned the same way.
Node version: v14.18.0
dependecies:
"dependencies": {
"axios": "^1.2.1",
"body-parser": "^1.20.1",
"express": "^4.18.2",
"jest": "^29.3.1",
"mysql": "^2.18.1",
}
If anyone needs more information to help me debug this, please let me know.
I've tried parsing the content that is returned in my query, but it didn't returned anything useful. I've added async and awaits, but it didn't helped either.
I'm expecting to see all my tables content when i access the /posts route.
You're converting the result to JSON twice, in the data file and the services file. You should only do the JSON conversion in one place, not both.
I recommend doing it only in the service, so in the data code use
exports.getPosts = () =>
database.query("select * from post;", (er, rows) => {
if (er) throw er;
return rows;
});
So the main issue was that 'mysql' lib does not handle promises correctly.
I've added mysql2 and and changed my Data.js file to
database.promise().query("select * from post;");
And it works just fine now.

How to store my session MySQL with express-mysql-session

see code below. its not storing a session in my database. I cant figure it. I have executiion file app.js. I have everythin setup and running but storing sessions in database dont work.. I posted the same question before but got no luck...
var express = require('express');
var mysql = require('mysql2');
var path = require('path');
var session = require('express-session');
var port = 3000;
var app = express();
var MySQLStore = require('mssql-session-store')(session);
app.use(session({
secret: 'tee tee',
resave: false,
saveUninitialized: false,
store: new MySQLStore(options)
}));
var connection = mysql.createConnection ({
host: 'localhost',
user: 'root',
password: '....',
database: 'node'
});
connection.connect(function(error) {
if(error) {
throw error;
} else {
console.log("We are now successfully connected with mySQL");
}
});
var options = {
connection: connection,
ttl: 3600,
reapInterval: 3600
};
app.get('/', (req, res) => {
res.sendFile('home.html', {
root: path.join(__dirname, './views')
});
});
app.listen(port, (req, res) => {
console.log('the server is running, ' + ' please, open your browser at http://localhost:%s', port);
});
It appears as you are using mssql-session-store (Microsoft SQL) to connect to MySQL.
Try using the npm package express-mysql-session:
https://www.npmjs.com/package/express-mysql-session
Like MySQL and MSSQL, there are many variants of SQL and SQL like databases so it is important you are precise with the database you are using.
Your line (below) should be the only issue. It is specifying the use of the Microsoft SQL session store package when you are trying to use the MySQL session store.
var MySQLStore = require('mssql-session-store')(session);
By changing it to
var MySQLStore = require('express-mysql-session')(session);
You specify the use of the mysql session store (Make sure you download the NPM package above first)

MySQL query in Supertest returning {sql, bindings} instead of results

I'm running supertest + jest against a route that queries a mysql db, run locally. Testing through insomnia returns the correct information, but none of my db queries return correct values when using supertest.
// test.js
import request from 'supertest';
import db from '../database';
import app from './app';
describe('/', () => {
test('should return db results', async () => {
const mock = jest.spyOn(db, 'query');
const response = await request(app).get(`/`)
console.log(response.error);
expect(dbMock).toBeCalled();
expect(response.status).toBe(200);
});
});
// route (excerpt from app.js)
import returnUserInfo from './returnUserInfo';
app.get(
'/',
returnUserInfo
);
// returnUserInfo.js
import db from '../database';
export default async function returnUserInfo(req, res, next) {
const sql = 'select `userInfo` from `users` where `email` = ?';
const bindings = ['test'];
try {
const data = await db.query(sql, bindings);
if (data.length !== 1) {
throw new Error(`Invalid ${Object.keys(data)}`);
}
return res.json(data[0]);
} catch (err) {
return next(err);
}
}
// database.js
import { PoolConnection } from 'mysql';
const db = new PoolConnection(
{
host: 'localhost',
user: 'root',
password: 'foobar',
database: 'db',
connectionLimit: 100,
},
false,
);
export default db;
As mentioned before, testing with postman/insomnia returns the correct result...
When testing with supertest and logging the error:
Error: Invalid sql,bindings
It appears there had been a node_module manual mock for the mysql db which was overriding all calls to the database and simply returning
{ sql, bindings }
Note to self: when things don't make sense...check that they're supposed to be making sense.

Node.js: how to apply util.promisify to mysql pool in its simplest way?

I saw another thread and and the post Create a MySQL Database Middleware with Node.js 8 and Async/Await, but right now I just want to use the util.promisify in the most simplest and straight-forward way that I can think, applying it to mysql connection-pool. Regarding the Node.js documentation, bellow is my code snipet:
exports.genAdminCfg = function (app) {
let sql = 'SELECT * FROM nav_tree;';
let pool = require('mysql').createPool({
host: 'localhost',
user: 'root',
password: 'mysql',
database: 'n4_ctrl',
connectionLimit: 4,
multipleStatements: true
});
/* --- Works fine without "promisify":
pool.query(sql, function (err, rows) {
if (err) {
console.log(err);
return err;
} else {
app.locals.adminCfg = genCfg(rows);
app.locals.adminCfg.dbConnectionPool = pool;
}
});
*/
/* --- Does not worke with "promisify":
TypeError: Cannot read property 'connectionConfig' of undefined
at Pool.query (/usr/home/zipper/node/n4.live/node_modules/mysql/lib/Pool.js:194:33)
*/
require('util').promisify(pool.query)(sql).then(function (rows) {
app.locals.adminCfg = genCfg(rows);
app.locals.adminCfg.dbConnectionPool = pool;
}, function (error) {
console.log(error);
});
};
The code I commented-out works fine without promisify. But the similar code next to it with promisify does not work and shows TypeError: Cannot read property 'connectionConfig' of undefined. What's wrong with the code?
node version = v8.1.4
It always should be expected that a method relies on the context, unless proven otherwise.
Since pool.query is a method, it should be bound to correct context:
const query = promisify(pool.query).bind(pool);
This may be unneeded because there are promise-based alternatives for most popular libraries that could make use of promises, including mysql.
Before sharing my example, here are greater details about bind method and async function.
const mysql = require('mysql')
const { promisify } = require('util')
const databaseConfig = {
connectionLimit : 10,
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME
}
const pool = mysql.createPool(databaseConfig)
const promiseQuery = promisify(pool.query).bind(pool)
const promisePoolEnd = promisify(pool.end).bind(pool)
const query = `select * from mock_table limit 1;`
const result = await promiseQuery(query) // use in async function
promisePoolEnd()

establishing a connection to database using environment variables

I'm creating an API for my app that fetches some data from my amazon db.
I have added environment variables to a custom.sh file in profile.d on my server. The weird thing is, I can print out those variables from my api, but the only variable that works when creating the connection is the user variable.
When I hard code the database credentials in the connection string it works just fine.
here is my custom.sh where I declare the environment variables
#custom environment variables, with the actual values removed
export DB_HOST=value1;
export DB_PASS=value2;
export DB_USER=value3;
export DB_NAME=value4;
here is my nodejs expressjs api file
var express = require('express');
var app = express();
var mysql = require('mysql');
var DB_HOST = formatEnvironmentVariable(process.env.DB_HOST);
var DB_USER = formatEnvironmentVariable(process.env.DB_USER);
var DB_PASS = formatEnvironmentVariable(process.env.DB_PASS);
var DB_NAME = "'"+process.env.DB_NAME+"'";
function formatEnvironmentVariable(env) {
env = "'"+env+"'";
return env
}
var pool = mysql.createPool({
host : DB_HOST,
user : DB_USER,
password : DB_PASS,
database : DB_NAME,
connectionLimit: 100,
debug: false
})
function QueryTheDatabase(req, res, querystring) {
pool.getConnection(function(err,connection){
if (err) {
connection.release();
res.json({"code" : 100, "status" : "Error in connection database"});
return;
}
console.log('connected as id ' + connection.threadId);
connection.query(querystring,function(err,rows){
connection.release();
console.log("error? "+err);
if(!err) {
res.json(rows);
}
});
connection.on('error', function(err) {
console.log("error");
res.json({"code" : 100, "status" : "Error in connection database"});
return;
});
});
}
app.get('theurl', function(req, res) {
QueryTheDatabase(req, res, "the query that works fine");
});
var server = app.listen(3000, function () {
var host = server.address().address;
var host = server.address().port;
});
I'm not exactly sure why only the DB_USER variable is set properly (it may be that some other process is sending that information). If you're running your shell script as such:
$ ./custom.sh
It won't work. It's essentially creating a sub-shell and the exported variables are local to that shell (they won't affect the parent)
The only way to make environment variables accessible to your node process would be to source the file first.
$ source ./custom.sh
I got it to work finally, turns out I didnt need to add ' ' with my format function.
instead of
var DB_HOST = formatEnvironmentVariable(process.env.DB_HOST);
var DB_USER = formatEnvironmentVariable(process.env.DB_USER);
var DB_PASS = formatEnvironmentVariable(process.env.DB_PASS);
var DB_NAME = "'"+process.env.DB_NAME+"'";
I wrote
var DB_HOST = process.env.DB_HOST;
var DB_USER = process.env.DB_USER;
var DB_PASS = process.env.DB_PASS;
var DB_NAME = process.env.DB_NAME;
For this I'll suggest To use
config npm
https://www.npmjs.com/package/config