Connecting to Multiple MySQL DBs in Feathers.js using Sequelize - mysql

I've been unable to find a documented way of connecting to multiple MySQL databases in Feathers.js using Sequelize. Is there a way to do this? My use case is to be able to insert and get rows of data into multiple DBs from the same action but the DBs won't necessarily be the same schema.
Thanks!

I made some local test and it is possible. You need to define 2 different sequelize clients.
If you are using the CLI generator and you set up a service based on sequelize you should have a connection string (my example is a mysql db):
a db connection string in the config/default.json
"mysql" : "mysql://user:password#localhost:3306/your_db"
a sequelize.js in the src root folder
In order to create a second sequelize client
create a new connection string in the config/default.json
"mysql2" : "mysql://user:password#localhost:3306/your_db_2"
create a copy of sequelize.js and name it sequelize2.js
const Sequelize = require('sequelize');
module.exports = function (app) {
const connectionString = app.get('mysql2');
const sequelize2 = new Sequelize(connectionString, {
dialect: 'mysql',
logging: false,
operatorsAliases: false,
define: {
freezeTableName: true
}
});
const oldSetup = app.setup;
app.set('sequelizeClient2', sequelize2);
app.setup = function (...args) {
const result = oldSetup.apply(this, args);
// Set up data relationships
const models = sequelize2.models;
Object.keys(models).forEach(name => {
if ('associate' in models[name]) {
models[name].associate(models);
}
});
// Sync to the database
sequelize2.sync();
return result;
};
};
add the new sequelize configuration to your app.js
const sequelize2 = require('./sequelize2');
app.configure(sequelize2);
Then in your model to a second db :
const Sequelize = require('sequelize');
const DataTypes = Sequelize.DataTypes;
module.exports = function (app) {
//load the second client you defined above
const sequelizeClient = app.get('sequelizeClient2');
//to check if connect to a different db
console.log ( sequelizeClient )
//your model
const tbl = sequelizeClient.define('your_table', {
text: {
type: DataTypes.STRING,
allowNull: false
}
}, {
hooks: {
beforeCount(options) {
options.raw = true;
}
}
});
// eslint-disable-next-line no-unused-vars
tbl.associate = function (models) {
// Define associations here
// See http://docs.sequelizejs.com/en/latest/docs/associations/
};
return tbl;
};
In order to work you need 2 different services, each one working with a different db.
If you want to put or get with a single action you can create a before/after hook in one of the service and call inside the hook the second service.
For the get you need to add the result of the second service to your hook result

Related

How do format exports of multiple modules in NodeJS

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.

typeorm "CREATE DATABASE" migration

I need to create database before connection and work with db. I'm using nest.js typeorm, provided all configurations. When I start my application it says
"Unable to connect to the database. Error: ER_BAD_DB_ERROR: Unknown database 'test'".
Once again: there is not DB "Test" in my MySQL Workbench => when I start the application
I want the application to create the database itself (not by me manually)
Is it possible?
I found a way to achieve this for postgresql. Also I'm using Nest.js and Typeorm too. Firstly, I created two sql files (one for check if database exists and one for create db) and then a config file for database. These files contents are like below.
checkDbIfExists.sql
SELECT 1 FROM pg_database WHERE datname = 'test'
createDB.sql
CREATE DATABASE test
config.ts
import { DynamicModule } from '#nestjs/common';
import { TypeOrmModule, TypeOrmModuleOptions } from '#nestjs/typeorm';
import { createConnection, getManager } from 'typeorm';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions';
import * as path from 'path';
const fs = require('fs');
const checkDBScript: string = fs
.readFileSync(path.join(__dirname, '../script/checkDBIfExists.sql'))
.toString();
const createDb: string = fs.readFileSync(path.join(__dirname, '../script/createDB.sql')).toString();
const CreateDBIfNotExists = async (options: TypeOrmModuleOptions): Promise<void> => {
const connection = await createConnection(options as PostgresConnectionOptions);
const manager = getManager();
const result = await manager.query(checkDBScript);
if (result.length === 0) await manager.query(createDb);
connection.close();
};
const DBConfig = async (): Promise<DynamicModule> => {
let options: TypeOrmModuleOptions = {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'asd',
entities: [],
synchronize: true,
cli: {
migrationsDir: 'persistence/migrations'
}
};
await CreateDBIfNotExists(options);
options = { ...options, database: 'test' };
return TypeOrmModule.forRoot(options);
};
export default DBConfig;
Then I added these lines
nest-cli.json
"compilerOptions": {
"assets": ["persistence/script/*"]
}
app.module.ts
#Module({
imports: [DBConfig()]
...
NestJS can't create the database, you need to manually create it before starting your application.
If you want it to be automatic, you can use a docker service to create your database when starting your docker compose with docker-compose up
version: "3.6"
services:
db:
image: mysql:8.0.20
command: --default-authentication-plugin=mysql_native_password
restart: always
ports:
- 3306:3306
environment:
- MYSQL_DATABASE=<database-name>
- MYSQL_ROOT_PASSWORD=<password>
I am using TypeORM version 0.3.11, so while M. Erim Tuzcuoglu's solution works for now, it's using deprecated methods.
Here is my function that is based on the newer DataSource approach.
export const createDBIfNotExists = async (): Promise<void> => {
const dbOptions = dbConfig().db;
const { createDatabase, database } = dbOptions;
if (!createDatabase) {
return;
}
const dataSource = new DataSource({
type: 'postgres',
...dbOptions,
database: 'postgres',
});
await dataSource.initialize();
const result = await dataSource.query(
`SELECT 1 FROM pg_database WHERE datname = '${database}'`
);
if (!result.length) {
console.log(`Creating database with name "${database}"`);
await dataSource.query(`CREATE DATABASE "${database}"`);
}
await dataSource.destroy();
}
Specifying postgres as the database for the connection is a bit "hacky" because it assumes it's existence. However in my case that is acceptable. Without it it would fail to connect.
I should also mention that dbConfig function is very similar to the approach provided in Nest documentation:
// Use process.env for the values, I've hardcoded them just for clarity here
export const dbConfig = (): { db: IDBConfig } => ({
db: {
host: 'localhost',
port: 5432,
database: 'your-db-name',
username: 'username',
password: 'password',
migrationsRun: true,
createDatabase: true,
logging: true,
synchronize: false,
},
});
Then in my main.ts I just call the function:
async function bootstrap() {
await createDBIfNotExists();
// ...
}
This allows me to simply change POSTGRES_DB env variable and it would create and use this DB automatically when I start the app (while also checking for the RUN_MIGRATION env).
It's quite specific to the requirements I had, but I hope the general implementation example could help someone in the future.

How to create table inside dynamically created database in sequelize?

I am working on a rest api using nodejs, express and sequelize orm. The api i am creating is for a android application which is based on SAAS platform. Therefore, I need to create separate database for each user of my application. There will be a master database which will be used to identify the database credential for each user while logging in. The master database will be connected by default and in each api call, in each route i need to connect/create the user specific database including the tables.
/* This is my db.js file for master database connection using sequelize. */
require('dotenv').config();
const sequelize = require('sequelize');
module.exports = new sequelize('master_db', process.env.DB_USER, process.env.DB_PASS, {
host: process.env.DB_HOST,
dialect: 'mysql',
operatorAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
}
});
/* This is a model Demo.js */
const Sequelize = require('sequelize');
const db = require('../../db');
const Demo_table = db.define('demo_table', {
// attributes
demo_id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true,
},
demo_name: {
type: Sequelize.INTEGER,
allowNull: false,
}
}, {
// options
timestamps: false,
freezeTableName: true,
});
module.exports = Demo_table;
/* This demo_route.js file in route folder*/
"use strict";
require('dotenv').config();
const express = require('express');
const router = express.Router();
const checkAuth = require('../middleware/auth');
//load controllers
const DemoController = require('../controllers/demo_member');
router.post('/demo_activity', checkAuth, DemoController.demo_activity);
module.exports = router;
/* This is the controller file demo_member.js*/
const moment = require('moment-timezone');
const Sequelize = require('sequelize');
const nodemailer = require("nodemailer");
const Op = Sequelize.Op;
//load models
const Demo = require('../models/Demo');
exports.create_demo_team = (req, res) => {
/* here i need to create the database of the user who is calling this api if the database(with tables) is not created else need to connect with the existing user's database and perform CRUD*/
}
I am expecting with the solution which will fulfil my requirement at the earliest. where i can create database dynamically, then create table and connect with it to perform CRUD.

Mongoose query returning only 101 records [duplicate]

I have a simple mongoose model on which I call find with limit max 100 it calls the done callback:
this.find({}).limit(100).exec(done);
The callback is never called If I modify this line into (or any higher number)
this.find({}).limit(101).exec(done);
There is no error anywhere, the database keeps working, but this node app freezes and must be restarted.
If I ssh into the server to connect to the same database and connect to mongo shell, on the same collection find({}) returns all ~700 collections in less than a sec.
When I cloned the same database to my local PC and run the app to connect to local database it worked, but the app freezes on the server if its connect to the database on the same server.
Any idea how to debug this one?
Edit1: Added model file:
Model file:
'use strict';
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let foodSchema = new Schema(
{
name: Object,
type: String,
description: Object,
price: Number,
priceBig: Number,
active: Boolean
},
{
collection: 'foods'
}
);
let model = mongoose.model('food', foodSchema);
model.getAllFoods = function (done) {
this.find({}, done);
};
model.getActiveFoods = function (done) {
this.find({active: true}, done);
};
model.getFoodById = function (id, done) {
this.findOne({_id: id}, done);
};
module.exports = model;
Usage:
foodModel.getAllFoods(function (err, docs) {
if (err) {
res.sendStatus(500);
return;
}
res.send(docs);
});
getActiveFoods works just fine (returns 96 docs)
After the tip from JohnnyK I updated Mongoose from 4.1.11 to 4.3.7 and that fixed the issue.

NodeJS - Connecting to a local Mysql Database

I am trying to create a mysql database connection for my node app and ran sequelize.authenticate().then(function(errors) { console.log(errors) }); to test if the connection worked. The response that is logged to my console is Executing (default): SELECT 1+1 AS result undefined The undefined portion makes me think that the connection either didn't work or that there isn't any database found. I can't seem to figure that out. I thought by creating a database through Sequel Pro and connecting to localhost via the Socket, I can use the same credentials for connecting with my Node app. Do I need to create a file within my app for the database and not use the Sequel Pro database?
Controller file (where the connection is created):
var Sequelize = require('sequelize');
var sequelize = new Sequelize('synotate', 'root', '', {
host:'localhost',
port:'3306',
dialect: 'mysql'
});
sequelize.authenticate().then(function(errors) { console.log(errors) });
var db = {}
db.Annotation = sequelize.import(__dirname + "/ann-model");
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
ann-model.js (where my table is being defined):
module.exports = function(sequelize, DataTypes) {
var Ann = sequelize.define('annotations', {
ann_id: {
type: DataTypes.INTEGER,
primaryKey: true
},
ann_date: DataTypes.DATE,
}, {
freezeTableName: true
});
return Ann;
}
Try this one, Executing (default): SELECT 1+1 AS result means that everything okay
sequelize
.authenticate()
.then(function(err) {
if (!!err) {
console.log('Unable to connect to the database:', err)
} else {
console.log('Connection has been established successfully.')
}
});
But i didn't know where you get undefined