How can I Connect a Postgres Schema in Nodejs and Switching Schemas Dynamically Using Sequelize? - sql

I have to connect to a Postgres database in Node.js and I want to switch schemas dynamically Using Sequelize Library.
Here is my Table Model and Controller Code.
module.exports = function (sequelize, DataTypes) {
const customer = sequelize.define(
'customer',
{
id: {
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
schema_name: {
type: DataTypes.STRING,
allowNull: false,
},
created_on: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW(),
},
},
{
schema: 'public',
}
)
return customer
}
And Controller Code
exports.login = async (req, res, next) => {
const { email, password, domain } = req.body
const domainData = await customer.findOne({
where: { schema_name: domain },
})
console.log('Log: exports.login -> domainData', domainData)
}
Once the response received I have to Switch Schemas Dynamically according to the above result.
Help me Guys Please

I resolved the problem using sequelize.query() function.
for getting Domain...
const domainData = await sequelize.query(
`SELECT schema_name from public.customer_management_client where schema_name = '${domain}'`
)
for switching to dynamic schema
const UserData = await sequelize.query(
`SELECT * from ${domainData[0][0].schema_name}.user_access_user where email = '${email}'`
)

Related

Sequelize findAll() or findOne is not working

I apologize for something that could be very simple to fix, I just cannot figure it out. I've used this code in another project before with the exact same layout and that project works, but on this project, it's not working.
As seen below in the code, I am doing a simple findAll() query. The database is connected perfectly fine but I keep getting this error no matter what I try.
[Error is here][1]
I wanted to double-check to see if it's because of me or if it's an issue with the latest release of sequelize, but I doubt it because I haven't seen any issues like this pop up yet.
But if nobody can see an issue, I will submit a bug ticket.
root/routes/ShopItems.js
const express = require('express')
const router = express.Router()
const { ShopItems } = require('../models')
router.get('/', async(req, res) => {
const data = await ShopItems.findAll()
res.json(data)
})
module.exports = router
root/index.js
const express = require('express')
const session = require('express-session')
const app = express()
const cors = require('cors')
const db = require('./models')
const { SECRET, PORT } = require('./temp_secret')
app.use(express.json())
app.use(cors())
app.use(session({
secret: SECRET,
resave: true,
saveUninitialized: false,
}))
app.use(express.urlencoded({extended: false}))
app.all('/*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
req.header("Access-Control-Allow-Origin", "*")
req.header("Access-Control-Allow-Headers", "X-Requested-With,GET,POST,content-type,Origin,Accept")
next()
})
// Routers
const ShopItemsRouter = require('./routes/ShopItems')
app.use('/shop', ShopItemsRouter)
db.sequelize.sync().then(() => {
app.listen(PORT, () => {
console.log(`Running on ${PORT}`)
})
})
root/models/index.js
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
root/models/ShopItems.js
module.exports = (sequelize, DataTypes) => {
const ShopItems = sequelize.define('shop_items', {
item_name: {
type: DataTypes.STRING(),
allowNull: false
},
item_description: {
type: DataTypes.STRING(),
allowNull: false
},
item_type: {
type: DataTypes.STRING(),
allowNull: false,
defaultValue: 'Single Item'
},
item_categories: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
item_genres: {
type: DataTypes.JSON(),
allowNull: false,
defaultValue: '{}'
},
initial_item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_discount: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
item_price: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
},
number_of_sales: {
type: DataTypes.INTEGER(),
allowNull: false,
defaultValue: 0
}
})
return ShopItems
}
Thank you anyone that helps, I greatly appreciate it.
[1]: https://i.stack.imgur.com/zBOTE.png
In the ShopItems routes file you have
const { ShopItems } = require('../models')
The problem with this is that the index.js file (Which is what gets brought in with the above statement), does not export a ShopItems property. This is why your error message is saying "Cannot read properties of undefined", because in this case the ShopItems variable is returning undefined.
Without seeing your ShopItems model I can't say for sure if this will work, but if that model exists and is set up properly, you should be able to do:
const db = require('../models')
And then inside of the route method:
const data = await db.ShopItems.findAll()

User authentication error using express not working

So I'm having trouble authenticating a user login using express for the backend. If I do a simple res.send I could get a response in postman. but if I do a check if the user and password check and generate a token if says error 401 invalid usernames and password. mind the tokens work for register and update profiles. I also attached the user schema.
Simple Approach
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
res.send({ email, password })
})
When I try to check and generate a token
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid Email or password')
}
})
Also here's my user schema using mongoose and added a function to check password since in the database it's encrypted using bcrypt.js
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
You see, your code is absolutely correct, but only if you have saved the user correctly in your model at the first. In addition, the password used during storage is hashed in the same way and from the same module.
Now you have to change your checking terms because there may not really be a user with this email in the database to match your password and return true value.
import mongoose from 'mongoose'
import bcrypt from 'bcryptjs'
const userSchema = mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
isAdmin: {
type: Boolean,
required: true,
default: false,
},
},
{
timestamps: true,
}
)
userSchema.pre('save', function (next) {
if (this.password) {
bcrypt.hash(this.password, bcrypt.genSaltSync(15), (err, hash) => {
if (err) console.log(err);
this.password = hash;
next();
});
}
}
userSchema.methods.Save = async (data) => {
let model=new User(data)
await model.save();
}
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password)
}
const User = mongoose.model('User', userSchema)
export default User
and update this code:
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body
const user = await User.findOne({ email })
if (user) {
if(await user.matchPassword(password)) {
res.json({
_id: user._id,
name: user.name,
email: user.email,
isAdmin: user.isAdmin,
token: generateToken(user._id),
})
} else {
res.status(401)
throw new Error('Invalid password')
}
} else {
res.status(401)
throw new Error('Invalid Email')
}
})
Now you know which part of the job is the problem. No user or no wrist password

How to use Mongoose Populate between 2 different Schema's

I have 2 schema's, Categories and Cards. Each Category has an array of cards, and I want to populate that array with values , but I am unsure how to go about this as the mongoose documentation is somewhat confusing to understand.
// Schemas in seperate files
// Category Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
title: {
type: String,
trim: true,
max: 30,
},
cards: [{ type: Schema.Types.ObjectId, ref: "categoryCard" }],
});
module.exports = mongoose.model("category", categorySchema);
// Category Card Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categoryCardSchema = new Schema({
category: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
},
post: {
type: String,
required: true,
trim: true,
},
});
module.exports = mongoose.model("categoryCard", categoryCardSchema);
// Below is the express router file . I want users to be able to create cards for different categories , after the category is already created. It worked in postman, but it doesn't work on the front end for some reason.
router.route("/createCard").post((req, res) => {
const { title, name, post } = req.body;
newCard = new categoryCard({
category: title,
name,
post,
});
newCard.save();
category.findOne({ title }).exec((err, item) => {
if (!err) {
item.cards.push(newCard._id);
item.save();
res.send(item);
} else {
res.send(err);
}
});
});
You can Follow this code...
let categorys= await category.findOne({ title }).populate("cards")

findOne is not a function

I am trying to create a model using Sequelize and mysql db.I am trying to post to '/students/register' it keeps giving me an error saying findOne is not a function. I tried requiring my sql but it's not working ..I also tried a different function like findAll and still not working.what seems to be the problem
const Sequelize = require('sequelize');
module.exports = function (sequelize, Sequelize) {
const Stundet = sequelize.define(
'student', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
created: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
}, {
timestamps: false
});
module.exports = Stundet;
}
routes
const Student_Info = require("../models/students")
student.post('/register', (req, res) => {
const dataToday = new Date()
const studentData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
created: dataToday
}
Student_Info.findOne({
where: {
email: req.body.email
}
})
.then(student => {
if (!student) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
studentData.password = hash
Student_Info.create(studentData)
.then(student => {
res.json({
status: student.email + 'registered'
})
})
.catch(err => {
res.send('error' + err)
})
})
} else {
res.json({
error: 'Student already registered'
})
}
})
.catch(err => {
res.send('error' + err)
})
})
module.exports = student;
When you use module.exports, you should return Stundet. You already export the whole function. And I think you should pass DataTypes instead of Sequelize.
Something like this:
module.exports = function (sequelize, DataTypes) {
const Stundet = sequelize.define(
//...
return Stundet;
}
So in your route in order to use your model:
const Sequelize = require('sequelize');
const DataTypes = sequelize.DataTypes;
let sequelize = new Sequelize(...);
const Student = require('../models/students')(sequelize, DataTypes);
I suspect that your Student_Info is null. Does you application successfully connect to the database? It helps to log... e.g.
sequelizeDB
.authenticate()
.then(() => {
console.log('Yes! DB Connection);
...
})
.catch(err => {
console.error('No! Unable to connect to DB', err);
});
... and IMHO the code reads better when you name the DB instance something other than "sequelize".

How can I override builtin login method in Loopback?

I've created a new User model, based on builtin one. I'm trying this:
module.exports = function(TiUser) {
TiUser.on('dataSourceAttached', function(obj) {
var login = TiUser.login;
TiUser.login = function(credentials, include, cb) {
var result = login.apply(this, credentials);
// Do my stuff
cb(null, my_data);
};
});
};
But I can't get it working... What is wrong? or how could this be done right?
Thanks
You may want to consider adding an afterRemote() hook to login(). Now you can achieve to add role( using Role model ) to user. For example:
TiUser.afterRemote('login', function(ctx, next) {
//add role to the user.
next();
});
At the end I've created a new method instead of overriding a current one:
module.exports = function(TiUser) {
TiUser.auth = function(credentials, include, fn) {
var self = this;
self.login(credentials, include, function(err, token) {
authInfo = {
token: token
};
fn(err, authInfo);
});
};
TiUser.remoteMethod(
'auth',
{
description: 'Login method with Role data information embedded in return',
accepts: [
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}},
{arg: 'include', type: ['string'], http: {source: 'query' },
description: 'Related objects to include in the response. ' +
'See the description of return value for more details.'}
],
returns: {
arg: 'accessToken', type: 'object', root: true,
description: 'User Model'
},
http: {verb: 'post'}
}
);
};