Saturday, 8 April 2017

Singleton not working in Mongoose

Background

I have Mongoose Schema about Surveys, that needs to check if the Survey belongs to a set of countries that is in another collection.

Code

To check this, I have a surveySchema, a countrySchema, and a file where I create the models and connect to the DB.

To perform the check that a survey belongs to a valid country, I am using Mongoose async validators in surveySchema like the following:

surveySchema.js:

"use strict";

const mongoose = require("mongoose");

const models = require("./models.js");
const {
    Country
} = models().getModels();

const surveySchema = {
    subject: { type: String, required: true },
    country: {
        type: String,
        validate: {
            validator: {
                isAsync: true,
                validator: async function(val, callback) {
                     const countries = await Country.find({"isoCodes.alpha2": val});
                     callback(countries.length === 1);
                }
            },
            message: "The country {VALUE} is not available in the DB at the moment."
        }
    }
};


module.exports = new mongoose.Schema(surveySchema);
module.exports.surveySchema = surveySchema;

countrySchema.js:

"use strict";

const mongoose = require("mongoose");

const countrySchema = {
    name: { type: String, required: true },
    isoCodes:{
        alpha2: { type: String, required: true }
        }
    }
};


module.exports = new mongoose.Schema(countrySchema);
module.exports.countrySchema = countrySchema;

models.js:

"use strict";

const mongoose = require("mongoose");
const fs = require("fs");

const DB_CONFIG = "./config/dbConfig.json";

/**
 *  Module responsible for initializing the Models. Should be a Singleton.   
 */
module.exports = function() {
    let models;

    const initialize = () => {

        const {
            dbConnectionURL
        } = JSON.parse(fs.readFileSync(DB_CONFIG, "utf8"));

        mongoose.connect(dbConnectionURL);
        mongoose.Promise = global.Promise;

        models = {
            Country: mongoose.model('Country', require("./countrySchema.js")),
            Survey: mongoose.model('Survey', require("./surveySchema.js"))
        };

    };

    const getModels = () => {
        if (models === undefined)
            initialize();

        return models;
    };

    return Object.freeze({
        getModels
    });
};

The idea here is that I am using the models.js file in other places as well. Because this file is also responsible for connecting to the DB, I decided to make it a Singleton. This way, I should only connect once, and all further requests will always return the same Models, which would be ideal.

Problem

The problem here, is that every time I run something like:

const {
    Survey
} = models().getModels();

The code tries to connect to the DB and tries to re-initialize the models again. The Singleton, is not a Singleton at all.

This is mostly visible in my api.js files, where I also use the models.js file.

Consequently, every time I launch the MEAN app I have, I have two types of critical errors:

  • Tried to connect to a DB whose connection is still active
  • Tried to instantiate models when they were already instantiated.

This whole thing happens because every time I call models(), the variable models is undefined, and so I run the initialize function again ...

Questions

  1. Is there any other way to check if a Survey's country is part of the county's collection without making a async validation?
  2. How can I structure/change my code so this doesn't happen?


via Flame_Phoenix

No comments:

Post a Comment