How do I use joi to validate phone numbers in Node.js? - node.js

I'd like to use joi in node.js to validate the user's phone number in a schema.
The schema is as follows:
phone: {
type: Number,
unique:true,
},
country code will be default: INDIA(+91). The number will change.

You can use an additional package like that https://www.npmjs.com/package/joi-phone-number
or
You can validate a phone number as a string with a regex like that :
joi.string().regex(/^[0-9]{10}$/).messages({'string.pattern.base': `Phone number must have 10 digits.`}).required()
or
You can extend joi to have an custom validator for phone number validation like that :
Joi.object({
phone: Joi
.string()
.custom((value, helper) => {
// you can use any libs for check phone
if (!checkPhone(value)) {
return helper.message("phone is incorrect")
return value
})
}).validate({
phone: '+79002940163'
});
For more information on custom validations please check : https://joi.dev/api/?v=17.6.0#anycustommethod-description

Related

Setting mongoose to allow null without breaking validation

Here is how my application works. A user logs in for the first time using Google Sign in. We get the following data from their Google Account:
Given name
Family name
Email ID
We wish to use this information to call our API (POST request) to create a user profile.
The data we send is
{
firstName: firstName ,
lastName: lastName,
email: email
}
Here is where the issue comes from. The user profile has many fields and one of them is designation. When the user logs in for the first time, we don't know their designation.
We are using MongoDB for our database. So we use Mongoose to set up the connection. In Mongoose model, we have added some validation for our schema. Designation is a required field. It should be at least one character of length and maximum of 40 characters. If we set designation as null, the validation would fail.
Is there any way to allow null in a required field in Mongoose?
Rather than setting required to true or false, you can pass it a function:
const user = new Schema({
designation: {
type: String,
minLength: 1,
maxLength: 40,
required: function() {
// Rather than checking a stored variable, you could check
// static functions on the model, a custom value on the
// instance that isn't persisted, etc.
return this.hasLoggedInAtLeastOnce === true;
// If this function returns true, the field is required.
}
}
hasLoggedInAtLeastOnce: {
type: Boolean,
}
});

How to set Joi validations with custom messages?

I was trying to set some validations with some custom messages in Joi. So, for example, I have discovered that when a string must have at least 3 characters we can use "string.min" key and associate this to a custom message. Example:
username: Joi.string().alphanum().min(3).max(16).required().messages({
"string.base": `Username should be a type of 'text'.`,
"string.empty": `Username cannot be an empty field.`,
"string.min": `Username should have a minimum length of 3.`,
"any.required": `Username is a required field.`,
}),
Now here is my question:
Question
// Code for question
repeat_password: Joi.ref("password").messages({
"string.questionHere": "Passwords must match each other...",
}),
What method (questionHere) name need to set to repeat_password to be able to notify the user that passwords must match? I don't even know if Join.ref("something") accept .messages({...})...
If someone could please show me some help in the Joi docs, I haven't find anything yet by there...
What you are trying to find here is the error type. It can be found in the error object that the joi validate function returns. eg: error.details[0].type will give you what you are looking for.
Regarding your second question, Join.ref("something") doesn't accept .messages({...}). Here you can use valid in conjunction with ref.
eg:
const Joi = require('joi');
const schema = Joi.object({
username: Joi.string().alphanum().min(3).max(16).required().messages({
"string.base": `Username should be a type of 'text'.`,
"string.empty": `Username cannot be an empty field.`,
"string.min": `Username should have a minimum length of 3.`,
"any.required": `Username is a required field.`,
}),
password: Joi.string().required(),
password_repeat: Joi.any().valid(Joi.ref('password')).required().messages({
"any.only" : "Password must match"
})
});
const result = schema.validate({ username: 'abc', password: 'pass', password_repeat: 'pass1'});
// In this example result.error.details[0].type is "any.only"

Remove generated string from Mongoose schema with custom validation

I have a schema with a custom validation.
const schema = new mongoose.Schema({
username: {
type: String,
required: true,
validate: {
validator: /^[a-zA-Z0-9_]{3,16}$/,
message: "Usernames must be 3 to 16 characters long and contain only alphanumeric characters and underscores (_)."
},
},
// ...other things
});
However, the validation message comes out like this when I type an invalid username:
User validation failed: username: Usernames must be 3 to 16 characters long and contain only alphanumeric characters and underscores (_).
How do I get rid of the part of the string at the start that says User validation failed: username: ?
The format is embedded into the ValidationError class. Short of monkey patching that class, I can't see a way to easily change the format.
One option could be to run validation before being thrown by the model:
const user = new User({ username: 'ab' })
const error = user.validateSync()
console.log(error.errors['username'].message)
Or handle ValidationError when caught:
try {
const user = new User({ username: 'ab' })
await user.save()
} catch (error) {
if (error instanceOf mongoose.Document.ValidationError ) {
console.log(error.errors['username'].message)
}
}
To get rid of the initial string , you could simply use a split method on the string returned before displaying it. Here is a sample code:
let stringa = "User validation failed: username: Usernames must be 3 to 16 characters long and contain only alphanumeric characters and underscores (_)."; //replace this with error message
let stringb = (stringa.split(":")[2]);
console.log(stringb);//This displays the needed output string with Usernames
Your error object has another nested object called properties that has a field called message.
(example: properties.message) This gives you the exact string you wrote in the mongoose schema

Mongoose:create model using "1" as value for Schema.Types.ObjectId

What im tryng to do is the following, i have my models defined as:
const channelSchema= new Schema({
name:{type: String},
country_id:{type: Schema.Types.ObjectId, ref: 'country'}
})
and
const countrySchema = new Schema({
_id{ type : Number }
name: { type: String },
gmt: { type: String }
})
now, when i create a new countrySchema, i use a personalized "_id", such as 1 or 2, always a number and so on, this is created with 0 errors.
My problem is when i try to create a new channel schema, using
country_id = "1"
or
country_id = 1
i get the error:
'Cast to ObjectID failed for value "1" at path "country_id"'
what i've read from mongoose documentation, ObjectId is created (at default) with 12bytes.
My question is: Is there a way to evade having to use 12bytes keys, and to use "1" as ObjectId so i can populate channels with countries?
IMPORTANT: im using Node.Js, mongoose and express
If you want to reference a relationship, you need to set the same type to both sides of a relation. In this case _id of country is declared as Number, so country_id should be Number as well
country_id: { type: Number, ref: 'country' }
You could use "id" instead of "_id".

How can I 'resolve' a MogoDB object to a specific JSON structure in Nodejs?

Suppose the following User Schema in MongoDB (using Mongoose/Nodejs):
var UserSchema = new Schema({
email: {
type: String,
unique: true,
required: 'User email is required.'
},
password: {
type: String,
required: 'User password is required.'
},
token: {
type: String,
unique: true,
default: hat
},
created_at: {
type: Date,
default: Date.now
},
});
// mongoose-encrypt package
UserSchema.plugin(encrypt, {
secret: 'my secret',
encryptedFields: ['email', 'password', 'token', 'created_at']
});
Now assume I want to return the user object from an API endpoint. In fact, suppose I want to return user objects from multiple API endpoints. Possibly as a standalone object, possibly as a related model.
Obviously, I don't want password to be present in the returned structure - and in many cases I wouldn't want token to be returned either. I could do this manually on every endpoint, but I'd prefer a no-thought solution - being able to simply retrieve the user, end of story, and not worry about unsetting certain values after the fact.
I mainly come from the world of Laravel, where things like API Resources (https://laravel.com/docs/5.6/eloquent-resources) exist. I already tried implementing the mongoose-hidden package (https://www.npmjs.com/package/mongoose-hidden) to hide the password and token, but unfortunately it seems as though that breaks the encryption package I'm using.
I'm new to Nodejs and MongoDB in general - is there a good way to implement this?
How to protect the password field in Mongoose/MongoDB so it won't return in a query when I populate collections?
You can use this: Users.find().select("-password"),
but this is done whenever you send the queried item to the user (res.json()...) so you can do your manipultions with this field included and then remove it from the user before you send it back (this is using the promise approach, the best practice).
And if you want your changes to be used as default you can add "select: false" into the schema object's password field.
Hope this helps :)

Resources