pass argument to mysql connection string - node.js

Below is my code (under file db.js)
function connection() {
try {
const mysql = require('mysql2');
const pool = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase",
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
const promisePool = pool.promise();
return promisePool;
} catch (error) {
return console.log(`Could not connect - ${error}`);
}
}
const pool = connection();
module.exports = {
connection: async () => pool.getConnection(),
execute: (...params) => pool.execute(...params)
};
And to use it, I simply add var db = require('./../db.js'); at the top of the files that requires it. Additionally, below is how I'd execute any sql statements
const myResult = await db.execute("Select COUNT(*) AS dataCount from teachers WHERE username = ?", [username]);
But now I need to separate my database according to countries. So my database would be named with mydatabase_ + the country code, for example mydatabase_my , mydatabase_jp, mydatabase_uk etc etc. So how do I pass the country code to the function connection() ?
I tried something like
function connection(country) {
try {
const mysql = require('mysql2');
const pool = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase_" + country,
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
const promisePool = pool.promise();
return promisePool;
} catch (error) {
return console.log(`Could not connect - ${error}`);
}
}
const pool = connection(country);
module.exports = {
connection: async (country) => pool.getConnection(country),
execute: (...params) => pool.execute(...params)
};
and at the calling page, I did
var db = require('./../db.js');
db.connection("my")
but for the above, I will get ReferenceError: country is not defined which refers to country at the line const pool = connection(country);

Here's how I did it:
const mysql = require('mysql2');
const pool = function(country){
var mydb = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase_" + country,
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
return mydb;
}
const promisePool = function(country){
return pool(country).promise();
}
module.exports = {
connection: async (country) => promisePool(country).getConnection(),
execute: (country,...params) => promisePool(country).execute(...params)
};
And in the page that uses mysql will have something like this:
db.connection(country)
const myResult = await db.execute(country, "Select * from schools");

Related

Nextjs and Sequelize database connection singleton problem

I'm trying to use nextjs and sequelize together. It works but I have a problem with each request from the database it opens a connection for DB. I have a Db class and export an instance from this class then use it wherever I need.
import { FindOptions, Model, ModelCtor, Sequelize } from 'sequelize'
import { initModels, Models } from './models';
class Db {
connection: Sequelize | null = null;
constructor() {
this.connection = this.connectDb()
initModels(this.connection)
}
retrieveAll(model: ModelCtor<Model<any, any>>, options: FindOptions<any> = {}) {
return model?.findAll(options);
}
retrieve(model: ModelCtor<Model<any, any>>, options: FindOptions<any> = {}) {
return model?.findOne(options);
}
connectDb() {
const dbName = process.env.DB_NAME
const dbHost = process.env.DB_HOST
const dbUser = process.env.DB_USER as string
const dbPassword = process.env.DB_PASSWORD
const sequelize = new Sequelize({
dialect: 'postgres',
host: dbHost,
port: 5432,
database: dbName,
username: dbUser,
password: dbPassword,
pool: {
max: 5,
idle: 86400000,
}
});
try {
sequelize.authenticate();
console.log('Connection has been established successfully.');
return sequelize;
} catch (error) {
console.error('Unable to connect to the database:', error);
return null;
}
}
closeConnectionDb() {
this.connection?.close()
}
}
export const db = new Db();
For example, I make a query database for login then on homepage I get merchant list from the database. I see two times "Connection has been established successfully.". How can I solve this problem? By the way, it works very well in the production server. I think it's about module caching but I'm not sure.

Local Postgres Database won't connect in Node backend

I have setup the node backend and try to connect to the local pgAdmin. When i try to run the Node app.js it's always shows the following error.
Error: connect ECONNREFUSED 127.0.0.1:5400
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1247:16) {
errno: -4078,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '127.0.0.1',
port: 5400
}
following is my code.
import('express').then((express) => {
import('got').then((g) => {
import('pg').then((p) => {
var cors = require('cors');
const got = g.default
const pg = p.default
const app = express.default()
app.use(cors())
const rtr = express.Router()
const masterRouter = express.Router()
const colorRouter = express.Router()
const bomRouter = express.Router()
const userRouter = express.Router()
const cdtRouter = express.Router()
const historyRouter = express.Router()
const { Pool } = pg
const baseUrl = 'https://xxxx.com/csi-requesthandler/api/v2'
const login = `${baseUrl}/session`
const customers = `${baseUrl}/customers`
const suppliers = `${baseUrl}/suppliers`
const styles = `${baseUrl}/styles`
const color_ways = `${baseUrl}/colorways`
const materials = `${baseUrl}/materials`
const boms = `${baseUrl}/apparel_boms`
const bom_revs = `${baseUrl}/apparel_bom_revisions`
const part_materials = `${baseUrl}/part_materials`
const db_user = 'admin'
const db_password = 'admin'
const db_host = 'localhost'
const db_catalog = 'postgres'
const db_port = '5400'
if (!db_user || !db_password || !db_host || !db_catalog) {
console.error('Database configuration params are missing from environment!')
process.exit(-1)
}
const pool = new Pool({
user: db_user,
host: db_host,
database: db_catalog,
password: db_password,
port: db_port
})
/**
*
* #param {*} req
* #param {*} res
* #param {()} next
* #returns call to next
*/
function tokenValidator(req, res, next) {
if (!req.headers.token) {
return res.status(400).json({ error: "Token must required" })
}
req.tokenCookie = req.headers.token
next()
}
rtr.use(express.json({ limit: '50mb' }))
rtr.use('/master', tokenValidator)
rtr.use('/master', masterRouter)
rtr.use('/color', tokenValidator)
rtr.use('/color', colorRouter)
rtr.use('/bom', tokenValidator)
rtr.use('/bom', bomRouter)
rtr.use('/user', tokenValidator)
rtr.use('/user', userRouter)
rtr.use('/cdt', cdtRouter)
rtr.use('/history', historyRouter)
app.use('/api', rtr)
app.listen(PORT, () => {
console.log(`Server is Listening of port ${PORT}`)
})
const cdt_map = []
rtr.get('/connection', (req, res) => {
return res.status(200).json({ success: 'Api Connected' });
})
rtr.post('/login', (req, res) => {
const rbody = req.body;
if (!rbody.username) {
return res.status(400).json({ error: 'Username not specified' })
}
if (!rbody.password) {
return res.status(400).json({ error: 'Password not specified' })
}
pool.query(`SELECT users.id, roles.id AS role, roles.role AS role_desc FROM users INNER JOIN roles ON roles.id = users.role WHERE LOWER(users.username) = LOWER('${rbody.username.toLowerCase().trim()}')`, (err, dbr) => {
if (err) {
return res.status(500).json({ error: 'Unable to query existance of the user', db: err })
}
if (dbr.rowCount == 0) {
return res.status(401).json({ error: 'User does not exist!' })
}
const uid = dbr.rows[0].id
const role = dbr.rows[0].role
const role_desc = dbr.rows[0].role_desc
got.post(login, { json: rbody })
.then((success) => {
const respBody = JSON.parse(success.body)
const cookie = { cookie: respBody.token, user_id: uid, type: role, desc: role_desc }
return res.contentType('application/json').send(cookie)
}, reject => {
if (reject.response.statusCode == 400) {
return res.status(400).json({ "error": "Invalid username or password" })
}
})
.catch((err) => { console.error(err) })
})
})
I can't figure out what's the error here. my locally run pgAdmin url is http://localhost:5432. i have checked and tried so many methods and still couldn't figure out the error. if anyone can help me out would be really appreciated.
Usually postgres database listens on the default port: 5432.
Change your db_port to 5432 and restart the node server.
Here is how to check which port is being used by postgres database.
sudo netstat -plunt |grep postgres
or
if you able to run psql then run this command
\conninfo
or
If you able to connect to pgAdmin (FYI pgAdmin runs on a different server on a different port from postgres database server) get the port from the server properties.

Node.js mssql package select query cache problem

I try to retrieve data from SQL Server using npm mssql package. When I used postman to make a request, I got same data (resultset) at every time although data was changed.
My SQL Server connection config and code is here:
const sql = require('mssql');
const config = {
user: 'XXXX',
password: 'XXXX',
server: 'XXXX',
database: 'XXXX',
pool: {
max: 50,
min: 0,
idleTimeoutMillis: 10
},
options: {
trustServerCertificate: true
}
}
const getConnection = async () => {
try {
const conn = await sql.connect(config);
return {pool: conn, err: false, errorMSG: null};
} catch (err) {
return {conn: null, err: true, errorMSG: err}
}
};
And my db function :
const conn = await getConnection();
if (conn.err == false) {
try {
let result = await conn.pool.request()
.input('registryNumber', sql.NVarChar(50), registryNumber)
.input('password', sql.NVarChar(50), password)
.query('SELECT role FROM [OperatorLogin].[dbo].[TBL_users]' +
'WHERE [TBL_users].[registryNumber] = #registryNumber AND [TBL_users].[password] = #password AND [TBL_users].[isActive] = 1 ')
if (result.rowsAffected[0] >= 1) {
return { Execution: true, ExecutionCode: 1, ExecutionMessage: "Record is available", ExecutionData: result.recordset }
}
else {
return { Execution: true, ExecutionCode: 2, ExecutionMessage: "No records", ExecutionData: "" }
}
}
catch (err) {
return { Execution: false, ExecutionCode: -1, ExecutionMessage: "DB pool request error : " + err, ExecutionData: "" }
}
}
else {
return { Execution: false, ExecutionCode: -1, ExecutionMessage: " DB error : " + conn.errorMSG, ExecutionData: "" }
}
On the other hand, I use pm2 and reverse proxy on Windows server.
What may be problem ? What am I missing ?

How to mock SQL Server connection pool in node

I am attempting to mock SQL Server connection pool so that I can test the function of a DAL.
I have a connection pool file
connectionPool.js
const sql = require('mssql');
const log = require('../services/logger');
const config = {
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
server: process.env.SERVER,
database: process.env.DATABASE
};
const poolPromise = new sql.ConnectionPool(config)
.connect()
.then(pool => {
log.info('Connected to SQL Server');
return pool;
})
.catch(err => {
log.error(err, 'Database connection failed');
});
module.exports = poolPromise;
and I use it in the DAL. Very stripped down, but the essentials are there.
const {poolPromise} = require('./connectionPool');
const getData = async () => {
const pool = await poolPromise;
const request = pool.request()
const result = await request('SELECT * FROM table');
}
This way, the connection pool is only created once per application. (See How can I use a single mssql connection pool across several routes in an Express 4 web application?)
I want to mock the mssql module so that the connection pool function still works. I have tried multiple options. How to mock SQL Server connection pool using Jest? gets me close, but its not quite there.
__mocks/mssql.js
const mockExecute = jest.fn();
const mockInput = jest.fn(() => ({ execute: mockExecute }));
const mockRequest = jest.fn(() => ({ input: mockInput }));
jest.mock('mssql', () => ({
ConnectionPool: jest.fn(() => ({request: mockRequest})),
NVarChar: jest.fn()
}));
const sql = require('mssql');
module.exports = sql;
However I get the error
TypeError: (intermediate value).connect is not a function
17 |
18 | const poolPromise = new sql.ConnectionPool(config)
19 | .connect()
| ^
20 | .then(pool => {
21 | log.info('Connected to SQL Server');
22 | return pool;
This may be a solution.
A bit of refactoring of connectionPool.js
const sql = require('mssql');
const log = require('../services/logger');
const config = {
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
server: process.env.SERVER,
database: process.env.DATABASE
};
if (process.env.NODE_ENV === 'development') {
config.options = {
encrypt: false,
trustServerCertificate: true
};
}
const connectionPool = new sql.ConnectionPool(config);
const poolPromise = connectionPool
.connect()
.then(pool => {
log.info('Connected to MSSQL');
return pool;
})
.catch(err => {
log.error(err, 'Database connection failed');
});
module.exports = poolPromise;
Then in /__mocks__/mssql.js
'use strict';
const mockExecute = jest.fn();
const mockInput = jest.fn().mockReturnValue({ execute: mockExecute });
const mockQuery = jest.fn().mockReturnValue({recordset: 'Mock data'});
const mockRequest = jest.fn().mockReturnValue({
input: mockInput,
query: mockQuery
});
const mockTransaction = jest.fn().mockImplementation(() => {
return {
begin: callback => callback(),
commit: jest.fn(),
rollback: jest.fn()
};
});
const mockConnect = jest.fn().mockImplementation(() => {
return Promise.resolve({ transaction: mockTransaction });
});
jest.mock('mssql', () => ({
ConnectionPool: jest.fn().mockReturnValue({
request: mockRequest,
connect: mockConnect
}),
Request: mockRequest,
NVarChar: jest.fn()
}));
const mssql = require('mssql');
module.exports = mssql;
It appears to work, but I am not sure if it is correct

Using promise in connection string password field returns database not connected

Decrypting password from environment parameter using aws.kms gets not resolved to use in pg-promise connection object. Database can not connect because of empty password. Password gets resolved after about one second from my local machine, long after the koa server is ready. I tried everything to get GraphQL wait for the database connection, but i couldn't find much information to my problem.
When using environment password direct everything works as intended.
My db.init.js
const pgp = require("pg-promise")();
const aws = require("aws-sdk");
const kms = new aws.KMS({
accessKeyId: process.env.ACCESSKEYID,
secretAccessKey: process.env.SECRETACCESSKEY,
region: process.env.REGION
});
let params = {
CiphertextBlob: Buffer.from(
process.env.ENCRYPTED_DATABASE_PASSWORD,
"base64"
)
};
module.exports = kms.decrypt(params, async (err, data) => {
const password = await data.Plaintext.toString("utf-8");
const cn = {
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password: password,
};
return pgp(cn);
});
Works when changing db.init.js to
(using plain password instead of encrypted password):
const pgp = require("pg-promise")();
const cn = {
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password: process.env.PLAIN_DATABASE_PASSWORD
};
module.exports = pgp(cn);
Using it in schema:
const { GraphQLSchema, GraphQLObjectType, GraphQLString} = require("graphql");
const db = require("./db.init")
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "RootQueryType",
fields: () => ({
sql: {
type: GraphQLString,
async resolve() {
return await db
.any("SELECT * FROM user;")
.then(data => data[0].name)
.catch(err => `Something went wrong: ${err}`);
}
}
})
})
});
module.exports = schema;
Server file
const Koa = require('koa');
const mount = require('koa-mount');
const graphqlHTTP = require('koa-graphql');
const schema = require('./schemas');
function createServer() {
server.use(
mount(
'/graphql',
graphqlHTTP({
schema,
graphiql: true,
})
)
);
return server;
}
Local Server
const server = require("./server");
const port = 4000;
server().listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}/graphql`);
});
Lambda function
const awsServerlessKoa = require('aws-serverless-koa');
const serverlessMiddleware = require('aws-serverless-koa/middleware');
const server = require('./server');
server().use(serverlessMiddleware());
module.exports.handler = awsServerlessKoa(server);
GraphQL gives failure: "db.any is not a function". In the db object in schema.js is still the unresolved aws kms object in the connection. I didn't try this as a lambda function because i have to make sure that the database is ready when the function fires.
Thanks for the comment vitaly-t, I figured it finally out:
db.init.js
const pgp = require('pg-promise')();
const aws = require('aws-sdk');
const kms = new aws.KMS({
accessKeyId: process.env.ACCESSKEYID,
secretAccessKey: process.env.SECRETACCESSKEY,
region: process.env.REGION,
});
const params = {
CiphertextBlob: Buffer.from(
process.env.DATABASE_PASSWORD,
'base64',
),
};
async function getDb() {
return kms
.decrypt(params)
.promise()
.then(async res => {
const password = await res.Plaintext.toString('utf-8');
return pgp({
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT,
database: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password,
});
});
}
module.exports = getDb();
schema.js
const { importSchema } = require('graphql-import');
const { makeExecutableSchema } = require('graphql-tools');
const dbp = require('../db/init');
const schema = importSchema('src/api/schemas.graphql');
module.exports = dbp.then(db => {
const resolvers = {
Query: {
user: () => {
return db
.any('SELECT * FROM user;')
.then(data => data[0].name);
},
},
};
return makeExecutableSchema({
typeDefs: schema,
resolvers,
});
});
schemas.graphql
type Query {
user: String
}
schema {
query: Query
}
server.js
const Koa = require('koa');
const mount = require('koa-mount');
const graphqlHTTP = require('koa-graphql');
const schema = require('../api/schemas');
function createServer() {
const server = new Koa();
server.use(
mount(
'/graphql',
graphqlHTTP(async () => ({
schema: await schema,
graphiql: true,
})),
),
);
return server;
}
module.exports = createServer;
server.local.js
const server = require('./server');
const port = 4000;
server().listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}/graphql`);
});
Only the lambda function I didn't test. Should be straight forward.

Resources