Related
I am working on a node project in which i need to display mongoose errors in a different language.
I tried overriding default mongoose errors, i.e mongoose.Error.messages with my custom messages, it works. But i need a configurable solution in which i just pass the language in a query param or variable and mongoose will load the error messages in that language.
I found out it is possible to do this by nesting/subdocumenting error messages like this :
mongoose.Error.messages.general = {required: {en: "{path} requires {value}", fr:"{path} some french words {value}"
i cannot pass that language variable/query-params to validate using schema while doing some db operations.
i found some npm packages(mongoose-intl etc) which can be used, but those are very old and not maintained anymore.
Is there any way i can dynamically change language of mongoose default/built-in errors using middlewares or some similar solutions?
This is my mongoose schema. What i want is mongoose to display error in a language which will be sent by api call when errors like "firstName is required" occurs.
const customerSchema = new mongoose.Schema({
firstName: {
type: String,
minlength: 4,
maxlength: 60,
required: true,
trim: true
},
lastName: {
type: String,
maxlength: 60,
trim: true
},
middleName: {
type: String,
maxlength: 60,
trim: true
},
fullName: {
type: String,
maxlength: 120,
trim: true
},
profilePicture: {
type: String,
maxlength: 200,
trim: true
},
email: {
type: String,
minlength: 7,
maxlength: 255,
trim: true
},
phone: {
type: String,
minlength: 7,
maxlength: 15,
trim: true
},
countryCode: {
type: String,
maxlength: 9,
trim: true
},
number: {
type: String,
maxlength: 15,
trim: true
},
permissions: {
type: [String],
required: true
},
status: {
type: String,
trim:true,
default: customerConstants.miscellaneous.customerStatus.InActive
},
firstLogin: Number,
hasProfile : {
type: Boolean, //to indicate user has already filled profile details or not
default: false
},
isEmailVerified:{
type: Boolean,
default: false
},
isPhoneVerified:{
type: Boolean,
default: false
},
functionality: Number //TODO :remove funcitonality maybe
}, { timestamps: true })
const Customer = mongoose.model('customers', customerSchema)
I am trying to write a reusable validation schema and I can change the rules of fields if I need it. I do this using a method called a fork. However, this time I couldn't imagine how can I access and change the rule of an object inside of an array. In some cases, some fields must be required. So I call the changed schema the default schema with the validation method. I am using this solution for a few models and generally, it works perfectly. Can you help me to imagine how can I solve this problem?
In this model, I have a subdocument field. This field is an array of objects field. That's why I have 2 different schemas. When the create method calls, I just need the title, description, and category object fields. If a user wants to add a question to the quiz record, I need a question array.
I was thinking that I can write a validator for only the question schema but if I don't add a field to the quiz validation schema then joi throws an error with the message "ABC field not allowed". I'm stuck because of this situation and I can't continue.
Quiz Model and Validation Schema and Method
const Joi = require('joi');
const mongoose = require('mongoose');
const slugCreator = require('mongoose-slug-updater');
mongoose.plugin(slugCreator);
const QuestionSchema = mongoose.Schema({
questionText: {
type: String,
trim: true,
minLength: 10
},
firstChoiceText: {
type: String,
trim: true,
minLength: 1
},
firstChoiceIsTrue: {
type: Boolean
},
secondChoiceText: {
type: String,
trim: true,
minLength: 1
},
secondChoiceIsTrue: {
type: Boolean
},
thirdChoiceText: {
type: String,
trim: true,
minLength: 1
},
thirdChoiceIsTrue: {
type: Boolean
},
fourthChoiceText: {
type: String,
trim: true,
minLength: 1
},
fourthChoiceIsTrue: {
type: Boolean
}
}, { timestamps: true });
const QuizSchema = mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
minLength: 15,
maxLength: 250
},
description: {
type: String,
requried: true,
trim: true,
minLength: 50
},
coverImage: {
type: String,
trim: true
},
slug: {
type: String,
unique: true,
trim: true,
slug: ['title'],
slugPaddingSize: 3
},
category: {
title: {
type: String,
trim: true,
required: true
},
categoryId: {
type: mongoose.Types.ObjectId,
trim: true,
required: true
},
slug: {
type: String,
trim: true,
required: true
}
},
questions: [QuestionSchema],
createdBy: {
userId: {
type: mongoose.Types.ObjectId,
required: true
},
fullName: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
trim: true
}
},
updatedBy: {
userId: {
type: mongoose.Types.ObjectId,
required: true
},
fullName: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
trim: true
}
}
}, { collection: 'quizzes', timestamps: true });
const validationSchema = {
title: Joi.string().trim().min(15).max(250),
description: Joi.string().trim().min(50),
coverImage: Joi.string().trim(),
slug: Joi.string().trim(),
category: {
title: Joi.string().trim(),
categoryId: Joi.string().trim(),
slug: Joi.string().trim()
},
questions: Joi.array().items(
Joi.object({
questionText: Joi.string().trim().min(10),
firstChoiceText: Joi.string().trim().min(1),
firstChoiceIsTrue: Joi.boolean(),
secondChoiceText: Joi.string().trim().min(1),
secondChoiceIsTrue: Joi.boolean(),
thirdChoiceText: Joi.string().trim().min(1),
thirdChoiceIsTrue: Joi.boolean(),
fourthChoiceText: Joi.string().trim().min(1),
fourthChoiceIsTrue: Joi.boolean(),
})
),
createdBy: {
userId: Joi.string().trim(),
fullName: Joi.string().trim(),
email: Joi.string().email().trim()
},
updatedBy: {
userId: Joi.string().trim(),
fullName: Joi.string().trim(),
email: Joi.string().email().trim()
}
};
QuizSchema.statics.joiValidationForQuizCreate = async (quizObject) => {
const requiredSchema = Joi.object(validationSchema).fork(['title', 'description', 'createdBy.userId', 'createdBy.fullName', 'createdBy.email', 'updatedBy.userId', 'updatedBy.email', 'updatedBy.email'], item => item.required());
return await requiredSchema.validateAsync(quizObject);
};
module.exports = mongoose.model('quiz', QuizSchema);
I want to access these Question fields using the Fork method as in the joiValidationForQuizCreate method. Is this possible or is there a better method available? I don't want to write schematics over and over on a case-by-case basis.
Let's say I a student model like this:
const Schema = mongoose.Schema;
const StudentSchema = new Schema({
fullName: {
type: String,
required: true,
unique: true,
trim: true,
maxLength: 60,
minLength: 3,
},
studentID: {
type: String,
required: true,
unique: true,
trim: true,
maxLength: 60,
minLength: 3,
},
semester: {
type: Number,
min: 1,
max: 15,
}
});
module.exports = mongoose.model('StudentModel', StudentSchema);
How can I tell mongoose to automatically increase the semester by one each 6 months in an Express app?
this is a example maybe could help. add this property.
semester_next_time: {
type: Date,
}
StudentSchema.pre('find', function() {
// if semester_next_time is less than now do stuff and update next time semester
});
Im new to mongoose and nosql databases. I have the following mongoose model (profile).
import { model, Schema } from 'mongoose';
import Joi from '#hapi/joi';
const profileSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users',
},
handle: {
type: String,
minlength: 2,
maxlength: 20,
required: true,
trim: true,
},
company: {
type: String,
minlength: 1,
maxlength: 100,
trim: true,
},
website: {
type: String,
maxlength: 100,
trim: true,
},
location: {
type: String,
maxlength: 100,
trim: true,
},
status: {
type: String,
maxlength: 50,
trim: true,
required: true,
},
skills: {
type: [String],
required: true,
},
bio: {
type: String,
maxlength: 500,
trim: true,
},
githubUserName: {
type: String,
maxlength: 50,
trim: true,
},
socialLinks: {
youtube: {
type: String,
maxlength: 100,
trim: true,
},
twitter: {
type: String,
maxlength: 100,
trim: true,
},
facebook: {
type: String,
maxlength: 100,
trim: true,
},
linkedin: {
type: String,
maxlength: 100,
trim: true,
},
instagram: {
type: String,
maxlength: 100,
trim: true,
},
},
date: {
type: Date,
default: Date.now,
},
});
export default model('profile', profileSchema);
export const validateProfile = (profile) => {
const schema = Joi.object({
handle: Joi.string().trim().min(2).max(20).required(),
company: Joi.string().trim().min(2).max(20),
website: Joi.string().trim().max(100),
location: Joi.string().trim().min(2).max(20),
status: Joi.string().trim().min(2).max(20),
skills: Joi.array().required(),
bio: Joi.string().trim().max(500),
githubUserName: Joi.string().max(50),
youtube: Joi.string().trim().max(100),
twitter: Joi.string().trim().max(100),
facebook: Joi.string().trim().max(100),
linkedin: Joi.string().trim().max(100),
instagram: Joi.string().trim().max(100),
});
return schema.validate(profile);
};
Every profile can have several experiences. This is my experience model.
import { model, Schema } from 'mongoose';
import Joi from '#hapi/joi';
const experienceSchema = new Schema({
title: {
type: String,
maxlength: 100,
trim: true,
required: true,
},
company: {
type: String,
maxlength: 100,
trim: true,
required: true,
},
location: {
type: String,
maxlength: 100,
trim: true,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
maxlength: 500,
trim: true,
},
});
export default model('experience', experienceSchema);
export const validateExperience = (profile) => {
const schema = Joi.object({
title: Joi.string().trim().max(100).required(),
company: Joi.string().trim().max(100).required(),
location: Joi.string().trim().max(100).required(),
from: Joi.date().required(),
to: Joi.date(),
current: Joi.boolean().default(false),
description: Joi.string().trim().max(500),
});
return schema.validate(profile);
}
;
I want to link this experience model to profile model. Experience is an array. So how can I link this experience model array to profile model? First I thought to keep the experience array inside the profile model, but it makes the model too big. I want to separate things here. What should be the better practice here??
you can add one field experience_ids in your Profile Schema , it will save ids of experience in Profile Schema
experience_ids: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'experience'
}]
you can populate experience like this :
Profile
.find()
.populate('experience_ids')
.exec(...)
Nesting experience array inside the profile model wouldn't make the model 'too big'. Consider this any Person wont ideally have more than 20 companies that he had worked for in his entire career. In fact we would mostly be looking at a no less than 20 .Which in any case isn't a length that would slow the query.
Now as you have decided to not nest the experience inside the Profile array
what you can now do is to create an array that would hold the ids for experience and keep it in your Profile Schema
experiences:[
{
type: mongoose.Schema.Types.ObjectId,
ref: "experience"
}
]
The ref keyword as you might have guessed is the name of the collection where the referenced document resides.
To retrieve the data for the user You would need to use
profile.findById(id).populate("experience").exec((err, User) =>{
console.log(User);
});
the populate method basically is used for populating the data inside the reference while the execute method basically takes the query object returned but the Mongoose query and runs this as a unit to get your result
read more about exec here : What does exec do ?
I am brand new to MongoDB and to Node JS.
I have got a problem adding a child to a document in Mongo.
The database has a collection cald weddings and in each document is one wedding.
The wedding documents also contain the guests, something like this:
wedding 1
- guest 1
- guest 2
wedding 2
- guest 3
- guest 4
etc.
Now I am having trouble adding guests to the weddings.
Here is my Model:
const Joi = require('joi');
const mongoose = require('mongoose');
const guestSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
surname: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
plz: {
type: String,
minlength: 2,
maxlength: 10
},
confirmed: { type: Boolean, default: false },
type: Number,
questlink_id: Number,
confirmedDate: Date,
hasExtraTable: Boolean
});
const weddingSchema = new mongoose.Schema({
title: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
nameA: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
nameB: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
surnameA: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
surnameB: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
location: {
type: String,
required: true,
minlength: 2,
maxlength: 255
},
street: {
type: String,
required: true,
minlength: 2,
maxlength: 50
},
streetnumber: {
type: String,
required: true,
minlength: 1,
maxlength: 50
},
plz: {
type: String,
required: true,
minlength: 2,
maxlength: 10
},
city: {
type: String,
required: true,
minlength: 2,
maxlength: 50
},
country: {
type: String,
required: true,
minlength: 2,
maxlength: 50
},
telefon: {
type: String,
minlength: 5,
maxlength: 50
},
email: {
type: String,
minlength: 5,
maxlength: 255
},
payed: { type: Boolean, default: false },
user_id: {
type: String,
required: true
},
weddingDate: {
type: Date
},
registerUntilDate: {
type: Date
},
tableSize: {
type: Number
},
link: {
type: String,
unique: true,
default: null
},
guest: [ guestSchema ]
});
const Wedding = mongoose.model('wedding',weddingSchema);
function validateWedding(wedding) {
const schema = {
title: Joi.string().min(2).max(255).required(),
nameA: Joi.string().min(5).max(50).required(),
surnameA: Joi.string().min(5).max(50).required(),
nameB: Joi.string().min(5).max(50).required(),
surnameB: Joi.string().min(5).max(50).required(),
location: Joi.string().min(5).max(50).required(),
street: Joi.string().min(5).max(50).required(),
streetnumber: Joi.string().min(5).max(50).required(),
city: Joi.string().min(5).max(50).required(),
plz: Joi.string().min(5).max(50).required(),
country: Joi.string().min(5).max(50).required(),
};
return Joi.validate(wedding, schema);
}
function validateGuest(guest) {
const schema = {
name: Joi.string().min(5).max(50).required(),
surname: Joi.string().min(5).max(50).required(),
plz: Joi.string().min(5).max(50).required()
};
return Joi.validate(guest, schema);
}
exports.guestSchema = guestSchema;
exports.weddingSchema = weddingSchema;
exports.Wedding = Wedding;
exports.validateWedding = validateWedding;
exports.validateGuest = validateGuest;
Here is my Router, or at least the important parts:
const auth = require('../middleware/auth');
const {Wedding, validateWedding, validateGuest, guestSchema} = require('../models/wedding');
const {User} = require('../models/user');
const _ = require('lodash');
const mongoose = require('mongoose');
const express = require('express');
const router = express.Router();
router.get('/:id/guests', auth, async (req, res) => {
const weddings = await Wedding.findById(req.params.id);
if(weddings.user_id != req.user._id)
res.status(400).send('This is not your wedding');
res.send(weddings.guest);
});
router.post('/:id/guests', auth, async (req, res) => {
const { error } = validateGuest(req.body);
if (error) return res.status(400).send(error.details[0].message);
const weddings_1 = await Wedding.findById(req.params.id);
if(weddings_1.user_id != req.user._id)
res.status(400).send('This is not your wedding');
const guest = mongoose.model('Guest',guestSchema);
guest.name = req.body.name;
guest.surname = req.body.surname;
guest.plz = req.body.plz;
let weddings = await Wedding.findByIdAndUpdate(req.params.id,{
guest: [ {
name : req.body.name,
surname: req.body.surname,
plz: req.body.plz
} ]
});
// weddings.guest.push(guest);
res.send(weddings);
});
module.exports = router;
I have tried to push the data into the DB and I have tried to update the whole document.
If anyone has any suggestions, thanks!
I think the problem is that you are not creating the new guest and not saving the Wedding either:
(thanks #ykit9 for the correction)
const Guest = mongoose.model('Guest', guestSchema);
const newGuest = new Guest({
name : req.body.name,
surname: req.body.surname,
plz: req.body.plz
});
newGuest.save();
Wedding.findOne({_id: req.params.id}, (err, foundWedding)=>{
foundWedding.guest.push(newGuest);
foundWedding.save();
res.send(foundWedding);
});
If you need further information: Constructing Documents in Mongoose - Documentation