I want to create two rows.
First I want to create a tenant and after I want to creae a user who has a reference to the tenant.
I want to do this in one transaction (register).
The tenant would be created, but when sequelize try to create the user I get an error:
Failing row contains
Routine: ExecConstraints
My DB-Mappings:
const TenantMapping = sequelize.define('tenant', {
id: { type: DataTypes.NUMBER, primaryKey: true, autoIncrement: true },
label: { type: DataTypes.STRING, allowNull: false },
name: { type: DataTypes.STRING },
postOfficeBox: { type: DataTypes.STRING },
street: { type: DataTypes.STRING },
houseNo: { type: DataTypes.STRING },
zipCode: { type: DataTypes.STRING, validate: { max: 10 } },
city: { type: DataTypes.STRING },
phone: { type: DataTypes.STRING },
mobilePhone: { type: DataTypes.STRING },
email: { type: DataTypes.STRING, allowNull: false },
website: { type: DataTypes.STRING },
birth: { type: DataTypes.DATE, allowNull: false },
death: { type: DataTypes.DATE }
}, {
...getSequelizeTableSettings({ schema: 'auth' })
});
const UserMapping = sequelize.define('user', {
id: { type: DataTypes.NUMBER, primaryKey: true, autoIncrement: true },
tenantId: { type: DataTypes.NUMBER, allowNull: false },
email: { type: DataTypes.STRING, allowNull: false },
password: { type: DataTypes.STRING, allowNull: false },
role: { type: DataTypes.STRING, allowNull: false },
isActivated: { type: DataTypes.BOOLEAN, allowNull: false },
birth: { type: DataTypes.DATE, allowNull: false },
death: { type: DataTypes.DATE }
}, {
...getSequelizeTableSettings({ schema: 'auth' })
});
My Controller
const response = await sequelize.transaction(async (transaction) => {
try {
const { tenantLabel, email, password } = req.body;
const tenant = await this.tenantRepository.create({
tenantLabel,
email
}, {
transaction
});
const user = await this.userRepository.create({
email,
password,
tenantId: tenant.id,
role: UserRole.ADMIN
}, {
transaction
});
await transaction.commit();
res.status(200).json({
tenant,
user
});
} catch (exception) {
res.status(400).send({
...exception
});
}
});
If I create the tenant and user in two different transactions - it works fine.
What is wrong?
I use PostgreSQL
{
"name": "SequelizeDatabaseError",
"parent": {
"length": 257,
"name": "error",
"severity": "ERROR",
"code": "23502",
"detail": "Failing row contains (22, null, email#mail.com, null, null, f, 2022-01-22 12:07:33.899, null).",
"schema": "auth",
"table": "user",
"column": "tenant_id",
"file": "execMain.c",
"line": "1965",
"routine": "ExecConstraints",
"sql": "INSERT INTO \"auth\".\"user\" (\"id\",\"email\",\"birth\") VALUES (DEFAULT,$1,$2) RETURNING \"id\",\"tenant_id\",\"email\",\"password\",\"role\",\"is_activated\",\"birth\",\"death\";",
"parameters": [
"email#mail.com",
"2022-01-22 12:07:33.899 +00:00"
]
},
"original": {
"length": 257,
"name": "error",
"severity": "ERROR",
"code": "23502",
"detail": "Failing row contains (22, null, email#mail.com, null, null, f, 2022-01-22 12:07:33.899, null).",
"schema": "auth",
"table": "user",
"column": "tenant_id",
"file": "execMain.c",
"line": "1965",
"routine": "ExecConstraints",
"sql": "INSERT INTO \"auth\".\"user\" (\"id\",\"email\",\"birth\") VALUES (DEFAULT,$1,$2) RETURNING \"id\",\"tenant_id\",\"email\",\"password\",\"role\",\"is_activated\",\"birth\",\"death\";",
"parameters": [
"email#mail.com",
"2022-01-22 12:07:33.899 +00:00"
]
},
"sql": "INSERT INTO \"auth\".\"user\" (\"id\",\"email\",\"birth\") VALUES (DEFAULT,$1,$2) RETURNING \"id\",\"tenant_id\",\"email\",\"password\",\"role\",\"is_activated\",\"birth\",\"death\";",
"parameters": [
"email#mail.com",
"2022-01-22 12:07:33.899 +00:00"
]
}
I log my object with I want to create a new row before to try create with sequelize.
console.log('create new something', JSON.stringify(object));
const response = await this.sequelize.create(object, transaction);
In the console I have this:
create new something {"email":"email#mail.com","password":"$2b$04$2vDzrLah9FjhxG.nlIJjluByE28O6bjyIr4s7LXqoFgGwyjHODJMG","role":"ADMIN","tenantId":22,"isActivated":false}
Thats correct. But look in the error message. Sequelize try to create a new user only with email - no password, no role, no tenantId, no isActivated...
TenantRepository:
export class TenantRepository extends Repository<TenantResource> {
constructor() {
super(DB_MAPPINGS.TenantMapping, { identifier: 'id' });
}
public async create(
{ tenantLabel, email }: { tenantLabel: string; email: string; },
{ transaction }: { transaction: Transaction }
): Promise<TenantResource> {
return await this.CREATE({
label: tenantLabel,
email
},
{
transaction
}
);
}
}
UserRepostory:
export class UserRepository extends Repository<UserResource> {
constructor() {
super(DB_MAPPINGS.UserMapping, { identifier: 'id' });
}
public async create(
{ email, password, role, tenantId }: { email: string; password: string; role: UserRole; tenantId: number; },
{ transaction }: { transaction: Transaction }
): Promise<UserResource> {
return await this.CREATE({
email,
password: await this.encryptPassword({ password }),
role,
tenantId,
isActivated: false
},
{
transaction
}
);
}
}
Repository:
export class Repository<T> extends RepositoryHelper<T> {
public async CREATE(
object: Partial<T>,
{ transaction }: { transaction: Transaction }
): Promise<T> {
const response = await this.sequelize.create(object, transaction);
return response.toJSON();
}
}
I found my problem. I have to pass the transaction.
export class Repository<T> extends RepositoryHelper<T> {
public async CREATE(
object: Partial<T>,
{ transaction }: { transaction: Transaction }
): Promise<T> {
const response = await this.sequelize.create(object, { transaction });
return response.toJSON();
}
}
Related
this is user model
const { Model } = require("sequelize");
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({ Kyc }) {
// define association here
this.hasOne(Kyc, { foreignKey: "userID" });
}
}
User.init(
{
uuid: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
},
firstname: {
type: DataTypes.STRING,
allowNull: false,
},
lastname: {
type: DataTypes.STRING,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
},
role: {
type: DataTypes.STRING,
defaultValue: "user",
allowNull: false,
validate: {
roles(value) {
const rolesArray = ["user", "admin"];
if (!rolesArray.includes(value)) {
throw new Error('plese enter valid role "user or admin"');
}
},
},
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
modelName: "User",
}
);
return User;
};
this is kyc model
const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
class Kyc 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({ User }) {
// define association here
this.belongsTo(User, { foreignKey: "userID", as: "user" });
}
}
Kyc.init(
{
uuid: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
},
docimg: {
type: DataTypes.STRING,
allowNull: false,
},
details: {
type: DataTypes.STRING,
allowNull: false,
},
status: {
type: DataTypes.STRING,
allowNull: false,
},
userID: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
sequelize,
modelName: "Kyc",
}
);
return Kyc;
};
kyc middlware
const verifyKyc = async (req, res, next) => {
// check that user has posted or not if yes then give error
const user = await User.findByPk(req.user.id);
const kyc = await Kyc.findOne({
userID: req.user.id,
});
console.log(user.id);
console.log(kyc);
if (user.id === kyc) {
}
next();
};
Error
Executing (default): SELECT "id", "uuid", "firstname", "lastname", "email", "role", "password", "createdAt", "updatedAt" FROM "Users" AS "User" WHERE "User"."id" = 1;
(sequelize) Warning: Model attributes (userID) passed into finder method options of model Kyc, but the options.where object is empty. Did you forget to use options.where?
Executing (default): SELECT "id", "uuid", "docimg", "details", "status", "userID", "createdAt", "updatedAt" FROM "Kycs" AS "Kyc" LIMIT 1;
1
Kyc {
dataValues: {
id: 117,
uuid: '99461f78-4781-42cc-a01f-b6541fda849d',
docimg: 'admin.png',
details: 'KSAPK0550P',
status: 'pending',
userID: 1,
createdAt: 2022-06-04T10:59:21.039Z,
updatedAt: 2022-06-04T10:59:21.039Z
_previousDataValues: {
id: 117,
uuid: '99461f78-4781-42cc-a01f-b6541fda849d',
docimg: 'admin.png',
details: 'KSAPK0550P',
status: 'pending',
userID: 1,
createdAt: 2022-06-04T10:59:21.039Z,
updatedAt: 2022-06-04T10:59:21.039Z
isNewRecord: false
}
i am tring to print userID but giving me this error
thank you for your help
You forgot to wrap your condition into where option:
const kyc = await Kyc.findOne({
where: {
userID: req.user.id,
}
});
I am trying to fetch data from DB with sequelize. The many to many relationships between users and roles. When i fetch the users does not include the roles.
The code look like:
user model
// model defines the user objects
const userModel = (sequelize, Sequelize) => {
const users = sequelize.define("user", {
id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
},
firstname: {
allowNull: false,
type: Sequelize.STRING,
},
lastname: {
allowNull: false,
type: Sequelize.STRING,
},
password: {
allowNull: false,
type: Sequelize.STRING,
},
email: {
allowNull: false,
type: Sequelize.STRING,
},
image: {
allowNull: true,
type: Sequelize.STRING,
},
});
//don not show password and id
users.prototype.toJSON = function () {
let values = Object.assign({}, this.get());
delete values.password;
delete values.id;
return values;
};
return users;
};
export default userModel;
Roles model
// model defines the events objects
const rolesModel = (sequelize, Sequelize) => {
const roles = sequelize.define("roles", {
id: {
type: Sequelize.STRING,
allowNull: false,
primaryKey: true,
},
name: {
allowNull: false,
type: Sequelize.STRING,
},
description: {
allowNull: true,
type: Sequelize.STRING,
},
});
return roles;
};
export default rolesModel;
The associations:
db.users.associate = (db) => {
db.users.belongsToMany(db.roles, {
through: "userroles",
constraints: false,
foreignKey: "rolesId",
});
};
db.roles.associate = (db) => {
db.roles.belongsToMany(db.users, {
through: "userroles",
constraints: false,
foreignKey: "userId",
});
};
There are two controller functions that are adding and fetching the user data
Controller
User.create(userDetails)
.then(() => {
let roles = req.body.roles;
roles.forEach(async (element) => {
let role = await Roles.findByPk(element);
if (role) {
await Userroles.create({
id: uniqid(),
rolesId: element,
userId: userId,
});
} else {
logger.warn(`tried adding to ${userId} a none existent role`);
}
});
})
// get user
let user = await User.findOne({
where: { email: username },
include: { model: db.roles },
});
So the roles are only a empty array when I try getting user details:
"firstname": "Mathew",
"lastname": "Murimi",
"email": "******#gmail.com",
"image": null,
"createdAt": "2022-02-12T22:56:40.000Z",
"updatedAt": "2022-02-12T22:56:40.000Z",
"roles": []
Receive the user created in the then, add the id of "newUser" in "userId"
User.create(userDetails)
.then((**newUser**) => {
let roles = req.body.roles;
roles.forEach(async (element) => {
let role = await Roles.findByPk(element);
if (role) {
await Userroles.create({
id: uniqid(),
rolesId: element,
userId: **newUser.id**,
});
} else {
logger.warn(`tried adding to ${**newUser.id**} a none existent role`);
}
});
})
I've tried to isolate this example and I hope it's ok. I know, this isn't great code, but I hope you get the drift.
For the time being the resolvers return a static result object.
Here's my problem:
The result of the company resolve function should be passed on the user's resolve function. But that ain't happenin' and I wonder what I am missing.
const GraphQL = require('graphql');
const UserType = new GraphQL.GraphQLObjectType({
name: 'User',
fields: {
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
});
const CompanyType = new GraphQL.GraphQLObjectType({
name: 'Company',
fields: {
legalName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
employees: { type: new GraphQL.GraphQLList(UserType) },
},
});
const queryDef = new GraphQL.GraphQLObjectType({
name: 'Query',
fields: {
user: {
type: UserType,
args: {
id: { type: GraphQL.GraphQLID },
givenName: { type: GraphQL.GraphQLString },
familyName: { type: GraphQL.GraphQLString },
city: { type: GraphQL.GraphQLString },
},
resolve: (parent, args, context, info) => {
console.log('parent should provide company object', parent);
// currentyl parent is undefined
return {
id: 10,
givenName: 'test',
};
},
},
company: {
type: CompanyType,
args: {
id: { type: GraphQL.GraphQLID },
},
resolve: (parent, args, context, info) => {
return {
id: 3,
legalName: 'legal test name',
city: 'company location',
};
},
},
},
});
const schema = new GraphQL.GraphQLSchema({ query: queryDef });
const companyQuery = `
{
company(id: 1) {
city
employees {
familyName
}
}
}`;
GraphQL.graphql(schema, companyQuery).then( (companyResult) => {
console.log(companyResult);
} ).catch( (err) => {
console.error(err);
});
It's my first time using PostgreSQL and I'm creating a REST API that using sequelize for postgresql, expressjs and nodejs.
I created a user and each user has many parcels. I am now creating a parcel which belongs to a user using userId as reference. I keep getting the database sequelize error and it has no message. It's routine says, 'checkInsertTarget'.
Error message
{
"name": "SequelizeDatabaseError",
"parent": {
"name": "error",
"length": 190,
"severity": "ERROR",
"code": "42703",
"position": "48",
"file": "d:\\pginstaller.auto\\postgres.windows-x64\\src\\backend\\parser\\parse_target.c",
"line": "1033",
"routine": "checkInsertTargets",
"sql": "INSERT INTO \"Parcels\" (\"id\",\"name\",\"delivered\",\"presentLoc\",\"destination\",\"description\",\"createdAt\",\"updatedAt\",\"userId\") VALUES (DEFAULT,'Shoe',false,'Onitsha','ESUT','black shoe, size 34','2018-11-25 15:48:13.100 +00:00','2018-11-25 15:48:13.100 +00:00','3') RETURNING *;"
},
"original": {
"name": "error",
"length": 190,
"severity": "ERROR",
"code": "42703",
"position": "48",
"file": "d:\\pginstaller.auto\\postgres.windows-x64\\src\\backend\\parser\\parse_target.c",
"line": "1033",
"routine": "checkInsertTargets",
"sql": "INSERT INTO \"Parcels\" (\"id\",\"name\",\"delivered\",\"presentLoc\",\"destination\",\"description\",\"createdAt\",\"updatedAt\",\"userId\") VALUES (DEFAULT,'Shoe',false,'Onitsha','ESUT','black shoe, size 34','2018-11-25 15:48:13.100 +00:00','2018-11-25 15:48:13.100 +00:00','3') RETURNING *;"
},
"sql": "INSERT INTO \"Parcels\" (\"id\",\"name\",\"delivered\",\"presentLoc\",\"destination\",\"description\",\"createdAt\",\"updatedAt\",\"userId\") VALUES (DEFAULT,'Shoe',false,'Onitsha','ESUT','black shoe, size 34','2018-11-25 15:48:13.100 +00:00','2018-11-25 15:48:13.100 +00:00','3') RETURNING *;"
}
Parcel migration code
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Parcels', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
delivered: {
type: Sequelize.BOOLEAN
},
presentLoc: {
type: Sequelize.STRING
},
destination: {
type: Sequelize.STRING
},
description: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
},
userId: {
type: Sequelize.INTEGER,
onDelete: 'CASCADE',
references: {
model: 'Users',
key: 'id',
as: 'userId',
}
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Parcels');
}
};
Parcel model code
'use strict';
module.exports = (sequelize, DataTypes) => {
const Parcel = sequelize.define('Parcel', {
name: {
type: DataTypes.STRING,
allowNull: false
},
delivered: {
type: DataTypes.BOOLEAN,
defaultValue: false
},
presentLoc: {
type: DataTypes.STRING,
allowNull: false
},
destination: {
type: DataTypes.STRING,
allowNull: false
},
description: DataTypes.STRING,
}, {});
Parcel.associate = (models) => {
Parcel.belongsTo(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
})
};
return Parcel;
};
Parcel controller
const Parcel = require('../models').Parcel;
const joiSchema = require('../joischema/parcelSchema');
const validator = require('../joischema/validator');
module.exports = {
create(req, res) {
const data = {
name: req.body.name,
description: req.body.description,
destination: req.body.destination,
presentLoc: req.body.presentLoc,
userId: req.params.userId,
};
const valid = validator.isValid(req, res, joiSchema, data);
if (valid != null){
res.status(500).send(valid);
}else{
return Parcel
.create(data)
.then(parcel => res.status(201).send(parcel))
.catch(error => res.status(400).send(error));
}
}
I checked the database and saw a column that I thought I'd removed. I had to run sequelize db:migrate:undo and then sequelize db:migrate again to incorporate my changes i.e. removing that column.
I've got a user model, and I'm just trying to test out beforeCreate by returning err, but when I do POST /user it creates the model fine, without executing the beforeCreate method. What am I doing wrong here? Here's models/User.js:
module.exports = {
connection: "mysql",
attributes: {
firstname: {
type: "string",
required: true
},
lastname: {
type: "string",
required: true
},
fullname: function() {
return this.firstname + ' ' + this.lastname;
},
username: {
type: "string",
required: true
},
password: {
type: "string",
required: true
},
email: {
type: "email",
required: true
},
status: {
type: "integer",
/*
* 0 - unconfirmed, 1 - confirmed, 2- suspended
*/
enum: [0, 1, 2]
},
// Override toJSON instance method to remove password value
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
},
// Associations
roles: {
collection: "role",
via: "users"
},
permissions: {
collection: "permission",
via: "users"
},
// Lifecycle Callbacks
beforeCreate: function(values, next) {
return next(err);
}
}
};
beforeCreate is not an attribute but a method on the model, so it should be like this:
attributes: {
....
},
beforeCreate: {
....
}
Your beforeCreate got inside attributes. It have to be on the outside. like -
module.exports = {
connection: "mysql",
attributes: {
firstname: {
type: "string",
required: true
},
lastname: {
type: "string",
required: true
},
fullname: function() {
return this.firstname + ' ' + this.lastname;
},
username: {
type: "string",
required: true
},
password: {
type: "string",
required: true
},
email: {
type: "email",
required: true
},
status: {
type: "integer",
/*
* 0 - unconfirmed, 1 - confirmed, 2- suspended
*/
enum: [0, 1, 2]
},
// Override toJSON instance method to remove password value
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
},
// Associations
roles: {
collection: "role",
via: "users"
},
permissions: {
collection: "permission",
via: "users"
}
},
beforeCreate: function(values, next){
return next(err);
}
};