Why doesn't my Mongoose schema method "see" my required object? - node.js

I'm really confused about a variable scope issue with a file required via a path in a config file. Why does my Mongoose schema method "see" the required objects when called from within the model file but not when called from my app.js file? I'm convinced that I must be doing something obviously wrong but I can't see it.
The Node project has the following (simplified) structure:
|models
-index.js
-story.js
-post.js
-app.js
-config.js
This is config.js:
config = {};
config.test = 'test';
config.models = __dirname + '/models';
module.exports = config;
This is story.js:
var config = require('../config.js');
var models = require(config.models);
var foo = {};
foo.bar = 'baz';
var storySchema = mongoose.Schema
({
author: {type: mongoose.Schema.Types.ObjectId},
root: {type: mongoose.Schema.Types.ObjectId, default: null}
});
storySchema.methods.test = function()
{
console.log(foo.bar);
console.log(config.test);
console.log(models);
}
var Story = exports.model = mongoose.model('story', storySchema);
When I create a new Story in app.js and call its test() method, I get this output:
baz (so I know it's seeing objects in the same file)
test (so I know it's seeing variables in the config file)
{} (this "should" log my models object but it logs an empty object, why?)
When I create a new Story object within the story.js file, and run it (node ./models.story.js) the values returned are as expected (the models object is logged rather than an empty object).
Update, here are the index.js and app.js files:
index.js:
module.exports = {
post: require('./post'),
story: require('./story')
};
app.js:
var config = require('./config');
var models = require(config.models);
var story = new models.story.model();
story.test();

I believe the issue is that you've created a circular dependency. Story executes require(config.models) which requires Story again inside index.js.
Rather than storing a string and requireing it everywhere, try storing the models directly in config.models:
config.js
module.exports = {
test: 'test',
models: require(__dirname + '/models')
};

In case anyone runs into this same issue, I wanted to point to a couple resources I came across that helped me resolve the issue. As ssafejava pointed out, the problem does have to do with circular dependency (although ssafejava's solution did not entirely resolve the issue) . What worked for me was designing this dependency out of my application but there are other options if doing so is not possible. See the following issues' comments for a better explanation (in particular, see 'isaacs' comments):
https://github.com/joyent/node/issues/1490
https://github.com/joyent/node/issues/1418

Related

Node require - i don't understand this code

I'm starting to learn node.js and mongoose and I do not understand the following code.
I have 2 files: app.js and idea.js
app.js
const mongoose = require('mongoose')
require('./models/Idea')
const Idea = mongoose.model('ideas')
idea.js
const mongoose1 = require('mongoose')
const Schema = mongoose1.Schema
const IdeaSchema = new Schema(
{
title: { type:String, required:true},
details: {type:String, required:true},
date: {type:Date, default:Date.now()}
}
)
mongoose1.model('ideas', IdeaSchema)
When I run app.js, no error occurs. How is it possible? I did not export anything from the idea.js file!
How did the app.js file get access to the ideas model?
require is cached. So, this:
let obj1 = require('an_object');
let obj2 = require('an_object');
will load the object in the first line, and return that same object from cache in the second line. This holds even if the two requires are in different files. Since obj1 and obj2 are references to the same object, if obj1 gets modified, obj2 also gets modified (because they're one and the same).
You did not export anything from Idea, but it doesn't matter; the purpose of Idea was to modify the mongoose object, not to return anything.
Simplified:
// storage.js
exports.hello = 'Mumble mumble';
// mod_storage.js
let modding_storage = require('./storage');
modding_storage.hello = "Hello, world!";
// main.js
let main_storage = require('./storage');
require('./mod_storage');
console.log(main_storage.hello);
// => Hello, world!
mod_storage changed modding_storage, but modding_storage is the same object as main_storage; change one, both change.

Avoiding multiple instances of the Sequelize class

I'm trying to build out a basic database schema using Express and Sequelize. I define all of the models in separate files. I have a single file (models/index.js) where I create an instance of the Sequelize class, import the models, and establish the relationships among the models. I also have multiple controllers that each need to have access to the models exported from models/index.js.
Here's the file where the models are imported:
// models/index.js
var Sequelize = require('sequelize');
var sequelize = new Sequelize('myApp', 'username', 'password');
var User = sequelize.import('./users');
var Contact = sequelize.import('./contacts');
var Conversation = sequelize.import('./conversations');
var Medium = sequelize.import('./mediums');
User.hasMany(Contact);
Contact.belongsTo(User);
Contact.hasMany(Conversation);
Conversation.belongsTo(Contact);
Medium.hasMany(Conversation);
Conversation.belongsTo(Medium);
module.exports.Sequelize = Sequelize;
module.exports.sequelize = sequelize;
module.exports.User = User;
module.exports.Contact = Contact;
module.exports.Conversation = Conversation;
module.exports.Medium = Medium;
Here's one of the controllers that needs access to the models.
// controllers/users.js
var models = require('../models');
module.exports.addUser = function () {
};
module.exports.getUser = function () {
};
Here's another controller that needs access to the models.
// controllers/contacts.js
var models = require('../models');
module.exports.addContact = function () {
};
module.exports.getContact = function () {
};
module.exports.getAllContacts = function () {
};
My concern relates to the fact that both controllers require the models/index.js file. Each time the models/index.js file is required, a new instance of the Sequelize class is created, which establishes a new connection to the database. Does anybody have any suggestions to avoid multiple instances of the Sequelize class?
Thanks in advance!
Modules (files) are cached in node:
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo') will get exactly the same object returned, if it would resolve to the same file.
Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.
If you want to have a module execute code multiple times, then export a function, and call that function.
https://nodejs.org/api/modules.html#modules_caching
This means, that the code on models/index.js will only be run once

Node + Express + Bookshelf

I'm new to Node world. I want to build a Node+Express+Bookshelf (forget the front end for now). I'm following this tutorial.
But I want to refactor to what a Node structure looks like:
app/
controller/
models/
...
server.js
gruntFile.js
...
As I am still new to this world, I cannot figure out where the initialization goes,
Bookshelf.DB = Bookshelf.initialize({
client: 'postgres',
connection: {
....
}
});
And how to write my model inside the app/models/users.server.model.js would go. This is what I have:
'use strict';
var Bookshelf = require('bookshelf').DB;
exports.model = Bookshelf.Model.extend({
tableName: "users",
});
And finally, how do I use my model in some other file?
I just include the files with the model in the controller, there is this node lib that basically let you include an entire folder
include-folder
You should use Bookshelf.Registry plugin for that:
http://bookshelfjs.org/#Plugins-Registry
Then just require all your models and define them like this:
module.exports = bookshelf.model('MyModel', { ... });
To retrieve a model afterwards, do:
var MyModel = bookshelf.model('MyModel');

How to avoid using global?

I have very small nodejs app. Inside this application I define a model object in app.js like so:
global.model = {
name: 'Foobar'
};
The model is not persisted to any storage but kept in memory all the time. My requirement is, to be able to read and modify this model inside any module of my app.
I read that it is bad practice to use global. What is the better way? Through exports? Can you explain?
You can have a single module that creates and stores the model. Then, any other module that wants to get the model can require() your model module and then call a method on it to fetch the single shared model.
in model.js:
var mymodel = {
name: 'Foobar'
}
module.exports.getModel = function() { return mymodel;}
in any other module that wants to get the model:
var mymodel = require('./model').getModel();
If your model module would not generally be used for other things, then you could simplify it like this:
var mymodel = {
name: 'Foobar'
}
module.exports = function() { return mymodel;}
in any other module that wants to get the model:
var mymodel = require('./model')();

How to export properly in node.js?

I'm running some issues with my node application after upgrading to Express 3.0. So, since I'm rewriting it, I was trying to follow the style of routes/index.js because it looks clean.
So, in my main app.js I have some variables such as:
var calls = 0;
var agents = [];
And also use the node-mysql module. However, the routes definition doesn't have app.js scope but their own scope so calls and agents aren't visible.
How should I make them visible?
For mysql I tried something like:
// libraries/mysql.js
mysql = require('mysql');
var mysql_conf = {
host: myhost,
user: myuser,
password: mypass,
database: mydb
};
var mysql_client = mysql.createClient(mysql_conf);
exports.mysql_client;
//Later in routes/index.js
mysql_client = require('../helpers/mysql.js');
But it seems not to work as it says TypeError: Object #<Object> has no method 'query'
Any hints please?
The line
exports.mysql_client;
Does not actually assign anything to the mysql_client property of exports. It accesses a property, and immediately discards the value. You need to do something like this:
var mysql_client = mysql.createClient(mysql_conf);
exports.mysql_client = mysql_client;
// or simply
exports.mysql_client = mysql.createClient(mysql_conf);

Resources