How to do Prisma runtime model validation? - orm

In my application, I have validated the input credential at the DTO level by using class-validator. But I need runtime model validation like sequelize ORM.
In sequelize:
'use strict';
import { DataTypes, Sequelize } from 'sequelize';
function User(sequelize: Sequelize) {
const user = sequelize.define(
'User',
{
name: {
type: DataTypes.STRING,
allowNull: false
},
role: {
type: DataTypes.STRING(20),
allowNull: false
},
email: {
type: new DataTypes.STRING,
allowNull: false,
validate: {
isEmail: {
// args: true,
msg: 'Invalid email'
},
len: {
args: [1, 100] as readonly [number, number],
msg: 'Email length should be 1 to 100 characters'
},
notNull: {
// args: true,
msg: 'Email cannot be empty'
}
}
},
password: {
type: DataTypes.VIRTUAL,
allowNull: true,
},
},
{
tableName: 'users',
underscored: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
deletedAt: 'deleted_at',
paranoid: true
}
);
return user;
}
export default User;
Is there any possibility to do model validation in Prisma?

There is an open feature request for Prisma to support runtime model validation directly at the Schema level. Alternatively, you can leverage the Client Extensions to perform validation. There is an example in this blog post that shows how to perform custom runtime validation.

Related

Mongoose population issue at express.js

Hello evryone i have issue last 2 days tryin to populate reviews on company list.
Im trying to get list of all companys with their reviews.
Every review is assigned to company Id.
On postman response i get empty array : []
Here is the code im having isuses with;
Company Model:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var slug = require("mongoose-slug-generator");
mongoose.plugin(slug);
var CompanySchema = new Schema({
title: {type: String, required: true},
slug: { type: String, slug: "title" },
address: {type: String, required: true},
totalRating: {type: Number, required: false},
description: {type: String, required: false},
facebookLink: {type: String, required: false},
twitterLink: {type: String, required: false},
googleLink: {type: String, required: false},
linkedIn:{type: String, required: false},
instagramLink:{type: String, required: false},
contactNumber:{type: Number, required: false},
website:{type: String, required: false},
email:{type: String, required: false},
review: [{type: Schema.Types.ObjectId, ref: "Review"}],
user: { type: Schema.ObjectId, ref: "User", required: true },
}, {timestamps: true});
module.exports = mongoose.model("Company", CompanySchema);
Review Model :
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var ReviewSchema = new Schema({
title: {type: String, required: true},
description: {type: String, required: true},
rating: {type: String, required: true},
user: { type: Schema.ObjectId, ref: "User", required: true },
company: {type: Schema.ObjectId, ref:"Company"}
}, {timestamps: true});
module.exports = mongoose.model("Review", ReviewSchema);
And where i populate it :
exports.companyList = [
function (req, res) {
try {
Company.
find({id: req._id}).
populate("review").
then((companies)=>{
if(companies.length > 0){
return apiResponse.successResponseWithData(res, "Uspješno 1", companies);
}else{
return apiResponse.successResponseWithData(res, "Uspješno 2", []);
}
});
} catch (err) {
//Baci error 500...
return apiResponse.ErrorResponse(res, err);
}
}
];
Ive tryed evrything thanks infront.
Try changing in your Company Model
review: [{type: Schema.Types.ObjectId, ref: "Review"}]
to
review: {type: Schema.Types.ObjectId, ref: "Review"}
Then when you try to populate, I assume you either pass the _id of the company in your params or body of the request, so most likely:
req.params.id
or
req.body.id
the actual function:
module.exports = {
fncName: (req, res) => {
Company.findById(req.params.id)
.populate({
path: 'review',
})
.exec((err, company) => {
if (!err) {
if(companies.length > 0){
return apiResponse.successResponseWithData(res, "Uspješno 1", companies);
}else{
return apiResponse.successResponseWithData(res, "Uspješno 2", []);
}
}else {
console.log(err)
}
})
}
}
I am not sure how you register the router but it could look something like this:
const router = require ('express-promise-router')();
router.get('/get-company', nameOfTheController.fncName);
Note: to populate review from Company Model you do not necessarily need this line in your Review Model:
company: {type: Schema.ObjectId, ref:"Company"}

Sequelize v5.21 error while initializing model. Dependency name must be given as a not empty string

I have managed to pinpoint the source of the error, despite it not mentioning any file name or anything. The error is Unhandled rejection TypeError: Dependency name must be given as a not empty string
It is occurring when I try to initialize the below model in my Express App
'use strict';
module.exports = (sequelize, DataTypes) => {
const Order = sequelize.define('Order', {
customerName: {
type: DataTypes.STRING,
allowNull: true
},
customerContact: {
type: DataTypes.STRING,
allowNull: true
},
address: {
type: DataTypes.TEXT,
allowNull: true
},
additionalDetails: {
type: DataTypes.TEXT,
allowNull: true
},
areaId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: sequelize.Area,
key: 'id'
}
},
createdBy: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: sequelize.User,
key: 'id'
}
},
}, {});
Order.associate = function(models) {
// Order.belongsTo(models.Area, {foreignKey: 'areaId', as: 'area'});
};
return Order;
};
However, if I comment/remove the areaId and createdBy attributes the app runs without error and the corresponding tables are created in MySQL Database.
Is there something I am doing wrong, I am using the same syntax in my other models to define foreign keys and they seem to run without error.
Any help would be appreciated, also if someone could point a way to get more descriptive errors while using Sequelize in Express it would be very helpful, as locating the source took me a lot of time due to numerous model definitions.
This problem was resolved after I defined the associations that used areaId and createdBy as foreign keys. Don't know why but without the associations defined, the foreign keys would give an error. Now my model definition is as below
'use strict';
module.exports = (sequelize, DataTypes) => {
const Order = sequelize.define('Order', {
customerName: {
type: DataTypes.STRING,
allowNull: true
},
customerContact: {
type: DataTypes.STRING,
allowNull: true
},
address: {
type: DataTypes.TEXT,
allowNull: true
},
additionalDetails: {
type: DataTypes.TEXT,
allowNull: true
},
areaId: {
type: DataTypes.INTEGER,
references: {
model: sequelize.Area,
key: 'id'
}
},
createdBy: {
type: DataTypes.INTEGER,
allowNull: true,
references: {
model: sequelize.User,
key: 'id'
}
},
status: {
type: DataTypes.STRING,
allowNull: true,
defaultValue: 'pending'
}
}, {});
Order.associate = function(models) {
Order.belongsTo(models.Area, {foreignKey: 'areaId', as: 'area'});
Order.belongsTo(models.User, {foreignKey: 'createdBy', as: 'user'});
};
return Order;
};

Fulltext mongodb $text search query in graphql-compose-mongoose

I'm unable to figure out how to construct a graphql query for performing the mongodb fulltext search using the text index. https://docs.mongodb.com/manual/text-search/
I've already created a text index on my string in the mongoose schema but I don't see anything in the schemas that show up in the grapqhl playground.
A bit late, though I was able to implement it like so
const FacilitySchema: Schema = new Schema(
{
name: { type: String, required: true, maxlength: 50, text: true },
short_description: { type: String, required: true, maxlength: 150, text: true },
description: { type: String, maxlength: 1000 },
location: { type: LocationSchema, required: true },
},
{
timestamps: true,
}
);
FacilitySchema.index(
{
name: 'text',
short_description: 'text',
'category.name': 'text',
'location.address': 'text',
'location.city': 'text',
'location.state': 'text',
'location.country': 'text',
},
{
name: 'FacilitiesTextIndex',
default_language: 'english',
weights: {
name: 10,
short_description: 5,
// rest fields get weight equals to 1
},
}
);
After creating your ObjectTypeComposer for the model, add this
const paginationResolver = FacilityTC.getResolver('pagination').addFilterArg({
name: 'search',
type: 'String',
query: (query, value, resolveParams) => {
resolveParams.args.sort = {
score: { $meta: 'textScore' },
};
query.$text = { $search: value, $language: 'en' };
resolveParams.projection.score = { $meta: 'textScore' };
},
});
FacilityTC.setResolver('pagination', paginationResolver);
Then you can assign like so
const schemaComposer = new SchemaComposer();
schemaComposer.Query.addFields({
// ...
facilities: Facility.getResolver('pagination')
// ...
});
On your client side, perform the query like so
{
facilities(filter: { search: "akure" }) {
count
items {
name
}
}
}

How to create element associated with an existing object

I created the following two models...
const Account = sequelize.define("account",
{
id_account: {
type: Sequelize.INTEGER.UNSIGNED,
primaryKey: true,
autoIncrement: true
},
name: {...},
surname: {...},
username: {...},
password: {...}
},
{
name: {
singular: "Account",
plural: "Accounts"
},
freezeTableName: true,
hooks: {
beforeSave: ((account, options) => {
return bcrypt.hash(account.password, 10)
.then(hash => {account.password = hash;})
.catch(err => {throw new Error();});
})
}
});
const Genre = sequelize.define("genre",
{
genre_name: {
type: Sequelize.STRING(40),
primaryKey: true
},
url_img: {
type: Sequelize.STRING(40),
allowNull: false
}
},
{
name: {
singular: "Genre",
plural: "Genres"
},
freezeTableName: true
});
...and the following associations
Account.Genres = Account.belongsToMany(Genre, {
through: "AccountGenre",
foreignKey: "ref_account"
});
Genre.Accounts = Genere.belongsToMany(Account, {
through: "AccountGenre",
foreignKey: "ref_genre"
});
I created the following genres: Rock, Metal, Pop, Hardcore.
Now i want to create an Account and associate it 3 genres.
The following code creates the Account but doesn't create the association with the existing genres in the AccountGenre table:
const genresArray = ["Rock", "Metal", "Pop"];
const account = {...} // I have an object with account properties
Account.create({
name: account.name,
surname: account.cognome,
username: account.nome_utente,
password: account.password,
genres: genresArray
}, {
include: [Genre]
});
What's wrong in this code?
The problem here is that you gave the reference to the
association the plural name “Genres”. You need the same in the include statement.
include: [Genres]

Sails.js + Waterline: Many-to-Many through association

I'm new to Sails.js (v0.10.5) and Waterline ORM. I have 3 tables in database: users (id, name), roles(id, alias) and join table users_roles(user_id, role_id). It's important not to change table names and field names in database. I want Policy entity to be a join entity between User and Role. Here is some mapping code:
//User.js
module.exports = {
tableName: 'users',
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
id: {
type: 'integer',
required: true
},
name: {
type: 'string'
},
roles: {
collection: 'role',
via: 'users',
through: 'policy'
},
}
}
//Role.js
module.exports = {
tableName: "roles",
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
id: {
type: 'integer',
required: true
},
alias: {
type: 'string',
required: true
},
users: {
collection: 'user',
via: 'roles',
through: 'policy'
}
}
}
//Policy.js
module.exports = {
tableName: "users_roles",
tables: ['users', 'roles'],
junctionTable: true,
autoCreatedAt: false,
autoUpdatedAt: false,
attributes: {
user: {
columnName: 'user',
type: 'integer',
foreignKey: true,
references: 'user',
on: 'id',
via: 'role',
groupBy: 'user'
},
roles: {
columnName: 'role',
type: 'integer',
foreignKey: true,
references: 'role',
on: 'id',
via: 'user',
groupBy: 'role'
}
}
}
But when I trying to access roles atribute in controller
User.findOne({id: 1}).populate('roles').exec(function(err, user) {
console.log(JSON.stringify(user.roles));
});
this returns
[]
And
User.findOne({id: 1}).populate('roles').exec(function(err, user) {
console.log(JSON.stringify(user));
});
returns
{"id":1,"name":"test", "roles":[]}
I checked twice that user, role and association between them exists in database. What is my mistake?
I have found way to solve this problem. It's not what I exactly want, but it works.
First: join entity:
//Policy.js
module.exports = {
tableName: "users_roles",
autoPK: false,
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
},
user: {
columnName: 'user_id',
model: 'user'
},
role: {
columnName: 'role_id',
model: 'role'
}
},
//tricky method to get all users for specified role_id
//or to get all roles for specified user_id
get: function(id, modificator, cb) {
var fields = ['user', 'role'];
if (fields.indexOf(modificator) < 0) {
cb(new Error('No such modificator in Policy.get()'), null);
}
var inversedField = fields[(fields.indexOf(modificator) + 1) % 2];
var condition = {};
condition[inversedField] = id;
this.find(condition).populate(modificator).exec(function(err, policies) {
if (err) {
cb(err, null);
return;
}
var result = [];
policies.forEach(function(policy) {
result.push(policy[modificator]);
});
cb(null, result);
return;
});
}
}
As you see, I added ID field to this entity (and to db table users_roles too), so it's not the great solution.
//User.js
module.exports = {
tableName: 'users',
autoPK: false,
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
unique: true,
},
name: {
type: 'string'
},
policies: {
collection: 'policy',
via: 'user'
}
}
}
And Role Entity:
//Role.js
module.exports = {
tableName: 'roles',
autoPK: false,
attributes: {
id: {
type: 'integer',
primaryKey: true,
autoIncrement: true,
},
alias: {
type: 'string',
required: true,
unique: true,
},
policies: {
collection: 'policy',
via: 'role'
}
}
}
That's how I get all roles for specified user_id:
...
id = req.session.me.id; //user_id here
Policy.get(id, 'role', function(err, roles) {
var isAdmin = false;
roles.forEach(function(role) {
isAdmin |= (role.id === 1);
});
if (isAdmin) {
next(null);
return;
} else {
return res.redirect('/login');
}
});
...
Maybe it'll be usefull for somebody =)