Mongoose schema suddenly invalid - javascript

I'm currently working on a MERN Application. I'm fairly new to backend and database related topics.
I have configured this mongoose model, using the following schema:
item.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const itemSchema = new Schema({
title: String, // String is shorthand for {type: String}
author: String,
body: String,
date: { type: Date, default: Date.now },
meta: {
reports: {type: Number, default: 0}
}
});
const Item = mongoose.model('Item', itemSchema);
module.exports = Item;
In my api.js file, this is how I import the model:
const Item = require('../models/item.model');
Now, when starting my server I get following error:
TypeError: Invalid schema configuration: `String` is not a valid type at path `title`
I'm wondering why the type is invalid. I am using the syntax from the mongoose docs.
I did refactor my code and moved my backend folder. I can't imagine that this is related to the error, but since refactoring, this error appeared. I also tried moving the backend folder back where it was but I still get this error.

You can use another way for type definitions
title: 'String'
or
title: mongoose.Schema.Types.String

Related

OverwriteModelError: Cannot overwrite `Movies` model once compiled [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);

How to use another model in model definition in Mongoose

I'm writing mongoose in Node.js, ES6.
I first specified a model called Address, and would like to use the Address model in the definition of another model, Channel.
The codes are like the following:
// Definition of Address
import mongoose from 'mongoose';
export const Address = mongoose.model('Address',
{
id: mongoose.SchemaTypes.ObjectId,
customer_id: String,
addresses: [{
address_type: String,
address_info: String,
}]
});
For another model Channel, I would like to have a subscriber field, which is a list of Address.
My tentative code is like the following:
import mongoose from 'mongoose';
import {Address} from './Address.js';
export const Channel = mongoose.model('Channel',
{
id: mongoose.SchemaTypes.ObjectId,
name: String,
path: String,
subscribers: [Address],
});
However, I got error like this:
TypeError: Invalid schema configuration: `model` is not a valid type within the array `subscribers`
I wonder how should I implement the idea in NodeJS?
If I got it right, you want each channel have an array of addresses specified to it. so you have to specify address field in your channel this way:
import mongoose from 'mongoose';
//import {Address} from './Address.js';
export const Channel = mongoose.model('Channel',
{
id: mongoose.Schema.Types.ObjectId,
name: String,
path: String,
subscribers: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Address'
}],
});
you do not need Address model imported into your Channel model, MongoDB will recognize it automatically. then when you want to create a channel document create it like this:
import {Address} from './Address';
import {Channel} from './Channel';
async function createChannel(){
Channel.create({
name: 'theName',
path: 'thePath',
subscribers: [await Address.find()] //you can add all addresses by just use find or use your specific query to find your favored addresses.
})
}

graphqljs - Query root type must be provided

How do I get around this error? For this particular schema, I do not need any queries (they're all mutations). I cannot pass null and if I pass an empty GraphQLObjectType it gives me the error:
Type Query must define one or more fields.
If you're using graphql-tools (maybe other SDL tools too), you can specify an empty type (such as Query) using:
type Query
Instead of
type Query {}
If you're building the schema programatically you'll have to add dummy query, much like:
new GraphQLObjectType({
name: 'Query',
fields: {
_dummy: { type: graphql.GraphQLString }
}
})
Which is the programmatic equivalent of the SDL:
type Query {
_dummy: String
}
If you are using Node.js express -
Your schema should have both Root query and root Mutation, although you don't have any query resolvers but still you need to define the root query.
E.g. -
type RootQuery {
hello: String!
}
schema {
query: RootQuery
mutation: RootMutation
}
If you're using express you can do this:
const {GraphQLObjectType, GraphQLSchema, GraphQLString} = require("graphql")
const Schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "Query",
fields: () => ({
message: {
type: GraphQLString,
resolve: () => "Hello World"
}
})
})
})
Use GraphQLObjectType. And the property Query should be in lowercase as query.

Graphql requiring module outside vs inside GraphQLObjectType

May be the title is not suitable with my problem but let me explain my scenario.
I am working with Graphql schema.Here is my initial schema.js file https://github.com/sany2k8/graphql-udemy/blob/master/schema/schema.js
and it was working fine then I decided to split it into different small files e.g root_query_type.js, mutation.js, user_type.js and company_type.js. All the files are exported as module and required circularly. For example -
user_type.js
const graphql = require('graphql');
const axios = require('axios');
const { GraphQLObjectType, GraphQLString, GraphQLInt } = graphql;
//const CompanyType = require('./company_type'); // *this line causing error*
const UserType = new GraphQLObjectType({
name: "User",
fields: () => ({
id:{ type: GraphQLString},
firstName:{ type: GraphQLString},
age:{ type: GraphQLInt},
company :{
type: require('./company_type'), // *this line fix the error*
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(res => res.data)
}
}
})
});
module.exports = UserType;
company_type.js
const graphql = require('graphql');
const axios = require('axios');
const { GraphQLObjectType, GraphQLString, GraphQLList } = graphql;
const UserType = require('./user_type');
const CompanyType = new GraphQLObjectType({
name: "Company",
fields: ()=> ({
id: { type: GraphQLString},
name: { type: GraphQLString},
description: { type: GraphQLString},
users:{
type: new GraphQLList(UserType),
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.id}/users`)
.then(res => res.data)
}
}
})
});
module.exports = CompanyType;
On my user_type.js file when I use const CompanyType = require('./company_type'); at the top of file like this const UserType it is showing me below error message
Error: User.company field type must be Output Type but got: [object
Object].
but if I comment out that line and put it directly then it works.
company :{
type: require('./company_type'),
resolve(parentValue, args){
return axios.get(`http://localhost:3000/companies/${parentValue.companyId}`)
.then(res => res.data)
}
}
So basically my question is why it is not working with const CompanyType = require('./company_type'); and but working withtype: require('./company_type'). I could be a simple logical issue but It couldn't able to find.Please help me out.
The behavior you're seeing is not specific to GraphQL, but rather node in general. You have cyclic dependencies in your modules, which is causing your require statement inside user_type.js to resolve to an incomplete copy of company_type.js.
According to the docs, given two modules (a.js and b.js) that require each other:
When main.js loads a.js, then a.js in turn loads b.js. At that point,
b.js tries to load a.js. In order to prevent an infinite loop, an
unfinished copy of the a.js exports object is returned to the b.js
module. b.js then finishes loading, and its exports object is provided
to the a.js module.
Moving the require statements inside your exports definition is one solution. You could also move your exports definition above your require calls to get the same affect. This question looks at cyclic dependencies in more depth and also offers some alternative solutions.
As a side note, this is one of the reasons I would recommend moving away from declaring a GraphQL schema programatically. You can use graphql-tools's generate-schema to generate a schema from a GraphQL language document. This prevents you from dealings with potential cycle dependencies and results in a much more readable schema. You can modularize your schema easily as well; your type definitions are just strings and your resolvers are just objects -- both of which can easily be combined.

Realm React Native - Migration is required: Target type 'string' doesn't exist for property 'emailAddresses'

I am building a small app with React Native and want to use Realm for persistency.
I defined the following schema for a Person:
const personSchema = {
name: 'Person',
properties: {
familyName: {type: 'string', optional: true},
givenName: {type: 'string'},
middleName: {type: 'string', optional: true},
emailAddresses: {type: 'list', objectType: 'string'},
}
}
export class Person {}
Person.schema = personSchema
The information as to how a Realm schema needs to be defined can be found in the Realm docs.
I then instantiate the DB like so:
const schemas = [Person]
const db = new Realm({schema: schemas})
However, there is an exception when reaching the last line giving me the following error:
Unhandled JS Exception: Migration is required due to the following
errors:
Target type string doesn't exist for property emailAddresses.
I am using the iOS simulator for testing and deleted the app several times and then reinstalled it using the play button in Xcode.
Does anyone have an idea why I am getting this exception?
Update
I now created a separate schema for another db object: EmailAdress
const emailAddressSchema = {
name: 'EmailAddress',
properties: {
label: 'string',
email: 'string'
}
}
export class EmailAddress {}
EmailAddress.schema = emailAddressSchema
I also changed the objectType of emailAddresses in the personSchema to be of type EmailAddress now:
...
emailAddresses: {type: 'list', objectType: 'EmailAddress'},
...
Now, I am not getting the exception any more. Isn't it possible to add a property to a Realm class that is a list of strings?
Lists of primitives are not yet supported in Realm. Until they are you need to wrap primitive types in an object as you have done with EmailAddress

Categories

Resources