Where to place email validator within project? - node.js

I am on day 2 of learning node and Java Script. I have been following basic tutorials and decided to attempt and implement simple email validation into my code.
The problem is i am not sure where to place the code - i have a server.js file that holds all of the CRUD operations and a Mongoose model that which ensures the correct data is entered. Does anyone have any advice as to the best way to validate a user-entered email using this module?
//Email-validation npm module
var validator = require("email-validator");
validator.validate("test#email.com");
//Mongoose model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Tickets = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
address: {
type: String,
required: true
},
price: {
type: Number,
required: true,
min: 1,
max: 100
}
});
module.exports = mongoose.model('Ticket', TicketSchema);

Validate email before saving object. Code should look something like this:
Tickets.pre('save', function (next) {
var ticket = this;
if (!ticket.isModified('email')) {
next();
} else {
var valid = validator.validate(ticket.email);
if(valid) {
next();
} else {
next(valid);
}
}
});

Related

What is wrong with my Mongoose Relationship?

I am learning mongoose relationship and stuck in this problem... someone tell me where am I wrong..? and Is there a better approach of doing this..?
Employee.js
const mongoose = require("mongoose");
const { departmentSchema, addDepartment } = require("./Departments");
const schema = new mongoose.Schema();
const employeeModel = mongoose.model("Employee", {
Name: { type: String, required: true, minlength: 5, maxlength: 50 },
Department: departmentSchema
});
async function addEmployee(Employee, Department, Language, Framework) {
const result = await new employeeModel({
...Employee,
Department: addDepartment(Department, Language, Framework)
});
return await result.save();
}
Department.js
const mongoose = require("mongoose");
const { languageSchema, addLanguage } = require("./Language");
const departmentSchema = new mongoose.Schema({
Depart: { type: String, required: true, minlength: 5, maxlength: 50 },
Languages: languageSchema
});
const DepartmentModel = mongoose.model("Department", departmentSchema);
async function addDepartment(Department, Language, Framework) {
const result = await new DepartmentModel({
...Department,
Languages: addLanguage(Language, Framework)
});
return await result.save();
}
module.exports = {
departmentSchema,
addDepartment
};
Language.js
const mongoose = require("mongoose");
const { frameworkSchema, addFramework } = require("./Frameworks");
const languageSchema = new mongoose.Schema({
Language: { type: String, required: true },
Framework: [frameworkSchema]
});
const languageModel = mongoose.model("Language", languageSchema);
async function addLanguage(Language, Framework) {
const result = await new languageModel({
...Language,
Framework: addFramework(Framework)
});
return await result.save();
}
module.exports = {
languageSchema: languageSchema,
addLanguage
};
Framework.js
const mongoose = require("mongoose");
const frameworkSchema = new mongoose.Schema({
Name: { type: String, required: true, minlength: 4, maxlength: 50 },
Version: { type: String, required: true, minlength: 1, maxlength: 50 }
});
const frameworkModel = mongoose.model("Framework", frameworkSchema);
async function addFramework(Framework) {
const result = await new frameworkModel(...Framework);
return await result.save();
}
module.exports = {
frameworkSchema,
addFramework
};
Error from Framework.js
Found non-callable ##iterator
what I am trying to do is
Employee ->
Name
Department ->
Depart
Language ->
Language Name
Framework ->
Framework Name
Framework Version
Every -> represents a new object (collection)
I know it's a little bit complex for a newbie like me...
I am a MySql database user and I loved it but now I am trying to grasp NoSql...
Thanks in advance :)
I will show you an example:
Suppose you have employSchema already like that:
const {Schema} = mongoose;
const employeeModel = mongoose.model("Employee", new Schema({
Name: { type: String, required: true, minlength: 5, maxlength: 50 },
Department: {type: Schema.Types.ObjectId, ref: "Department"}
}));
In the above model, I use ref property to connect to Department model through ObjectId (it mean "_id" property in Department collection).
Ex: You will create an new employeeModel like that:
let newEmployee = new employeeModel("Employee Name", Department ID here).
You absolutely do the opposite or do for others.
And you need to use .populate() function to query database.
Example:
employModel.find({}).populate("Department").exec(callback what you want to do);
You can read official document at: https://mongoosejs.com/docs/populate.html
Solved the error as always :p
The error is because of the spread operator (...) in Framework.js
const result = await new frameworkModel(...Framework);
still if someone can tell me a better approach of doing this, it will be highly appreciated.
Thanks everyone.

Returning a field which is not in the model

I have projects which store batches of records. When retrieving a project from my mongoDB using mongoose, I want to calculate the number of batches which belong to that specific project.
My Project model-schema currently looks like so:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
})
My batch model-schema looks:
const schema = new Schema({
project:{
type: ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
orginal_name: {
type: String,
required: true
}
}, {
timestamps: true
})
I have a function which counts the number of batches that belong to a project based off of the batch.project field and then adds to it to the JSON object of the project (e.g project.batchCount). However I have run into a problem where the new field project.batchCount is being removed by the by the models toJSON or toObject function because the field is not present in the model-schema.
My current solution is to add it to the model-schema as a 'dummy field' which is never saved to the mongoDB:
const schema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
},
batchCount: {
type: Number
}
}, {
timestamps: true
})
However I do not like this way as it makes my model larger than it needs to be and slightly less 'readable'.
Is there a better way to do this? And if so how?
I think what you're looking for is a virtual on the schema. A virtual on a document in mongoose is available locally, but doesn't get saved to the DB.
I liked the idea of using a static on the schema to get the project counts. I also liked the idea of just calling the method on the document rather than the Model, so I implemented it in an instance method. A static would have worked just as well. It depends on your preference.
Here is what I came up with:
batch.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const batchSchema = new Schema({
project: {
type: Schema.Types.ObjectId,
ref: 'Project',
required: true
},
file: {
type: String,
required: true
},
original_name: {
type: String,
required: true
}
}, {
timestamps: true
});
batchSchema.virtual('projectCount')
.get(function () {
return this._projectCount;
})
.set(function (v) {
this._projectCount = v;
});
batchSchema.methods.count = async function () {
let found = await this.model('Batch').count({ project: this.project });
this._projectCount = found;
};
const Batch = mongoose.model('Batch', batchSchema);
module.exports = Batch;
The virtual called projectCount has a simple setter and getter to overwrite the value if you need to or retrieve it once it's been set.
The instance method on each document is called count() and calls the Model.count() method with a query for the current document's project _id.
project.js
'use strict';
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const projectSchema = new Schema({
name: {
type: String,
required: true,
unique: true
},
slug: {
type: String,
required: true,
unique: true
}
}, {
timestamps: true
});
const Project = mongoose.model('Project', projectSchema);
module.exports = Project;
populate.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
const Project = require('./project');
const projects = [];
const batches = [];
for (let i = 0; i < 10; i++) {
const project = new Project({
name: `project${i}`,
slug: `slug${i}`
});
for (let j = 0; j < 10; j++) {
const batch = new Batch({
project: project._id,
file: `file${j}`,
original_name: `name${j}`
});
batches.push(batch);
}
projects.push(project);
}
async function add () {
await conn.dropDatabase();
const savedProjects = await Project.create(projects);
const savedBatches = await Batch.create(batches);
console.log(`Added ${savedProjects.length} projects.`);
console.log(`Added ${savedBatches.length} batches.`);
return conn.close();
}
add();
populate.js is just how I created the collections and the docs for this example, nothing fancy here.
get.js
#!/usr/bin/env node
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const conn = mongoose.connection;
const Batch = require('./batch');
async function get () {
let f = await Batch.findOne({});
await f.count();
console.log(`the project for this batch has ${f.projectCount} matches`);
return conn.close();
}
get().catch(console.error);
Once the instance method count() is called, we have access to the value stored in the virtual projectCount.
bash output
49729301: ./populate.js
Added 10 projects.
Added 100 batches.
49729301: ./get.js
the project for this batch has 10 matches
49729301:
mongo shell output
49729301: mongo --quiet test
> db.batches.findOne()
{
"_id" : ObjectId("5acbbebb4cd320cb4e403e8f"),
"project" : ObjectId("5acbbebb4cd320cb4e403e8e"),
"file" : "file0",
"original_name" : "name0",
"createdAt" : ISODate("2018-04-09T19:27:55.395Z"),
"updatedAt" : ISODate("2018-04-09T19:27:55.395Z"),
"__v" : 0
}
>
as you can see, virtual properties are only available locally, and do not get stored in the db.

Mongoose ODM - failing to validate

I'm trying to perform validation without saving. The API documentation shows that there's a validate method, but it doesn't seem to be working for me.
Here's my schema file:
var mongoose = require("mongoose");
var schema = new mongoose.Schema({
mainHeading: {
type: Boolean,
required: true,
default: false
},
content: {
type: String,
required: true,
default: "This is the heading"
}
});
var moduleheading = mongoose.model('moduleheading', schema);
module.exports = {
moduleheading: moduleheading
}
..and then in my controller:
var moduleheading = require("../models/modules/heading").moduleheading; //load the heading module model
var ModuleHeadingo = new moduleheading({
mainHeadin: true,
conten: "This appears to have validated.."
});
ModuleHeadingo.validate(function(err){
if(err) {
console.log(err);
}
else {
console.log('module heading validation passed');
}
});
You may notice that the parameters I'm passing in are called 'mainHeadin' and 'conten' instead of 'mainHeading' and 'content'. However, even when I do the call to validate() it never returns an error.
I'm obviously using validate incorrectly - any tips? The mongoose documentation is really lacking!
Thanks in advance.
Your validation will never fail because you've created default attributes for both mainHeading and content in your schema. In other words, if you don't set either of those properties, Mongoose will default them to false and "This is the heading" respectively - i.e. they will always be defined.
Once you remove the default property, you'll find that Document#validate will work as you initially expected. Try the following for your schema:
var schema = new mongoose.Schema({
mainHeading: {
type: Boolean,
required: true
},
content: {
type: String,
required: true
}
});

How to create interdependent schemas in Mongoose?

I have two Schemas and I want them to interact with eachother. For instance:
// calendar.js
var mongoose = require('mongoose');
var Scema = mongoose.Schema;
var Day = mongoose.model('Day');
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
module.exports = mongoose.model('Calendar', CalendarSchema);
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var Calendar = mongoose.model('Calendar');
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
module.exports = mongoose.model('Day', DaySchema);
However, I get an error because each schema depends on the other one. Is there a way to get this working using Mongoose? I include them like this:
// app.js
require('./models/calendar');
require('./models/day');
I realize this is an ancient thread, but I'm sure posting the solution will help others down the road.
The solution is to export the module before requiring the interdependent schemas:
// calendar.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
});
module.exports = mongoose.model('Calendar', CalendarSchema);
// now you can include the other model and finish definining calendar
var Day = mongoose.require('./day');
CalendarSchema.methods.getDays = function(cb){
Day.find({ cal: this._id }, cb);
}
// day.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var DaySchema = new Schema({
cal: { type: ObjectId, required: true },
date: { type: Number, required: true },
text: { type: String, default: 'hello' }
});
module.exports = mongoose.model('Day', DaySchema);
// same thing here. require after exporting
var Calendar = require('./calendar');
DaySchema.methods.getCal = function(cb){
Calendar.findById(this.cal, cb);
}
It really is that simple. Explanation by Brian Bickerton can be found here:
http://tauzero.roughdraft.io/3948969265a2a427cf83-requiring-interdependent-node-js-modules
It's nice to be able to use functions by name within a module instead of the lengthy module.exports.name. It's also nice to have a single place to look and see everything to be exported. Typically, the solution I've seen is to define functions and variables normally and then set module.exports to an object containing the desired properties at the end. This works in most cases. Where it breaks down is when two modules are inter-dependent and require each other. Setting the exports at the end leads to unexpected results. To get around this problem, simply assign module.exports at the top, before requiring the other module.
You need require the files. If they are in the same path do this:
//calendar.js
var Day = require('./day');
/* Other logic here */
var CalendarSchema = new Schema({
name: { type: String, required: true },
startYear: { type: Number, required: true }
})
, Calendar;
/* other logic here */
/* Export calendar Schema */
mongoose.model('Calendar', CalendarSchema);
Calendar = mongoose.model('Calendar');
exports = Calendar;
Do the same in day.js
EDIT: As JohnnyHK says this don`t work. Link to explanation

File Structure of Mongoose & NodeJS Project

I currently have all my models (Schema definitions) in the /models/models.js file for my Mongoose/NodeJS application.
I'd like to break these apart into different files as such: user_account.js, profile.js, etc. However I cannot seem to do so as my controllers break and report back "cannot find module" once I pull these classes apart.
My project structure is as follows:
/MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
models.js
server.js
The contents of my models.js file looks like this:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
mongoose.connect('mongodb://localhost/mydb');
var UserAccount = new Schema({
user_name : { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password : { type: String, required: true },
date_created : { type: Date, required: true, default: Date.now }
});
var Product = new Schema({
upc : { type: String, required: true, index: { unique: true } },
description : { type: String, trim: true },
size_weight : { type: String, trim: true }
});
My user.js file (controller) looks like this:
var mongoose = require('mongoose'),
UserAccount = mongoose.model('user_account', UserAccount);
exports.create = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
// Do epic sh...what?! :)
}
How can I break the schema definition into multiple files and also reference it from my controller? When I do reference it (after the schema is in a new file) I get this error:
*Error: Schema hasn't been registered for model "user_account".*
Thoughts?
Here's a sample app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
equipped: Boolean,
owner_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
},
room_id: {
type: mongoose.Schema.Types.ObjectId,
index: true
}
});
var Item = mongoose.model('Item', ItemSchema);
module.exports = {
Item: Item
}
To load this from an item controller in app/controllers/items.js I would do
var Item = require("../models/item").Item;
//Now you can do Item.find, Item.update, etc
In other words, define both the schema and the model in your model module and then export just the model. Load your model modules into your controller modules using relative require paths.
To make the connection, handle that early in your server startup code (server.js or whatever). Usually you'll want to read the connection parameters either from a configuration file or from environment variables and default to development mode localhost if no configuration is provided.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost');
A couple answers here really helped me develop an alternative approach. The original question is regarding breaking just the Schema definition out, but I prefer to bundle the Schema and Model definitions in the same file.
This is mostly Peter's idea, only exporting the model definition by overriding module.exports to make accessing the model from your controller a little less verbose:
Project layout:
MyProject
/controllers
user.js
foo.js
bar.js
// ... etc, etc
/models
Item.js
server.js
models/Item.js would look like:
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
}
});
module.exports = mongoose.model('Item', ItemSchema);
// Now `require('Item.js')` will return a mongoose Model,
// without needing to do require('Item.js').Item
And you access the model in a controller, say user.js, like:
var Item = require(__dirname+'/../models/Item')
...
var item = new Item({name:'Foobar'});
Don't forget to call mongoose.connect(..) in server.js, or wherever else you deem appropriate!
I recently answered a Quora question with regard to this same problem.
http://qr.ae/RoCld1
What I have found very nice and saves on the amount of require calls is to structure your models into a single directory. Make sure you only have a single model per file.
Create an index.js file in the same directory as your models. Add this code to it. Be sure to add the necessary fs require
var fs = require('fs');
/*
* initializes all models and sources them as .model-name
*/
fs.readdirSync(__dirname).forEach(function(file) {
if (file !== 'index.js') {
var moduleName = file.split('.')[0];
exports[moduleName] = require('./' + moduleName);
}
});
Now you can call all your models as follows:
var models = require('./path/to/models');
var User = models.user;
var OtherModel = models['other-model'];
Peter Lyons pretty much covered the basis.
Borrowing from the above example (removing the lines after the schema) I just wanted to add:
app/models/item.js
note: notice where `module.exports` is placed
var mongoose = require("mongoose");
var ItemSchema = module.exports = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
To load it from the app/controllers/items.js
var mongoose = require('mongoose');
var Item = mongoose.model('Item', require('../models/item'));
Another way without the module.exports or require:
app/models/item.js
var mongoose = require("mongoose");
var ItemSchema = new mongoose.Schema({
name: {
type: String,
index: true
},
...
});
mongoose.model('Item', ItemSchema); // register model
In the app/controllers/items.js
var mongoose = require('mongoose')
, Item = mongoose.model('Item'); // registered model
Inspired by sequelize-cli, I have a models directory where i define all schema.
Complete app on github: https://github.com/varunon9/node-starter-app-mongo
models/index.js-
'use strict';
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose');//.set('debug', true);
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
const Schema = mongoose.Schema;
fs
.readdirSync(__dirname)
.filter(fileName => {
return (
fileName.indexOf('.') !== 0)
&& (fileName !== basename)
&& (fileName.slice(-3) === '.js'
);
})
.forEach(fileName => {
const model = require(path.join(__dirname, fileName));
const modelSchema = new Schema(model.schema);
modelSchema.methods = model.methods;
modelSchema.statics = model.statics;
// user.js will be user now
fileName = fileName.split('.')[0];
db[fileName] = mongoose.model(fileName, modelSchema);
});
module.exports = db;
models/user.js-
'use strict';
module.exports = {
schema: {
email: {
type: String,
required: true,
unique: true,
},
mobile: {
type: String,
required: false
},
name: {
type: String,
required: false
},
gender: {
type: String,
required: false,
default: 'male'
},
password: {
type: String,
required: true
},
dob: {
type: Date,
required: false
},
deactivated: {
type: Boolean,
required: false,
default: false
},
type: {
type: String,
required: false
}
},
// instance methods goes here
methods: {
},
// statics methods goes here
statics: {
}
};
I like using classes to organize everything, maybe try this:
const mongoose = require('mongoose')
class UserAccount {
constructor() {
this.schema = new mongoose.Schema({
user_name: { type: String, required: true, lowercase: true, trim: true, index: { unique: true } },
password: { type: String, required: true },
date_created: { type: Date, required: true, default: Date.now }
});
this.model = new mongoose.model('UserAccount', this.schema)
}
create = (obj) => {
return new Promise((resolve, reject) => {
this.model.create({ ...item })
.then((result) => {
resolve(result)
}).catch((err) => {
reject(err)
});
});
}
}
module.exports = UserAccount;
This approach allows you to add custom methods. Plus it combines the controller/model into a single vector, allowing you to detach the model at any time. This may not scale in enterprise, but it may suite smaller apps.

Resources