This is not a concrete app/code question, it's just about common app architecture.
I'm trying to understand proper way to organize my mongoose application. As I'm new to mongoose, that's how I do it now:
core/settings.js
var mongoose = require('mongoose');
exports.mongoose = mongoose;
mongoose.connect('mongodb://localhost/blog');
exports.db = mongoose.connection;
core/models.js
settings = require("./settings");
// post schema
var postSchema = settings.mongoose.Schema({
header: String,
author: String,
text: String
})
//compiling our schema into a Model
exports.post = settings.mongoose.model('post', postSchema)
core/db-layer.js
settings = require("./core/settings");
models = require("./core/models");
exports.function = createAndWriteNewPost(function(callback) {
settings.db.on('error', console.error.bind(console, 'connection error:'));
settings.db.once('open', function callback() {
new models.post({
header: 'header',
author: "author",
text: "Hello"
}).save(function(err, post) {
callback('ok');
});
});
});
routes/post.js
db = reqiure("../core/db.js")
exports.get = function(req, res) {
db.createAndWriteNewPost(function(status){
res.render('add_material', {
//blah blah blah
});
});
};
app.js
var post = require ('routes/post.js')
...
app.get('/post', post.get);
So, this code was extremely simplified (even not tested) just to show my current architecture thoughts. It's not a concrete app, just something like creating an abstract blog post. So thats how it works:
app.js --> routes/post.js <--> core/db-layer.js
|
v
core/models.js <--> core/settings.js
It seems a bit over superfluous for me. Could you suggest more optimal app structure? Thanks.
When I first got into Node.js, Express and Mongoose I struggled with scaling my code.
The intention of my answer is to help someone who's working on more than just a simple blog, but to help with an even larger scalable project.
I am always connected to the database, I do not open and close connections when needed
I use index.js as the root file of a folder, just like we'd do in other languages
models are kept in their own documents, and require()d into the models/index.js file.
routes are similar to models, each route level has a folder, which has an index.js file in turn. So it's easy to arrange something like http://example.com/api/documents/:id. It also makes more sense when one goes through the file structure.
Here's the structure of what I use:
-- app.js
-- models/
---- index.js
---- blog.js
-- mongoose/
---- index.js
-- routes/
---- index.js
---- blog/index.js
-- public/
-- views/
---- index.{your layout engine} => I use Jade.lang
-- methods/
---- index.js => use if you'd rather write all your functions here
---- blog.js => can store more complex logic here
app.js
var db = require('./mongoose'),
express = require('express');
// note that I'm leaving out the other things like 'http' or 'path'
var app = express();
// get the routes
require('./routes')(app);
// I just require routes, without naming it as a var, & that I pass (app)
mongoose/index.js
// Mongoose connect is called once by the app.js & connection established
// No need to include it elsewhere
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/blog');
// I have just connected, and I'm not exporting anything from here
models/index.js
// Logic here is to keep a good reference of what's used
// models
Blog = require('./blog');
// User = require('./user');
// exports
exports.blogModel = Blog.blogModel;
// exports.userModel = User.userModel;
models/blog.js
So for every model that you work on you create a model.js document, and add it in the models/index.js above. As an example I've added a User model but commented it out.
// set up mongoose
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var BlogSchema = Schema({
header: {type: String },
author: {type: String },
text: {type: String },
_id: { type: ObjectId } // not necessary, showing use of ObjectId
});
Blog = mongoose.model('Blog', BlogSchema);
// the above is necessary as you might have embedded schemas which you don't export
exports.blogModel = Blog;
routes/index.js
module.exports = function(app) {
app.get('/', function(req, res) {
// do stuff
});
require('./blog')(app);
// other routes entered here as require(route)(app);
// we basically pass 'app' around to each route
}
routes/blog/index.js
module.exports = function(app) {
app.get('/blog', function(req, res) {
// do stuff
});
require('./nested')(app);
// this is for things like http://example.com/blog/nested
// you would follow the same logic as in 'routes/index.js' at a nested level
}
suggested use
models: for creating the logic that deals with the documents, i.e. creating, updating, deleting, and searching.
routes: minimal coding, only where I need to parse http data, create instances of models, and then I send queries to the relevant model.
methods: for the more complex logic that doesn't directly involve models. As an example, I have an algorithms/ folder where I store all the algorithms that I use in my app.
Hope this provides more clarity. This structure is working wonders for me as I find it easy to follow.
That's pretty much how I go about it, with a few differences:
I don't think you can have the open listener inside your function in the db-layer. What I generally do when using a persistent connection like yours is start the application itself in the db open handler. If you don't want to use persistent connections, use createConnection in the db layer function, and make sure you close it before calling the callback. I am not sure if I am making myself clear. Let me know if you want a code example.
This is more of a general node.js tip, but I keep my database connection string and other configuration in a json file and require it wherever it is needed. You probably won't need another settings.js file after that.
You can also use schema functions (http://mongoosejs.com/docs/api.html#schema_Schema-method) to code some app functionality into your models itself.
Related
Creating a small web app on the MEAN stack and I'm in the process of migrating my schemas to a separate "models" directory. When the schemas are defined in the same app.js file, everything works fine; however, when I switch the code to a separate more modular file and import it I get this error:
TypeError: Player.find is not a function
at /Users/username/code/express/WebApp/v3/app.js:57:12
This error occurs when it gets to that first route where it needs to look players up and I'm not quite sure what I'm missing after staring at this for hours.
My app.js file:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
Player = require("./models/players")
const port = 3000;
mongoose.connect("mongodb://localhost/players", { useNewUrlParser: true, useUnifiedTopology: true });
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({extended: true}));
// PLAYER SCHEMA ORIGNALLY DEFINED HERE BUT NOW ATTEMPTING TO MOVE TO DIFF DIRECTORY & IMPORT
/*var playerSchema = new mongoose.Schema({
player: String,
position: String,
description: String
});
var Player = mongoose.model("Player", playerSchema);*/
app.get("/", function(req, res) {
res.render("landing");
});
app.get("/players", function(req, res) {
// Get all players from DB
Player.find({}, function(err, allPlayers){
if(err){
console.log(err);
} else {
console.log("We're good.");
res.render("players", {players: allPlayers});
}
});
});
and my player.js file that I'm attempting to import:
var mongoose = require("mongoose");
var playerSchema = new mongoose.Schema({
player: String,
position: String,
description: String
});
// Compile into a model
module.exports = mongoose.model("Player", playerSchema);
The above schema definition and model definition work completely fine when they're in the app.js file, but not when imported. What am I missing here? Thanks in advance for the help.
I think your file name is wrong at the require statement. Its
const Player = require('../models/player')
as your file name is player.js, not players.js and if you stored the js file in a model folder. Do check out how to navigate using the file path
/ means go back to the root folder, then traverse forward/downward.
./ means begin in the folder we are currently in and traverse forward/downward
../ means go up one directory, then begin the traverse.
And also your backend should look like this.
Backend File Management
This has probably been asked before because of how high level the question is, however I couldn't find a solution and am struggling to get this set up. I am working on my first full stack web app using MERN stack. I am on a Mac. Everything I'm doing here is on my local machine.
For MongoDB, I have it installed on my local machine. I have the mongod dameom running. Here's what I have in my interactive mongo shell:
// run in terminal
> mongo
> show dbs
admin 0.000GB
config 0.000GB
mydboverhere 0.064GB
local 0.000GB
> use mydboverhere
switched to db mydboverhere
> show collections
table_one
table_two
andathirdtable
I would like to connect my node/express API to the mydboverhere database. In my node directory structure, I have a models directory with:
/models/index.js
var mongoose = require('mongoose');
mongoose.set('debug', true);
// is this line correct?
mongoose.connect('mongodb://localhost:27017/mydboverhere/table_one');
mongoose.Promise = Promise;
module.exports.Todo = require("./table1"); // this requires table1.js
and /models/table1.js
// this matches the form of the data in the database, I believe
var mongoose = require('mongoose');
var tab1Schema = new mongoose.Schema({
name: {
type: String,
required: 'cannot be blank'
},
completed: {
type: Boolean,
default: false
},
created_date: {
type: Date,
default: Date.now
}
})
var Table1 = mongoose.model('Table1', tab1Schema)
module.exports = Table1;
I believe I have my /routes/tableroutes file correct:
var express = require('express');
var router = express.Router();
var db = require('../models')
router.get('/', function(req, res){
// res.send("Hello from the table1 route")
db.Table1.find()
.then(function(data) {
res.json(data);
})
.catch(function(err) {
res.send(err);
})
});
module.exports = router;
and also I think I am loading these routes into my root /index.js file correctly as well:
var express = require('express')
var app = express();
var tableRoutes = require('./routes/tableroutes');
// test the root route
app.get('/', function(req, res){
res.send("Hello from the Root Route")
});
// set base route to /api for my tableRoutes
app.use('/api', tableRoutes);
app.listen(3000, () => console.log('Example app listening on port 3000!'))
Unfortunately, with mongod running, when I try to run node index.js to get my node app running, I receive the following error message in my command line:
... (node:66245) UnhandledPromiseRejectionWarning: Error: Unsupported host 'localhost:27017/mydboverhere/table_one', hosts must be URL encoded and contain at most one unencoded slash ...
And I'm stuck here right now... pretty much, i'm not sure if Im connecting my node API with mongodb correctly or not. This is all being done on my local machine, and I have mongodb installed at /data/db as it should be. Maybe the error is due to the underscore in the collection name table_one. Maybe the error is because the data in the table_one collection in mongo doesnt' match exactly with the schema in table1.js (I created the mongodb separately by pushing a dataframe from R into it, and then wrote the table1.js schema to match it).
Regardless of which of the following above issues is the issue, I'm not sure, and I'm struggling to continue. Any help here is greatly appreciated!
EDIT1: I have a strong feeling that the following line:
mongoose.connect('mongodb://localhost:27017/mydboverhere/table_one');
is incorrect, and am seeing the proper way to connect to a specific db.
EDIT2: I think there's another javascript library called mongoDB for this, but I would very much prefer to get this working with mongoose.
I think there is an error here:
You are using thisdboverhere whereas it should be mydboverhere.
mongoose.connect('mongodb://localhost:27017/mydboverhere', function(){
// do your process here
});
Or
mongoose.connect('mongodb://localhost:27017/mydboverhere');
var db = mongoose.connection // I think you are forgetting to instantiate the connection here
From this good github post here, I found the following:
Make sure you're connecting to the same database
(mongoose.connect('mongodb://hostname:27017/<db name here>'))
and accessing the same collection
(mongoose.model('<model name>', schema, '<collection name here>'))
I'm late to the party, but I had the same error message. Removing the underscore in the database name fixed it for me.
My original database name I tried was:
const URL_MONGODB = "mongodb://localhost:27017/portfolio_db";
I removed the underscore, and used this database name:
const URL_MONGODB = "mongodb://localhost:27017/portfoliodb";
After which, I no longer got the error "UnhandledPromiseRejectionWarning: Error: Unsupported host 'localhost:27017/data/db', hosts must be URL encoded and contain at most one unencoded slash"
I am trying to build a search module using mongodb indexing but not sure how to use it on the collection created using mongoose
i.e,
db.books.createIndex({"Title":"text"}) this works fine in mongo shell
where books is a collection which is inside database called bookish
but how to use it in nodejs application where i have few apis calls defined
var express = require('express');
var mongoose = require('mongoose');
router = express.Router();
var cors = require('cors');
module.exports = function (app) {
app.use(router);
};
/*+++++++++++++++++++++++++++++++++++++++++ Defining Model Starts++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
Books = mongoose.model('Books');
/*+++++++++++++++++++++++++++++++++++++++++ Defining Model Stops++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/*+++++++++++++++++++++++++++++++++++++++++ Router Starts++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
router.route('/api/book/')
.get(function(req,res){
Books.find({},function(err,books){
console.log( req.params.id);
res.send(books);
})
});
router.route('/api/delete/book/:id')
.post(function(req,res){
console.log("delete id"+req.params.id)
Books.findOne({bookId: req.params.id}, function(err,updateRemoveBook) {
updateRemoveBook.isDeleteBook= 0;
updateRemoveBook.save();
res.send(updateRemoveBook);
});
});
router.route('/cerateIndex')
Books.createIndex({"subject":"text","content":"text"})
in this file how to create index on this book ie here books is a refrence of model but create Index should be called on the collection right
i am not able to do like
db.Books.createIndex({"subject":"text","content":"text"})
i am referring this article to perform indexing
http://code.tutsplus.com/tutorials/full-text-search-in-mongodb--cms-24835
not sure how to use it with nodejs application please provide suggestion to how to go about it point some sample which can demonstrate the same
You can create the text indexes using the model schema's index() method:
bookSchema = new mongoose.Schema({"subject": String, "content": String},{"collection": "books"});
bookSchema.index({ "subject": "text", "content": "text" });
Books = mongoose.model('Books', bookSchema);
I'm developing my first Node.js App with Socket.IO and everything is fine but now the app is slowly getting bigger and I'd like to divide the app-code into different files for better maintenance.
For example I'm defining all my mongoose schemas and the routings in the main file. Underneath are all the functions for the socket.IO connection. But now I want to have an extra file for the schemas, an extra file for routing and one for the functions.
Of course, I'm aware of the possibility to write my own module or load a file with require. That just does not make sense for me, because I can't work with the vars like app, io or db without making them global. And if I pass them to a function in my module, I can't change them. What am I missing? I'd like to see an example how this is done in practice without using global vars..
It sounds like you have a highly coupled application; it's difficult for you to split out your code into modules because pieces of the application that should not depend on each other do. Looking into the principles of OO design may help out here.
For example, if you were to split your dataabse logic out of the main application, you should be able to do so, as the database logic should not depend on app or io--it should be able to work on its own, and you require it into other pieces of your application to use it.
Here's a fairly basic example--it's more pseudocode than actual code, as the point is to demonstrate modularity by example, not to write a working application. It's also only one of many, many ways you may decide to structure your application.
// =============================
// db.js
var mongoose = require('mongoose');
mongoose.connect(/* ... */);
module.exports = {
User: require('./models/user');
OtherModel: require('./models/other_model');
};
// =============================
// models/user.js (similar for models/other_model.js)
var mongoose = require('mongoose');
var User = new mongoose.Schema({ /* ... */ });
module.exports = mongoose.model('User', User);
// =============================
// routes.js
var db = require('./db');
var User = db.User;
var OtherModel = db.OtherModel;
// This module exports a function, which we call call with
// our Express application and Socket.IO server as arguments
// so that we can access them if we need them.
module.exports = function(app, io) {
app.get('/', function(req, res) {
// home page logic ...
});
app.post('/users/:id', function(req, res) {
User.create(/* ... */);
});
};
// =============================
// realtime.js
var db = require('./db');
var OtherModel = db.OtherModel;
module.exports = function(io) {
io.sockets.on('connection', function(socket) {
socket.on('someEvent', function() {
OtherModel.find(/* ... */);
});
});
};
// =============================
// application.js
var express = require('express');
var sio = require('socket.io');
var routes = require('./routes');
var realtime = require('./realtime');
var app = express();
var server = http.createServer(app);
var io = sio.listen(server);
// all your app.use() and app.configure() here...
// Load in the routes by calling the function we
// exported in routes.js
routes(app, io);
// Similarly with our realtime module.
realtime(io);
server.listen(8080);
This was all written off the top of my head with minimal checking of the documentation for various APIs, but I hope it plants the seeds of how you might go about extracting modules from your application.
I'm developing my first Node.js App with Socket.IO and everything is fine but now the app is slowly getting bigger and I'd like to divide the app-code into different files for better maintenance.
For example I'm defining all my mongoose schemas and the routings in the main file. Underneath are all the functions for the socket.IO connection. But now I want to have an extra file for the schemas, an extra file for routing and one for the functions.
Of course, I'm aware of the possibility to write my own module or load a file with require. That just does not make sense for me, because I can't work with the vars like app, io or db without making them global. And if I pass them to a function in my module, I can't change them. What am I missing? I'd like to see an example how this is done in practice without using global vars..
It sounds like you have a highly coupled application; it's difficult for you to split out your code into modules because pieces of the application that should not depend on each other do. Looking into the principles of OO design may help out here.
For example, if you were to split your dataabse logic out of the main application, you should be able to do so, as the database logic should not depend on app or io--it should be able to work on its own, and you require it into other pieces of your application to use it.
Here's a fairly basic example--it's more pseudocode than actual code, as the point is to demonstrate modularity by example, not to write a working application. It's also only one of many, many ways you may decide to structure your application.
// =============================
// db.js
var mongoose = require('mongoose');
mongoose.connect(/* ... */);
module.exports = {
User: require('./models/user');
OtherModel: require('./models/other_model');
};
// =============================
// models/user.js (similar for models/other_model.js)
var mongoose = require('mongoose');
var User = new mongoose.Schema({ /* ... */ });
module.exports = mongoose.model('User', User);
// =============================
// routes.js
var db = require('./db');
var User = db.User;
var OtherModel = db.OtherModel;
// This module exports a function, which we call call with
// our Express application and Socket.IO server as arguments
// so that we can access them if we need them.
module.exports = function(app, io) {
app.get('/', function(req, res) {
// home page logic ...
});
app.post('/users/:id', function(req, res) {
User.create(/* ... */);
});
};
// =============================
// realtime.js
var db = require('./db');
var OtherModel = db.OtherModel;
module.exports = function(io) {
io.sockets.on('connection', function(socket) {
socket.on('someEvent', function() {
OtherModel.find(/* ... */);
});
});
};
// =============================
// application.js
var express = require('express');
var sio = require('socket.io');
var routes = require('./routes');
var realtime = require('./realtime');
var app = express();
var server = http.createServer(app);
var io = sio.listen(server);
// all your app.use() and app.configure() here...
// Load in the routes by calling the function we
// exported in routes.js
routes(app, io);
// Similarly with our realtime module.
realtime(io);
server.listen(8080);
This was all written off the top of my head with minimal checking of the documentation for various APIs, but I hope it plants the seeds of how you might go about extracting modules from your application.