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)
suddenly my route started showing me this error, I don't know whats wrong with it.
I am trying to pass the id from the middleware. and use it to fetch the document.
I made a field name hostId where I store the id of the user who has created the project in the string form.
The error:
CastError: Cast to ObjectId failed for value "proj" (type string) at path "_id" for model "Student"
at model.Query.exec (D:\KabirProject\FindAlly\node_modules\mongoose\lib\query.js:4545:21)
at model.Query.Query.then (D:\KabirProject\FindAlly\node_modules\mongoose\lib\query.js:4644:15)
at processTicksAndRejections (internal/process/task_queues.js:95:5) {
messageFormat: undefined,
stringValue: '"proj"',
at ObjectId.cast (D:\KabirProject\FindAlly\node_modules\mongoose\lib\schema\objectid.js:245:12)
at ObjectId.SchemaType.applySetters (D:\KabirProject\FindAlly\node_modules\mongoose\lib\schematype.js:1135:12)
at ObjectId.SchemaType._castForQuery (D:\KabirProject\FindAlly\node_modules\mongoose\lib\schematype.js:1567:15)
at ObjectId.SchemaType.castForQuery (D:\KabirProject\FindAlly\node_modules\mongoose\lib\schematype.js:1557:15)
at ObjectId.SchemaType.castForQueryWrapper (D:\KabirProject\FindAlly\node_modules\mongoose\lib\schematype.js:1534:20)
at cast (D:\KabirProject\FindAlly\node_modules\mongoose\lib\cast.js:336:32)
at model.Query.Query.cast (D:\KabirProject\FindAlly\node_modules\mongoose\lib\query.js:4968:12)
at model.Query.Query._castConditions (D:\KabirProject\FindAlly\node_modules\mongoose\lib\query.js:2056:10)
at model.Query.<anonymous> (D:\KabirProject\FindAlly\node_modules\mongoose\lib\query.js:2335:8)
at model.Query._wrappedThunk [as _findOne] (D:\KabirProject\FindAlly\node_modules\mongoose\lib\helpers\query\wrapThunk.js:27:8)
at D:\KabirProject\FindAlly\node_modules\kareem\index.js:370:33
at processTicksAndRejections (internal/process/task_queues.js:77:11),
valueType: 'string'
Here's my route:
router.get('/student/projects', signin,(req,res)=>{
try{
const id = req.user._id
const Projects = PersonalProject.find({hostId: id});
const user = req.user;
res.render("student/myProjects", {Projects, user});
} catch(err){
console.log(err);
}
});
Signin middleware:
function signin(req, res, next){
const {cookies} = req;
if(!cookies){
return res.status(401).json({error:"You must be signed in"})
}
const token = cookies.jwtToken.replace("Bearer ","")
jwt.verify(token,process.env.ACCESS_TOKEN_SECRET,(error,user)=>{
if(error){
return res.status(401).json({error: "You must be signed in"})
}
req.user = user;
console.log(user._id)
next();
});
}
personal Project Model:
const mongoose = require('mongoose');
const personalProjectSchema = new mongoose.Schema({
title:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [50, "Max length reached"]
},
host:{
type: String,
required: true,
minlength: [2, "field length is too short"],
maxlength: [50, "Max length reached"]
},
hostId:{
type:String,
required: true
},
year:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [30, "Max length reached"]
},
branch:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [50, "Max length reached"]
},
description:{
type:String,
required: true,
trim:true
},
role:{
type: String,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [15, "Max length reached"]
},
status:{
type: String,
default: "ACTIVE",
required: true
},
githubLink:{
type: String,
trim: true
},
teamLimit:{
type: Number,
trim: true,
required: true
},
teammates:[]
},
{timestamps:true}
);
module.exports = mongoose.model('Project', personalProjectSchema);
Student Model:
const mongoose = require('mongoose');
const studentSchema = new mongoose.Schema({
name:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [50, "Max length reached"]
},
email:{
type: String,
required: true,
trim: true
},
gender:{
type:String,
required: true,
trim: true,
minlength: [4, "Length of your name is too short"],
maxlength: [6, "Max length reached"]
},
year:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [30, "Max length reached"]
},
branch:{
type: String,
required: true,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [50, "Max length reached"]
},
password:{
type: String,
required: true,
},
mobNo:{
type: Number,
required: true,
minlength: [10, "Length of your name is too short"],
maxlength: [10, "Max length reached"]
},
role:{
type: String,
trim: true,
minlength: [2, "Length of your name is too short"],
maxlength: [15, "Max length reached"]
}
},
{timestamps: true}
);
mongoose.model("Student", studentSchema);
please help me find the problem.
First, you have a Cast error, it means that you need to use smth like this:
Schema.Types.ObjectId(_id)
to convert your value to ObjectId type.
Second, it looks like you have a problem in other part of your code(not one that you provided), check where are you using aggregation with Student model and convert _id prop to ObjectId type
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 ?
Im new to node and mongodb. I have the following mongoose model.
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,
},
experience: [
{
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,
},
},
],
education: [
{
school: {
type: String,
maxlength: 100,
trim: true,
required: true,
},
degree: {
type: String,
maxlength: 100,
trim: true,
required: true,
},
fieldOfStudy: {
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,
},
},
],
social: {
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);
I have created this model in a single file and it seems too big. So should I put experience, education and social into 3 seperate models? If so how should I do it? If I put these in to 3 seperate models, how can I link them with the profile model? An example would be highly appriciated.
Yes, you should seperate them. To link them you would just put the profile schema Id as a field on the other models.
const profileSchema = new Schema({
userId: Schema.Types.ObjectId
})
const experienceSchema = new Schema({
userId: Schema.Types.ObjectId
})
const educationSchema = new Schema({
userId: Schema.Types.ObjectId
})
Then you would just query the experience collection by the userId to get their experiences. This is the way I'd recommend.
Another way wouldbe to put experienceIds on the profile schema that would reference the Experience model and could use the populate method to fill the fields.
I have the following code:
var product = await Product.findOne({$and:[
{ name: req.body.name },
{ extraData: [
{brand: req.body.extraData.brand} ,
{quantity: req.body.extraData.quantity},
{typeOfQuantity: req.body.extraData.typeOfQuantity}]}
]});
if (product) res.status(400).send("Product already registered.");
What I would like to do is to check if the given product with these fields already exists in the database so I dont have multiple same object but i have a problem in the nested json object. The code above doesn't work and allows multiple same products to be stored in the array.
The product Scema is:
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 1,
maxlength: 255
},
extraData: {
type: extraDataSchema,
required: true
}});
const extraDataSchema = new mongoose.Schema({
brand: {
type: String,
required: true,
minlength: 1,
maxlength: 255
},
quantity: {
type: Number,
required: true,
minlength: 1,
maxlength: 10
},
typeOfQuantity: {
type: String,
required: true,
minlength: 1,
maxlength: 255
}
});