I got stuck for days now, trying to add mysql to my sails project as a non default datastore. If anybody knows a solution. Please
It seems like the additional datastore inside config/datastores.js is not being registered, or the error that is showing is not correct.
"sails": "^1.4.0",
"sails-mysql": "^1.0.1"
I keep getting this error:
error: A hook (orm) failed to load! error: Could not tear down the
ORM hook. Error details: Error: Invalid data store identity. No data
store exist with that identity.
config/datastores.js
module.exports.datastores = {
default: {
adapter: 'sails-disk',
},
mysqlDB: {
adapter: 'sails-mysql',
url: `mysql://${process.env.TC_DB_USERNAME}:${process.env.TC_DB_PASSWORD}#${process.env.TC_DB_HOST}:3306/Auth`,
},
};
api/models/Account.js
module.exports = {
datastore: 'mysqlDB',
schema: false,
};
As a workaround, I am using standard 'mysql' package to connect inside a helper. This works perfectly fine, but is resource heavy and I would love to get standard sails methods (Account.sendNativeQuery()) for the model to work.
If I can't get that to work, maybe someone can tell me how to make this connection global, so I only have to connect once and not on every helper call?
helpers/new-account.js
var mysql = require('mysql');
var connection = mysql.createConnection({
host: process.env.TC_DB_HOST,
user: process.env.TC_DB_USERNAME,
password: process.env.TC_DB_PASSWORD,
database: 'Auth',
});
connection.connect();
module.exports = {
friendlyName: 'Creates new account',
description: 'returns the id of the new usr or throws error',
inputs: {
username: {
description: 'The new account name.',
type: 'string',
required: true,
minLength: 4,
maxLength: 64,
},
salt: {
required: true,
type: 'ref',
description: 'generated salt.',
},
verifier: {
required: true,
type: 'ref',
description: 'generated verifier.',
},
email: {
required: true,
type: 'string',
isEmail: true,
description: 'Email linked to account.',
},
},
exits: {
success: {
description: 'All done.',
},
},
fn: async function ({ username, salt, verifier, email }, exits) {
connection.query(
'INSERT INTO account SET ?',
{ username, salt, verifier, email },
function (err, result) {
if (err) {
throw new Error(`MYSQL ERROR: ${err}`);
} else {
console.log(`new user with id ${result.insertId} created.`);
exits.success(result.insertId);
}
}
);
connection.end();
},
};
Thank you
Try requiring the sails-mysql package rather than just including the stringified name, for example:
module.exports.datastores = {
default: {
adapter: 'sails-disk',
},
mysqlDB: {
adapter: require('sails-mysql'),
url: `mysql://${process.env.TC_DB_USERNAME}:${process.env.TC_DB_PASSWORD}#${process.env.TC_DB_HOST}:3306/Auth`,
},
};
Related
I have created my first migration using sequelize-cli, now when I enter npx sequelize-cli db:migrate to run migration and create table in DB, I get error
I look into documentation could not find how and what should go into migration file.
Error
ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NUMBER, `otp` INTEGER, `otp_expiration_date` DATETIME, `createdAt` DATETIME NOT ' at line 1
My migration File
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
phone_number: {
type: Sequelize.NUMBER
},
otp: {
type: Sequelize.INTEGER(4)
},
otp_expiration_date: {
type: Sequelize.DATE
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
})
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Users');
}
};
My User Model:
const moment = require('moment');
'use strict';
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
User.init({
name: {
type: DataTypes.STRING,
allowNull: false
},
phone_number: {
type: DataTypes.NUMBER
},
otp: {
type: DataTypes.INTEGER(4)
},
otp_expiration_date: {
type: DataTypes.DATE,
set(value) {
// convert regular Date to moment Date
value = moment(value).add(5, 'minutes');
this.setDataValue('otp_expiration_date', value);
}
},
is_otp_expired: {
type: DataTypes.VIRTUAL,
get() {
// otp_expiration_date < current date
return this.getDataValue(otp_expiration_date).isAfter(moment()) ? true : false
}
}
}, {
sequelize,
modelName: 'User',
});
return User;
};
I have tried
changing datatypes
moving getters into migration
removing createdAt
[SOLVED]
Problem was with DataType of phone_number , there is no Sequelize.NUMBER type per Sequelize docs
I confused Sequelize DataTypes with MySql DataTypes
phone_number: {
type: DataTypes.NUMBER <---- bug
}
Solution
phone_number: {
type: DataTypes.INTEGER <---- solution
}
A model field with datatype Virtual and a get method copied directly from the Sequelize manual gives SQL syntax error when running db:migration with sequelize-cli. Any other possible errors in the file has been ruled out by trial and error.
MySQL 5.7.31 and Sequelize ^6.3.5 with Express 4.17.1 and mysql2 2.1.0.
My migrations file:
'use strict'
const { DataTypes } = require('sequelize')
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('users', {
id: {
type: DataTypes.UUID,
defaultValue: Sequelize.UUIDV4,
primaryKey: true,
allowNull: false, // constraint on mysql
unique: true,// constraint on mysql
validate: {
isUUID: {
args: 4,
msg: 'User ID must be a UUID4 string.'
}
}
},
firstName: {
type: DataTypes.STRING,
required: true,
allowNull: false,
notEmpty: true,
validate: {
len: {
args: [2, 90],
msg: 'The first name must contain between 2 and 90 characters.'
}
}
},
lastName: {
type: DataTypes.STRING,
required: true,
allowNull: false,
notEmpty: true,
validate: {
len: {
args: [2, 90],
msg: 'The last name must contain between 2 and 90 characters.'
}
}
},
// Code that breaks the migration process:
fullName: {
type: DataTypes.VIRTUAL,
get() {
return `${this.firstName} ${this.lastName}`;
}
},
// End code that breaks migration process
email: {
type: DataTypes.STRING,
unique: true,
required: true,
allowNull: false,
validate: {
isEmail: {
args: [true],
msg: 'Incorrect email format'
},
isLowercase: {
args: [true],
msg: 'Email address must be lowercase'
},
len: {
args: [2, 50],
msg: 'The email address must have between 2 and 50 characters',
},
notEmpty: {
args: [true],
msg: 'The email field can\'t be empty.'
},
}
},
password: {
type: DataTypes.STRING,
required: true,
allowNull: false,
validate: {
len: {
args: [8, 99],
msg: 'Your password must contain at least 8 characters.',
},
notEmpty: {
args: [true],
msg: 'The password field cannot be empty.'
}
}
},
resetPasswordToken: {
type: DataTypes.STRING
},
resetPasswordExpire: {
type: DataTypes.INTEGER
},
createdAt: {
type: DataTypes.DATE,
required: true,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
required: true,
allowNull: false
},
deletedAt: {
type: DataTypes.DATE
}
},
{
paranoid: true,
tableName: 'users'
})
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('users');
}
};
Console output after sequelize-cli db:migrate:
ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near 'VIRTUAL, `email` VARCHAR(255)
NOT NULL UNIQUE, `password` VARCHAR(255) NOT NULL,' at line 1
npm ERR! code ELIFECYCLE
npm ERR! errno 1
etc.
If the fullName field is removed from the model the migration runs successfully.
The get() method in the fullName field is identical to the example in the manual (https://sequelize.org/master/manual/getters-setters-virtuals.html#virtual-fields).
It gets extra interesting as the Sequelize manual states that Virtual datatypes will not be added to the database – which is the whole point of this functionality:
The VIRTUAL field does not cause a column in the table to exist.
In other words, the model above will not have a fullName column.
However, it will appear to have it!
Environment:
Sequelize version: ^6.3.5
Node.js version: 12.18.3
Operating System: MacOS/Docker 19.03.13 build 4484c46d9d
MySQL 5.7.31
Express 4.17.1
NPM package mysql2 version 2.1.0 (adapter)
Sequelize CLI 6.2.0
Thank you for your kind help.
Virtuals exist to help you with querying the models (for example, in your API controllers to get the user's full name). Since they don't exist in the database, they shouldn't be included as part of a migration file.
In short, you should use VIRTUAL in your model definition, not in the migration file using the queryInterface.createTable method.
Hope this answers your question.
I try to get two column values from my mysql database.
This is my model
const Sequelize = require('sequelize');
const db = require('../config/database');
const AuthDetails = db.define('auth_details', {
client_id : {
type: Sequelize.STRING
},
client_secret : {
type: Sequelize.STRING
}
},{
timestamps : false
});
module.exports = AuthDetails;
And, This is my route
router.post('/login', (req, res, next) => {
// console.log(req.body);
Users.findOne( { where : { mobile_number: req.body.mobile_number}})
.then(users => {
UserAuthDet.findAll({
where: {
client_id: req.body.client_id,
client_secret: req.body.client_secret
}
});
});
I'm having the error, while getting the client_id and client_secret from the database.
My error
UPDATED : Database.js File
const Sequelize = require('sequelize');
module.exports = new Sequelize('mydbname', 'root', '', {
host: 'localhost',
dialect: 'mysql',
operatorsAliases: false,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000
},
});
Try adding primaryKey: true to client_id in AuthDetails.
const AuthDetails = db.define('auth_details', {
client_id : {
type: Sequelize.STRING,
primaryKey: true
},
client_secret : {
type: Sequelize.STRING
}
},{
timestamps : false
});
I am guessing Sequelize considers id as primary key by default unless it is specified and appending id to findAll query.
ref: https://github.com/sequelize/sequelize/issues/741
I'm new to Sails and currently working on connection between Sails project and MySQL. I found that the 'Client Connection' on MySQL Workbench doesn't show any connection after my sails lift and I can't get any data from MySQL also.
[config/datastores.js]
module.exports.datastores = {
default: {
adapter: require('sails-mysql'),
url: 'mysql://root:pw#localhost:3306/database',
}
};
[api/model]
module.exports = {
attributes: {
'username': {
type: 'string',
},
'email': {
type: 'string',
},
'password': {
type: 'string',
},
'create_time': {
type: 'string',
},
},
};
[api/devController]
module.exports = {
index: function(req,res){
model.find().exec(function(err,info){
return res.json(info);
});
},
};
After sails lift, the json return empty.Is that something missing or wrong?
I am getting belo mentioned error when trying to make user authentication using passport-local and sequelize for MySQL. When running server it is creating new table in SQL if not already, but as soon as I hit sign up button it is showing error.
Error:
TypeError: Cannot read property 'findOne' of undefined
at Strategy._verify (E:\Web Development\node-SQL-Sequelize-passport-local\config\passport\passport.js:19:13)
at Strategy.authenticate (E:\Web Development\node-SQL-Sequelize-passport-local\node_modules\passport-local\lib\strategy.js:88:12)
at attempt (E:\Web Development\node-SQL-Sequelize-passport-local\node_modules\passport\lib\middleware\authenticate.js:361:16)
at authenticate (E:\Web Development\node-SQL-Sequelize-passport-local\node_modules\passport\lib\middleware\authenticate.js:362:7)
My server.js look like :
app.use(passport.initialize());
app.use(passport.session());
//Models
var models = require("./app/models");
//Routes
var authRoute = require('./app/routes/auth.js')(app,passport);
require('./config/passport/passport.js')(passport, models.user);
//Sync Database
models.sequelize.sync().then(function() {
console.log('Nice! Database looks fine')
}).catch(function(err) {
console.log(err, "Something went wrong with the Database Update!")
});
app.get('/', function(req, res) {
res.send('Welcome to Passport with Sequelize');
});
My user.js file:
module.exports = function(sequelize, Sequelize) {
var User = sequelize.define('userInfo', {
id: {
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstname: {
type: Sequelize.STRING,
notEmpty: true
},
lastname: {
type: Sequelize.STRING,
notEmpty: true
},
username: {
type: Sequelize.TEXT
},
about: {
type: Sequelize.TEXT
},
email: {
type: Sequelize.STRING,
validate: {
isEmail: true
}
},
password: {
type: Sequelize.STRING,
allowNull: false
},
last_login: {
type: Sequelize.DATE
},
status: {
type: Sequelize.ENUM('active', 'inactive'),
defaultValue: 'active'
}
});
return User;
}
My passport.js file :
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport, user) {
var User = user;
var LocalStrategy = require('passport-local').Strategy;
passport.use('local-signup', new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},function(req, email, password, done) {
var generateHash = function(password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
email: email
}
}).then(function(user) {
if (user)
{
return done(null, false, {
message: 'That email is already taken'
});
} else
{
var userPassword = generateHash(password);
var data =
{
email: email,
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname
};
User.create(data).then(function(newUser, created) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
}
));
}
Review this example: github.com/sequelize/express-example. In particular models/index.js for models load.
That way is handled in that example is:
require('./config/passport/passport.js')(passport, models.userInfo);
Because "userInfo" is defined in your model user.js: