load mongoose model from other controller in yeoman angular-fullstack project - node.js

I am writing an application using yeoman angular-fullstack,
I defined some entities that I would like to access from other entities controller.
For example I have in my server->api->Mall I have the following code:
var MallSchema = new Schema({
...
});
module.exports = mongoose.model('Mall', MallSchema);
in other Business controller I would like to search for Mall by _id, using mongoose, But I can not import MallSchema trying to do the following:
mall = mongoose.models("MallSchema").find({_id: "some_mall_id"})
business.mallType = mall.type
business.update();
or nesting Schema's
var MallSchema = new Schema({
business : Business;
});
I really tried in lot of ways, ending up with frustration, googling this yields lots of answers, none have works and few just seems too complicated like this one, I newbie to node.js so I mast been missing somthing simple. any help would be nice for the entire community.

it seems simple but somehow missed from documentation
this seems to work for me now
var business = require('../business/business.model')();
var user = require('../user/user.model')()
Hope it helps.

Related

I tried nestjs but I realized it reduces code readability because of so many decorators, please take a second to visit this

I recently used nestjs, But I realized its overcomplicated, I mean look the following code:
#post('/products')
getAllProducts(#Body('title') title, #Body('price') price, #Body('description') description) { }
It makes function parameters much dirty, Also there could be more decorators above function like #Header, #Params, etc.. Which reduces readability in my opinion.
Same code in nodejs
const { title: title, price: price, description: description } = req.body
nodejs is much more readable...
Then I researched why developers use nestjs, Reason was Modularity. Why we don't implement this on our own...
See below:
See my directory sutructure
In app.js I just kicked the app:
const express = require('express');
const app = express();
// express config
require('./startup/config')(app);
// handling routes
require('./startup/routes')(app);
// db setup
require('./startup/db')(app);
In startup folder I did the basic work like mongoose configuration and connection to db etc..
However, In startup/routes, I just kicked the module as follow:
const shopModule = require('../shop/shop.module');
module.exports = app => {
app.use('/', shopModule);
};
In shop module, I just kicked the routes as follow:
const router = require('express').Router();
const productsRouter = require('./products/index');
const cartRouter = require('./cart/index');
// Products
router.use('/products', productsRouter)
// Cart
router.use('/cart', cartRouter)
module.exports = router;
Now in cart/index.js, I handled the routes related to cart and same for products as follow (I will just show cart):
const router = require('express').Router();
const { getCart } = require('./cart.controller');
router.get('/', getCart);
module.exports = router;
In controller, basically we will do validation stuff etc or extracting data.. Then controller will kick service for database work..
const { userCart } = require('./cart.service');
exports.getCart = (req, res, next) => {
const cart = userCart();
return res.status(200).json(cart);
};
And finally in cart service:
exports.userCart = _ => {
// ... go to database and fetch cart
return [{ prodId: 123, quantity: 2 }];
};
And cart.model.js is responsible for DB schema,
I know the question was too long, but I wanted to explain my question.
I am not saying nestjs should not be used, I am just saying, what about the following structure as it follows the same pattern as angular or nestjs, Right?
With your first point about making the code more readable, why not do something like
#Post('/products')
getAllProducts(#Body() body: any) {}
instead of calling for each part of the body individually, then you can deconstruct the body as you showed with
const {title: title, price: price, description: description} = body;
No need to specify each part of the body that you need as a new parameter, just grab the object itself. The same also goes for #Header(), #Param(), and #Query().
As for how you are setting up your express app, you are completely correct that you can do that; however, if you are working on an open source project, or collaborating with other developers there is nothing that says they have to follow the same format and it could eventually lead to a messy code base. Nest enforces these patterns, similar to how Angular does. Sure, it is still possible to write terrible code, but with an opinionated architecture it does make it more difficult.
NestJS also treats Typescript as a first class citizen, which in my opinion helps get rid of a lot of development problems as well. Plus you get some really cool packages to play with like class-validator and class-transformer to help with validation and transformation of your requests via pipes. You can write an Express server in Typescript, but it isn't necessary, and you can write a NestJS server in JavaScript, but I think you lose a lot of good functionality if you do.
The last point that I don't think has been touched too much it that Nest handles a lot of black boxing for your code through it's modules. If you define a service for a module, it is only available for that module. If you need it in another module you have options, but it helps cut down on the cross-contamination of code and keeps a separations of concerns ideology in mind.
In my opinion, the fact the NestJS gives us specified files for things like authentication (guards), validation and transformation (pipes and interceptors), and error handling (exception filters) makes it easier for anyone to pick up a NestJS server and have a quick understanding of how the request will flow through the server, even if by just looking at the file structure. Personally I know if I see an AuthModule or a guards folder I'm going to be dealing with authentication to some extent.
To answer your final question: there is nothing wrong with how you are showing the express example, especially as you are using Inversion of Control by passing the app into the router to make it work that way, you can absolutely write an Express server that way. Nest just makes you do it that way. (I mean, you could write an entire server using just AppController, AppService and AppModule, but that's really an anti-pattern)
In the end, definitely use what you're comfortable with, but there is a reason Nest has become so popular recently.
it's dirty because you write it in the wrong way
why not use it like this
#Get('/')
getAllProducts(#Body() product: Product) {}
and then destructure it
const {title, price, description} = product

What is meaning of "mustacheExpress().cache = null;"?

now I`m implementation a simple application using Node.js, mustache and when using mustacheExpress(), some tutorial says to code like,
const mustacheExpressInstance = mustacheExpress();
mustacheExpressInstance.cache = null;
I have no idea what it means and what it is for.
nothing changed even marked this code out...
Documentation for Mustache Express
const mustacheExpressInstance = mustacheExpress();
This instantiates the mustache express into a variable
mustacheExpressInstance.cache = null;
This tells the object to not cache the file. I suggest you review the documentation and review Object Oriented concepts.

Best practices for mongoose with multiple identically structured databases

I have a Node.js project dealing with multiple customers. For several reasons, mainly separation of concerns, security, and partial transferability, I have to put every customer's data into a separate Mongo database. All databases store the same object types and thus have the same set of collections with identical structure. I have already found many technical solutions to solve that issue, but my question is more concerned with the best practices handling this situation. A simplified view into my code (omitting the boilerplate code to create a server, module structure, etc.):
const mongoose = require('mongoose');
// Customer codes act as DB names, actually comes from config file or DB
let customers = ['c1', 'c2'];
// keeps all mongoose connections
let conns = {};
// create a connection(pool) for each DB
for (let i in customers) {
let dbname = customers[i];
conns[dbname] = mongoose.createConnection(`mongodb://${host}:${port}/${dbname}`);
}
// a simple Schema for blog articles
const ArticleSchema = new mongoose.Schema({
title: String,
text: String
});
// keeps all Article models
let Article = {};
// create the identical models for the different DBs
// remember: the dbname is the unique customer's code
for (let dbname in conns) {
let conn = conns[dbname];
Article[dbname] = conn.model('Article', ArticleSchema);
}
const app = require('express');
// returns all articles of a certain customer
app.get('/customers/:customerid/articles', function(req, res) {
// get the customer's code from the URL
let dbname = req.params.customerid;
// Query the database
Article[dbname].find({}, function(err, articles) {
if (err) return res.status(500).send('DB error');
res.status(200).send(articles);
});
});
This code works. Nonetheless, I wonder, if there ist a best practice I am not aware of to handle this kind of requirement. Especially, it feels strange to keep the connections and models in maps and to access the objects with the dbname in square brackets. Please keep in mind that this is an extremely simplified version. In the real version the code is distributed across several modules handling different object types etc.
Note: A simple mongoose.connection.useDb(dbName) to switch to a different database doesn't work since the model has to be registered with respect to the connection, which itself must be bound to a database (as far as I understand it).
Update: Handling every customer with a single node process and setting up a proxy switching to those processes with respect to the customer - as suggested in the question mentioned by Paul - is currently not an option due to the administrative effort necessary to set things up in our current environment. We have to launch the service quickly right now.

Can't seem to display data from mongolab using node.js

i'm been trying to display just a simple data from mongolab to nodejs and it displays nothing for some reason.
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
mongoose.connect("mongodb://db-user:pass#ds049084.mongolab.com:49084/mydb");
mongoose.model('collection',{ name:String });
router.get('/', function(req, res, next) {
mongoose.model('collection').find({},function(err,docs){
res.send(docs);
});
});
I've already saved a name data in the document of the collection in mongolab, the only it shows when i run the debug script
debug=mynode:* npm start is []
does anybody know why is that? thank you.
Fixed it!, man..the problem is the way i named the collections at mongolab. I was missing the "s" at the end of collection. Once i deleted the collection and created another but this time the "s" at the end, then it worked.
e.g.
Naming a collection 'Car' will not work but naming it 'cars' will work somehow
I can't comment yet because of my rep...
But to elaborate.. The reason why you need the "s" at the end is because when you do:
mongoose.model('<the schema you want to map to in your db>',<yourschema>);
"Mongoose automatically looks for the plural version of your model name" in your db.. as it says in the Mongoose documentation.. hence if you type myDbSchema it would look for myDbSchemas (Note the "s") and so thats why in your mongolabs Db the schema would need the extra "s"... confused me for a bit to but I hope this can help someone! :) as this post did help me too.
FYI Mongoose documentation: Mongoose Models Doc

Routing in locomotive using ejs

I'm trying out node and some frameworks for node atm, specifically locomotive. However, i seem to be stuck on routing using locomotive. A couple questions i can't find the answer to, so here goes:
why does the locomotive out-of-box install use index.html.ejs as a
filename? Why not just index.ejs? What's the benefit?
i'm trying to add a route to a view: searchName.html.ejs which i
added in the views folder. To achieve this i made a toolController
like this:
var locomotive = require('locomotive').Controller,
toolController = new Controller();
toolController.searchName = function() {
this.render();
}
module.exports = toolController;
I also added a route in routes.js like so:
this.match('searchName', 'tool#searchName');
However, that doesn't work (and yet it's what the documentation says ought to work). The result is a 404 error. So how do i make that route work?
Suppose i want to make a route to eg, anExample.html? How do i go
about that? I notice that in the out-of-the-box app from
locomotive, you cannot enter localhost:3000/index.html . Nor even
localhost:3000/index This seems highly impractical to me, as there
are plenty of users who'll add the specific page they want to go to.
So how can i make that work?
PS: I went through all questions regarding this on stackoverflow and searched the web, but i still can't figure this out.enter code here
The benefit is that this naming scheme allows you to specify several different formats for a single route. So you could have search_name.html.ejs and search_name.xml.ejs, then respond with either view depending on what your client is expecting.
There are a couple issues with the example code you posted. You should be seeing a more descriptive error than a 404, so I'm not sure what's happening there, but here are the fixes to your code that work in my environment.
In the controller:
//tool_controller.js
var locomotive = require('locomotive');
var toolController = new locomotive.Controller();
toolController.searchName = function() {
this.render();
};
module.exports = toolController;
In routes.js:
//routes.js
module.exports = function routes()
{
this.match('searchName', 'tool#searchName');
}
Then, you'll need to change the view to this: views/tool/search_name.html.ejs. It's not clear from the documentation, but locomotive automatically lowercases and underscores actions that are camel-cased, like searchName.
Now start the app and browse to http://localhost:3000/searchName
If you just want to serve a static html file, the easiest way is to just drop it in the public folder. This folder is specifically for serving up static content like client-side js, css, etc. And it works just fine for serving static HTML as well.

Resources