Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mongoose & serverless offline "Cannot overwrite model once compiled." #258

Closed
kalote opened this issue Jun 26, 2017 · 27 comments
Closed

Mongoose & serverless offline "Cannot overwrite model once compiled." #258

kalote opened this issue Jun 26, 2017 · 27 comments

Comments

@kalote
Copy link

kalote commented Jun 26, 2017

Hello,

When using mongoose + mongodb + serverless offline, cannot make 2 calls in a row => the second one will not work & will exit with error:
'MongooseError: Cannot overwrite User model once compiled.'

Step to reproduce:

  • git clone git@github.com:serverless/examples.git
  • cd aws-node-rest-api-mongodb
  • add the serverless-offline plugin + npm install
  • start local mongodb & update handler.js with mongodb connection string
  • sls offline start

Error:

  • First call create user
    curl -XPOST -H "Content-type: application/json" -d '{"name":"name","firstname":"fname","birth":"01/01/1990","city":"Paris"}' 'http://localhost:3000/user'
  • then call get user
    curl -XGET -H "Content-type: application/json" 'http://localhost:3000/user/USER_ID'
  • error Cannot overwrite User model once compiled

Expected result:

  • second and following call to offline api should work fine

Additional information:

  • node version 7.10
  • npm version 5.0.3
  • serverless version 1.15.3

Any idea why the provided example doesn't work with offline ?

Thanks

@weiner
Copy link

weiner commented Jun 30, 2017

Hello,

I run in the same problem.
Using the cli parameter --skipCacheInvalidation fixt it for me.

--skipCacheInvalidation -c Tells the plugin to skip require cache invalidation. A script reloading tool like Nodemon might then be needed.

@dherault dherault closed this as completed Jul 6, 2017
@majoraze
Copy link

majoraze commented Feb 2, 2018

I'm using a microservice architecture, so my model are in multiple files and services like:
get.js
create.js

They are multiple handlers referencing the same model.

After I put the --skipCacheInvalidation -c flag it stopped doing this error when I call 2 times in a row the get.js service, but when I change and call the create.js after the get.js was called I get the same error as before.

Anyone?

@gokul-t
Copy link

gokul-t commented Feb 4, 2018

you are compiling user schema multiple times, the reason is you call mongoose.model('User', UserSchema) in the first request and then in the second call too.

@majoraze
Copy link

majoraze commented Feb 4, 2018

I know that, but with the microservice architecture this is expected right? I have a require of my model in each service I use that model.

@gokul-t
Copy link

gokul-t commented Feb 6, 2018

what about this?
global.UserSchema = global.UserSchema || mongoose.model('User', UserSchema);
module.exports = global.UserSchema;

@adrianovalente
Copy link

Hey guys! I got a nice hack to avoid this problem using the mongoose method to get an already declared model:

if (!modelAlreadyDeclared()) {
  const Users = mongoose.model('Users', yourSchema)
}

function modelAreadyDeclared () {
  try {
    mongoose.model('Users')  // it throws an error if the model is still not defined
    return true
  } catch (e) {
    return false
  }
}

@zhenyulin
Copy link

manage to resolve this problem via the following snippet, which restrict the behaviour in local offline environment as well as doesn't stop hot reload of models

if (process.env.IS_OFFLINE) {
	delete mongoose.connection.models.Item;
}

export default mongoose.model('Item', Item);

@harunsmrkovic
Copy link

It is also possible to circumvent this issue by using the new --useSeparateProcesses flag

@pedro-mass
Copy link

@harunsmrkovic Just tack on --useSeparateProcesses no further config?

I read that it kicks off a separate process for each function, but I'm still getting the OverwriteModelError: Cannot overwrite error

@PabloEzequiel
Copy link

Hello @adrianovalente, there is a misspelling in your code:
modelAlreadyDeclared != modelAreadyDeclared

so:

if (! modelAlreadyDeclared()) {
  const Users = mongoose.model('Users', yourSchema)
}

function modelAlreadyDeclared() {
  try {
    mongoose.model('Users')  // it throws an error if the model is still not defined
    return true
  } catch (e) {
    return false
  }
}

@PabloEzequiel
Copy link

PabloEzequiel commented Oct 7, 2018

The solution for me was the following code:

let Dataset = mongoose.models.Dataset || mongoose.model('Dataset', DatasetSchema);
Also, in mocha test:

//After all tests are finished:
after(() => {
        mongoose.models = {};
        mongoose.modelSchemas = {};
 });

After read a comment from:
https://github.com/cklanac/bellyful-api

that is a resume from:
Automattic/mongoose#1251

but, in deed, it suggest for mocha:

  after(function() {
     return mongoose.disconnect(() => {
       mongoose.models = {};
       mongoose.modelSchemas = {};
       mongoose.connection.close();
     });
  });

I hope this help ...

@michael-lowe-nz
Copy link

I had this problem.
Using the --skipCacheInvalidation flag worked, but this doesn't allow the server to reload on change.
So as per @harunsmrkovic, I used the --useSeparateProcesses flag instead of --skipCacheInvalidation and now it works with reloading on change.

@larswww
Copy link

larswww commented Jun 11, 2019

--skipCacheInvalidation worked for me, but gave me problems in production. This solved it in my case;
`let user
try {
sled = mongoose.connection.model('User')
} catch (e) {
user = mongoose.model('User', UserSchema)
}

module.exports = user`

@KunalAwasthiKooky
Copy link

Hello,

I run in the same problem.
Using the cli parameter --skipCacheInvalidation fixt it for me.

--skipCacheInvalidation -c Tells the plugin to skip require cache invalidation. A script reloading tool like Nodemon might then be needed.

can you share some links to integrate nodemon in serverless-http powered project?

@Elyx0
Copy link

Elyx0 commented Jul 28, 2019

--useSeparateProcesses breaks vscode debugger sadly

@phanorcoll
Copy link

phanorcoll commented Sep 26, 2019

@PabloEzequiel solution worked for me
let Dataset = mongoose.models.Dataset || mongoose.model('Dataset', DatasetSchema);

@anuj9196
Copy link

anuj9196 commented Sep 26, 2019

@gokul-t 's solution worked for me. Is there any issue with using global for all models? I have more than 10 models.

@nguyenthetoan
Copy link

--useSeparateProcesses is now renamed to --useChildProcesses
use --useChildProcesses works for me at the moment!
#768

@mohitnegi724
Copy link

When I use this solution
export const User = mongoose.models.User || mongoose.model('User', userSchema)

this gives Issue
User.findById is not a function

@tsaxena4k
Copy link

Adding this before schema declaration worked for me !

delete.mongoose.connection.models["name_of_your_model"];

@khalafalla
Copy link

How this comment don't have million like

@premraval-pr
Copy link

Thanks to @adrianovalente and @gokul-t that I could solve this error.

Faced the same issue, gone through the discussion here, and liked both the approaches i.e. By @adrianovalente here and By @gokul-t here

The first approach gave a great TS autocompletion support in the handlers after adding Model Type which is one of the purposes for TS but it was a lot of extra code for me for each model

The second approach was clean and precise but I couldn't get the TS support and error handling in the handler, it just became JS.

Hence I used the Interface approach from TS and came up with the code below which is a bit precise and also uses TS as its most. Just sharing as it could be helpful to anyone in the future.

import { Document, Model, model, models, Schema } from "mongoose";

const userSchema: Schema = new Schema(
    {
        first_name: {
            type: String,
            required: true,
        },
        last_name: {
            type: String,
            required: true,
        },
    },
    {
        collection: "users",
    }
);

interface IUser extends Document {
    first_name: string;
    last_name: string;
}

export default (models.users
    ? models.users
    : model("users", userSchema)) as Model<IUser>;

@mahdisoultana
Copy link

@premraval-pr this is really good solution work in my case Thank you very much

@itsgratien
Copy link

Thanks to @adrianovalente and @gokul-t that I could solve this error.

Faced the same issue, gone through the discussion here, and liked both the approaches i.e. By @adrianovalente here and By @gokul-t here

The first approach gave a great TS autocompletion support in the handlers after adding Model Type which is one of the purposes for TS but it was a lot of extra code for me for each model

The second approach was clean and precise but I couldn't get the TS support and error handling in the handler, it just became JS.

Hence I used the Interface approach from TS and came up with the code below which is a bit precise and also uses TS as its most. Just sharing as it could be helpful to anyone in the future.

import { Document, Model, model, models, Schema } from "mongoose";

const userSchema: Schema = new Schema(
    {
        first_name: {
            type: String,
            required: true,
        },
        last_name: {
            type: String,
            required: true,
        },
    },
    {
        collection: "users",
    }
);

interface IUser extends Document {
    first_name: string;
    last_name: string;
}

export default (models.users
    ? models.users
    : model("users", userSchema)) as Model<IUser>;

Thank you for sharing.

@nyel-gh
Copy link

nyel-gh commented Apr 6, 2022

@premraval-pr This is exactly what I needed. Thanks!

@francosalcedo
Copy link

Thanks to @adrianovalente and @gokul-t that I could solve this error.

Faced the same issue, gone through the discussion here, and liked both the approaches i.e. By @adrianovalente here and By @gokul-t here

The first approach gave a great TS autocompletion support in the handlers after adding Model Type which is one of the purposes for TS but it was a lot of extra code for me for each model

The second approach was clean and precise but I couldn't get the TS support and error handling in the handler, it just became JS.

Hence I used the Interface approach from TS and came up with the code below which is a bit precise and also uses TS as its most. Just sharing as it could be helpful to anyone in the future.

import { Document, Model, model, models, Schema } from "mongoose";

const userSchema: Schema = new Schema(
    {
        first_name: {
            type: String,
            required: true,
        },
        last_name: {
            type: String,
            required: true,
        },
    },
    {
        collection: "users",
    }
);

interface IUser extends Document {
    first_name: string;
    last_name: string;
}

export default (models.users
    ? models.users
    : model("users", userSchema)) as Model<IUser>;

thanksss

@ghostwalkerj
Copy link

Perfect. This is exactly what I needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests