Node js and Code First - node.js

I've worked on Entity Framework with Code First approach. Now I am learning Node.js I wonder is there a way to make the same code first approach using Node.js and some libraly? I am thinking of using MySql as database.

You can look into Sequelize. Example of usage from the home page (I added comments to relate to Code First):
var Sequelize = require('sequelize');
// define your "context"
var sequelize = new Sequelize('database', 'username', 'password');
// define your "entities"
var User = sequelize.define('user', {
username: Sequelize.STRING,
birthday: Sequelize.DATE
});
// use them
sequelize.sync().then(function() {
return User.create({
username: 'janedoe',
birthday: new Date(1980, 6, 20)
});
}).then(function(jane) {
console.log(jane.get({
plain: true
}));
});

Related

Cannot overwrite 'user' model once compliled when adding a second schema to a database. [duplicate]

Not Sure what I'm doing wrong, here is my check.js
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
var user = mongoose.model('users',{
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
user.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere })
});
and here is my insert.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/event-db')
var user = mongoose.model('users',{
name:String,
email:String,
password: String,
phone:Number,
_enabled:Boolean
});
var new_user = new user({
name:req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
_enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
Whenever I'm trying to run check.js, I'm getting this error
Cannot overwrite 'users' model once compiled.
I understand that this error comes due to mismatching of Schema, but I cannot see where this is happening ? I'm pretty new to mongoose and nodeJS.
Here is what I'm getting from the client interface of my MongoDB:
MongoDB shell version: 2.4.6 connecting to: test
> use event-db
switched to db event-db
> db.users.find()
{ "_id" : ObjectId("52457d8718f83293205aaa95"),
"name" : "MyName",
"email" : "myemail#me.com",
"password" : "myPassword",
"phone" : 900001123,
"_enable" : true
}
>
Another reason you might get this error is if you use the same model in different files but your require path has a different case.
For example, in my situation I had require('./models/User') in one file, and then in another file where I needed access to the User model, I had require('./models/user').
I guess the lookup for modules & mongoose is treating it as a different file. Once I made sure the case matched in both it was no longer an issue.
The error is occurring because you already have a schema defined, and then you are defining the schema again. Generally what you should do is instantiate the schema once, and then have a global object call it when it needs it.
For example:
user_model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
module.exports = mongoose.model('users', userSchema);
check.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
User.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere
})
});
insert.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
mongoose.connect('mongodb://localhost/event-db');
var new_user = new User({
name:req.body.name
, email: req.body.email
, password: req.body.password
, phone: req.body.phone
, _enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
I had this issue while 'watching' tests.
When the tests were edited, the watch re-ran the tests, but they failed due to this very reason.
I fixed it by checking if the model exists then use it, else create it.
import mongoose from 'mongoose';
import user from './schemas/user';
export const User = mongoose.models.User || mongoose.model('User', user);
I had this issue while unit testing.
The first time you call the model creation function, mongoose stores the model under the key you provide (e.g. 'users'). If you call the model creation function with the same key more than once, mongoose won't let you overwrite the existing model.
You can check if the model already exists in mongoose with:
let users = mongoose.model('users')
This will throw an error if the model does not exist, so you can wrap it in a try/catch in order to either get the model, or create it:
let users
try {
users = mongoose.model('users')
} catch (error) {
users = mongoose.model('users', <UsersSchema...>)
}
If you are using Serverless offline and don't want to use --skipCacheInvalidation, you can very well use:
module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);
I have been experiencing this issue & it was not because of the schema definitions but rather of serverless offline mode - I just managed to resolve it with this:
serverless offline --skipCacheInvalidation
Which is mentioned here https://github.com/dherault/serverless-offline/issues/258
Hopefully that helps someone else who is building their project on serverless and running offline mode.
If you made it here it is possible that you had the same problem i did.
My issue was that i was defining another model with the same name.
I called my gallery and my file model "File". Darn you copy and paste!
I solved this by adding
mongoose.models = {}
before the line :
mongoose.model(<MODEL_NAME>, <MODEL_SCHEMA>)
Hope it solves your problem
This happened to me when I write like this:
import User from '../myuser/User.js';
However, the true path is '../myUser/User.js'
Click here! Official example.
Most important! thing is to export like this
export default mongoose.models.Item || mongoose.model('Item', itemsSchema)
To Solve this check if the model exists before to do the creation:
if (!mongoose.models[entityDBName]) {
return mongoose.model(entityDBName, entitySchema);
}
else {
return mongoose.models[entityDBName];
}
I know there is an accepted solution but I feel that the current solution results in a lot of boilerplate just so that you can test Models. My solution is essentially to take you model and place it inside of a function resulting in returning the new Model if the Model has not been registered but returning the existing Model if it has.
function getDemo () {
// Create your Schema
const DemoSchema = new mongoose.Schema({
name: String,
email: String
}, {
collection: 'demo'
})
// Check to see if the model has been registered with mongoose
// if it exists return that model
if (mongoose.models && mongoose.models.Demo) return mongoose.models.Demo
// if no current model exists register and return new model
return mongoose.model('Demo', DemoSchema)
}
export const Demo = getDemo()
Opening and closing connections all over the place is frustrating and does not compress well.
This way if I were to require the model two different places or more specifically in my tests I would not get errors and all the correct information is being returned.
This may give a hit for some, but I got the error as well and realized that I just misspelled the user model on importing.
wrong: const User = require('./UserModel');
correct: const User = require('./userModel');
Unbelievable but consider it.
Here is one more reason why this can happen. Perhaps this can help someone else. Notice the difference, Members vs Member. They must be the same...
export default mongoose.models.Members || mongoose.model('Member', FamilySchema)
Change to:
export default mongoose.models.Member || mongoose.model('Member', FamilySchema)
What you can also do is at your export, make sure to export an existing instance if one exists.
Typescript solution:
import { Schema, Document, model, models } from 'mongoose';
const UserSchema: Schema = new Schema({
name: {
type: String
}
});
export interface IUser extends Document {
name: string
}
export default models.Users || model<IUser>('Users', UserSchema);
This problem might occur if you define 2 different schema's with same Collection name
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: String,
});
// Trying to get the existing model to avoid OverwriteModelError
module.exports = mongoose.model("user") || mongoose.model('user', userSchema);
You can easily solve this by doing
delete mongoose.connection.models['users'];
const usersSchema = mongoose.Schema({...});
export default mongoose.model('users', usersSchema);
There is another way to throw this error.
Keep in mind that the path to the model is case sensitive.
In this similar example involving the "Category" model, the error was thrown under these conditions:
1) The require statement was mentioned in two files: ..category.js and ..index.js
2) I the first, the case was correct, in the second file it was not as follows:
category.js
index.js
I solved this issue by doing this
// Created Schema - Users
// models/Users.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
export const userSchema = new Schema({
// ...
});
Then in other files
// Another file
// index.js
import { userSchema } from "../models/Users";
const conn = mongoose.createConnection(process.env.CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
conn.models = {};
const Users = conn.model("Users", userSchema);
const results = await Users.find({});
Better Solution
let User;
try {
User = mongoose.model("User");
} catch {
User = mongoose.model("User", userSchema);
}
I hope this helps...
I faced this issue using Next.js and TypeScript. The top answers made it such that typings would not work.
This is what works for me:
const { Schema } = mongoose
export interface IUser {
name: string
email: string
}
const UserSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true },
})
const UserModel = () => mongoose.model<IUser>('User', UserSchema)
export default (mongoose.models.User || UserModel()) as ReturnType<
typeof UserModel
>
I faced the same Issue with NextJS and MongoDB atlas. I had a models folder
with the model of session stored, but the problem was not that I defined the Schema twice.
Make sure the Collection is empty and does not have a previous Document
If it does, then Simply declare a Model without Schema, like this:
const Session = mongoose.model("user_session_collection")
You can delete the previous records or backup them, create the schema and then apply query on the database.
Hope it helped
Below is the full solution to similar problem when using Mongoose with Pagination in combination with Nuxt and Typescript:
import {model, models, Schema, PaginateModel, Document } from 'mongoose';
import { default as mongoosePaginate } from 'mongoose-paginate-v2';
export interface IUser extends Document {
name: string;
}
const UserSchema: Schema = new Schema({
name: String
});
UserSchema.plugin(mongoosePaginate)
interface User<T extends Document> extends PaginateModel<T> {}
const User: User<IUser> = models['User'] as User<IUser> || model<IUser>('User', UserSchema) as User<IUser>;
export default User
tsconfig.json:
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"#/*": ["./*"]
},
"types": ["#types/node", "#nuxt/types"]
},
"exclude": ["node_modules"]
}
To make pagination working you will also need to install
#types/mongoose-paginate-v2
The above solution should also deal with problems related to hot reloading with Nuxt (ServerMiddleware errors) and pagination plugin registration.
A solution that worked for me was just to check if an instance of the model exists before creating and exporting the model.
import mongoose from "mongoose";
const { Schema } = mongoose;
const mongoosePaginate = require("mongoose-paginate");
const articleSchema = new Schema({
title: String, // String is shorthand for {type: String}
summary: String,
data: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
published: { type: Boolean, default: true },
tags: [{ name: String }],
category: String,
_id: String,
});
const Post = mongoose.models.Post ? mongoose.models.Post : mongoose.model("Post",articleSchema);
export default Post;
The schema definition should be unique for a collection, it should not be more then one schema for a collection.
If you want to overwrite the existing class for different collection using typescript
then you have to inherit the existing class from different class.
export class User extends Typegoose{
#prop
username?:string
password?:string
}
export class newUser extends User{
constructor() {
super();
}
}
export const UserModel = new User ().getModelForClass(User , { schemaOptions: { collection: "collection1" } });
export const newUserModel = new newUser ().getModelForClass(newUser , { schemaOptions: { collection: "collection2" } });
I had the same problem,
reason was I defined schema an model in a JS function, they should be defined globally in a node module, not in a function.
just export like this
exports.User = mongoose.models.User || mongoose.model('User', userSchema);
ther are so many good answer but for checking we can do easier job.
i mean in most popular answer there is check.js ,our guy made it so much complicated ,i suggest:
function connectToDB() {
if (mongoose.connection.readyState === 1) {
console.log("already connected");
return;
}
mongoose.connect(
process.env.MONGODB_URL,
{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err) => {
if (err) throw err;
console.log("DB connected");
},
);
}
readyState== 1 means connected
so does not try to connect again
so you won't get the error
i think it because of connecting while it is connected
it is another way of connecting to db
Make sure you are not using the same model name for two different schemas.
Example:
// course model
const mongoose = require("mongoose");
const courseSchema = new mongoose.Schema({
course: {
type: String,
required: true,
},
course_category: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", courseSchema);
// student model
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", studentSchema);

Sequelize could not create a new table

I'm currently learning sequelize and its define method is used to create new database table, but it is not working...there is no error...do you know whats going on?
var express = require('express');
var Sequelize = require('sequelize');
var sequelize = require('../db/sequelize_conf.js');
var router = express.Router();
var User = sequelize.define('user',
{
name: Sequelize.STRING,
password: Sequelize.STRING,
mail: Sequelize.STRING
},
{
freezeTableName: true,
timestamps: false
});
User.create({
name: 'XiaoMing',
password: '1234567890',
mail: 'xiaoming#qq.com'
}).then(function(result){
console.log('inserted XiaoMing ok');
}).catch(function(err){
console.log('inserted XiaoMing error');
console.log(err.message);
});
module.exports=router;
It says the user table doesnt exist....
Okay ,
First check in the database if user table is there or not , coz the below code won't create a new table , it will just create a new entry in to user table
User.create({
name: 'XiaoMing',
password: '1234567890',
mail: 'xiaoming#qq.com'
})
If you want to create a table then you can use ,
// This will create only all the tables defines via sequelize
sequelize.sync().then(() =>
// put your user create code inside this
);
// OR
// This will create only user table
User.sync().then(() => {
// put your user create code inside this
});
I hope this will clear all your doubts

Testing mongoose pre-save hook

I am quite new to testing nodejs. So my approach might be completely wrong. I try to test a mongoose models pre-save-hook without hitting the Database. Here is my model:
// models/user.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
UserSchema = new Schema({
email: {type: String, required: true},
password: {type: String, required: true}
});
UserSchema.pre('save', function (next) {
const user = this;
user.email = user.email.toLowerCase();
// for testing purposes
console.log("Pre save hook called");
next();
});
module.exports = mongoose.model("User", UserSchema);
As I said, I do not want to hit the Database with my test, so I tried using a sinon stub of the Users save() method:
// test/models/user.js
const sinon = require("sinon");
const chai = require("chai");
const assert = chai.assert;
const User = require("../../models/user");
describe("User", function(){
it("should convert email to lower case before saving", (done) => {
const user = new User({email: "Valid#Email.com", password: "password123"});
const saveStub = sinon.stub(user, 'save').callsFake(function(cb){ cb(null,this) })
user.save((err,res) => {
if (err) return done(err);
assert.equal(res.email,"valid#email.com");
done();
})
})
});
However, If I do it like that the pre-save hook will not be called. Am I on the wrong path or am I missing something? Or is there maybe another way of triggering the pre-save hook and testing its outcome? Thanks very much in advance!
Before we start: I'm looking for the same thing as you do and I've yet to find a way to test the different hooks in Mongoose without a database. It's important that we distinguish between testing our code and testing mongoose.
Validation is middleware. Mongoose registers validation as a pre('save') hook on every schema by default. http://mongoosejs.com/docs/validation.html
Considering that validate will always be added to the model and I wish to test the automated fields in my model, I've switched from save to validate.
UserSchema = new Schema({
email: {type: String, required: true},
password: {type: String, required: true}
});
UserSchema.pre('validate', function(next) {
const user = this;
user.email = user.email.toLowerCase();
// for testing purposes
console.log("Pre validate hook called");
next();
});
The test will now look like:
it("should convert email to lower case before saving", (done) => {
const user = new User({email: "VALID#EMAIL.COM", password: "password123"});
assert.equal(res.email,"valid#email.com");
}
So What About the Pre Save Hook?
Because I've moved the business logic for automatic fields from 'save' to 'validate', I'll use 'save' for database specific operations. Logging, adding objects to other documents, and so on. And testing this only makes sense with integration with a database.
I just faced the same issue and managed to solve it by extracting the logic out of the hook, making it possible to test it in isolation. With isolation I mean without testing anything Mongoose related.
You can do so by creating a function, that enforces your logic, with the following structure:
function preSaveFunc(next, obj) {
// your logic
next();
}
You can then call it in your hook:
mySchema.pre('save', function (next) { preSaveFunc(next, this); });
This will make the reference to this available inside the function, so you can work with it.
The extracted part can then be unit tested by overwriting the next function to a function without a body.
Hope this will help anyone as it actually was a pain to solve this with my limited knowledge on Mongoose.

Sequelize - Custom Create Method

Is it possible to create a custom create method in Sequelize. I would like it so that I could pass in a URL to download a thumbnail photo from, and then a method would be called with that data to download the photo, upload it to S3, and save that S3 URL as the thumbnailPhotoURL.
Here is an example of the syntax I'm trying to do:
var Sequelize = require('sequelize');
var sequelize = new Sequelize('database', 'username', 'password');
var User = sequelize.define('user', {
username: Sequelize.STRING,
birthday: Sequelize.DATE,
thumbnailPhotoURL: Sequelize.STRING
});
sequelize.sync().then(function() {
return User.create({
username: 'janedoe',
birthday: new Date(1980, 6, 20),
// this will be used to download and upload the thumbnailPhoto to S3
urlToDownloadThumbnailPhotoFrom: 'http://example.com/test.png'
});
}).then(function(jane) {
console.log(jane.get({
plain: true
}));
});
Notice how I'm calling User.create with a urlToDownloadThumbnailPhotoFrom parameter, rather than a thumbnailPhotoURL parameter
You can use before create hook no need to define a custom create function
var Sequelize = require('sequelize');
var sequelize = new Sequelize('database', 'username', 'password');
var User = sequelize.define('user', {
username: Sequelize.STRING,
birthday: Sequelize.DATE,
thumbnailPhotoURL: Sequelize.STRING
});
User.beforeCreate(function(model, options, cb) {
var urlToDownloadThumbnailPhotoFrom = model.urlToDownloadThumbnailPhotoFrom;
//.....Here you write the logic to get s3 url using urlToDownloadThumbnailPhotoFrom and then assign it to model and call the call back it will automatically get saved
model.thumbnailPhotoURL = thumbnailPhotoURL;
cb();
});

Save mongoose document again after deleting it

I try to unit test my restify node.js-app using mocha and without mocking out the mongodb database. As some tests will alter the database, I'd like to reset its contents before each test.
In my tests I also need to access the mongoose documents I am creating. Thus I have to define them outside of the beforeEach hook (see the user document below).
However, it seems like it's not possible to save a document a second time after emptying the database.
Below is a minimal example I've come up with. The second test will fail in that case, because user won't get saved a second time. If I delete the first test, beforeEach only gets called once and everything works nicely.
Also if I define user inside the beforeEach hook, it works as well.
So my actual question: Is it possible to work around this issue and save a document a second time after deleting it? Or do you have any other idea on how I can reset the database inside the beforeEach hook? What's the proper way to have the same database setup before each test case?
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var should = require('should')
var flow = require('async')
var UserSchema = new Schema({
username: {type: String, required: true, unique: true},
password: {type: String, required: true},
name: {type: String, default: ''}
})
mongoose.model('User', UserSchema)
var User = mongoose.model('User')
describe('test mocha', function() {
var user = new User({
username: 'max',
password: 'asdf'
})
before(function(done) {
var options = {server: {socketOptions: {keepAlive: 1}}}
mongoose.connect('mongodb://localhost/unittest', options, done)
})
beforeEach(function(done) {
flow.series([
function(callback) {
User.collection.remove(callback)
}, function(callback) {
user.save(callback)
}
], function(err, res) {
done()
})
})
it('should pass', function(done) {
true.should.equal(true)
// also access some elements of user here
done()
})
it('should have a user', function(done) {
User.find().exec(function(err, res) {
res.should.not.be.empty
})
done()
})
after(function(done) {
mongoose.disconnect()
done()
})
})
I faced same problem,I generated a copy of the document to save. When need to save the document after deleting it I saved the copy, and it worked. Like
var user = new User({
username: 'max',
password: 'asdf'
});
var userCopy = new User({
username: 'max',
password: 'asdf'
});
And in test cases.
user.remove(callback)
}, function(callback) {
userCopy.save(callback){
// should.not.exist(err)
}
}
It might not be good solution ,but it worked for me.

Resources