Search code examples
node.jsmongodbexpressmongoose

Error: Accessing non-existent property. Module exports in circular dependency


I am trying to create a new document using mongoose inside my POST Route handler. I am using express.Router to define my routes.

index.js

const express = require('express')
const app = express()
const userRouter = require('./routes/user.js')
const mongoose = require('mongoose')
mongoose.connect('mongodb://127.0.0.1:27017/Users')

const userSchema = new mongoose.Schema({
    first_name: {
        type: String,
        required: true
    },
    last_name: {
        type: String,
        required: true
    },
    email: {
        type: String,
        required: true
    },
    gender: String
})
const User = new mongoose.model('user', userSchema)

app.use(express.urlencoded({ extended: false }))
app.use(express.json())

app.use('/user', userRouter)
app.listen(3000, () => console.log('Server Started!'))
module.exports = User;

users.js

const express = require('express')
const User = require('../index')
const router = express.Router()

router.post('/', async (req, res) => {
    const newUser = await User.create({
        first_name: "John",
        last_name: 'Doe',
        email: '[email protected]',
        gender: 'Male'
    })
    res.send({ status: 'success', created: newUser })
})
module.exports = router;

However, I am receiving the following warning-

Accessing non-existent property 'create' of module exports inside circular dependency

and the error-

User.create is not a function

I assume it is saying User is undefined even though I have exported User model from index.js (apparently).

What is wrong with the code?


Solution

  • Note: This is a detailed answer, there is a shorter version also posted.

    This is a case of cyclic module dependency.

    Example, the module a.js requires the module b.js, meantime b.js also requires the same a.js.

    In this case the module index.js requires user.js, meantime user.js also requires the same index.js. And thus leading to cyclic dependency.

    Technically this would cause an infinite calling of the require module until the stack will overflow.

    Nodejs warns about this situation as you have seen, and it then allows such cyclic dependency by breaking the infinite looping. It breaks this infinite looping at the very first cyclic reference by returning an unfinished object for the first recursive require call. However, take care, it is Node's technical solution to circumvent the infinite looping, therefore this unfinished object may not have reached the stage of having the complete functionalities as it has been designed and coded. The following quote from Node’s documentation may also mean the same.

    Careful planning is required to allow cyclic module dependencies to work correctly within an application.

    This unfinished object is the reason for the error “User.create is not a function” in this case. You can test and see this unfinished object via console.log in users.js. The unfinished object in this case is an empty object. As we know, an empty object is not the same as a null object.

    users.js
    …
    const User = require('./user');
    console.log(User);
    ….
    
    Output:
    {}
    

    Please also note that though the unfinished object in this case is en empty object, it does not need to be so always. It entirely depends upon how those modules have been structured or coded. This point is very important to understand for those who would like to code with cyclic dependency. Therefore request you may please refer to the link link 1 below, for a very good example documented by Nodejs. Please note that the unfinished object in this example, is not an empty object. It has one property done. However its values are different in different stages. This is the point to understand prior to rely on this kind of coding.

    Coming to the fix, for the sake of simplicity, request please refactor the code to avoid cyclic dependency. @jQueeny has commented in the same line, please create a new module and keep everything related to the user schema within it and import it in the route handler module. He has also provided the specifics about it, please refer to it.

    Optionally, please also refer to link link 2 for the general guidance on how mongoose object will be imported and its connection is handled on the server side.

    Links

    1 Cycles

    2. Why do we import Mongoose in the Express server file?