How to avoid fat models in a node.js + mongoose app? - node.js

The app is using express 3. Here is a barebones example of a route that fetches data from the database:
var Post = mongoose.model('Post')
app.get('post/:id/loompas', function(req, res) {
Post.getLoompas(function(err, data){
res.render('x', data)
})
})
Where Posts.getSomeData is defined as instance methods in /models/post.js, and sometimes accesses external APIs:
PostSchema.method('getLoompas', function(callback){
var post = this
API.get('y', function(x){
this.save(x)
callback(x)
})
})
This is starting to smell, and doesn't look like it belongs along the Schema definition. The collection of methods could grow quite large.
What design patterns are recommended to separate these concerns and avoid extremely fat models? A service layer for external API calls? Any interesting solutions out there?

This does indeed smell a little bit.
I would use the approach of considering your web app merely as a view of your application.
The best way to ensure this is to never use your mongoose models from your webapp. You could have your webapp living in a process and your model specific logic in another process. The job of that second process would be to take care of your business logic and persistence layer (mongoDB), making it the M in MVC.
Accessing external APIs would take place in that Model layer, we your can separate it from your persistence implementation.
There's a way of communicating between node processes that I like, it's dnode. Once set up, it looks like you are communicating with objects and callbacks within your own process. I would make the webapp and the business app communicating through this in order to get data. The webapp needn't manipulate the actual data and instead sends message to the Model layer (as described by the MVC pattern).
This ensures complete separation between controller/view (webapp) and model+persistence.
One side effect of this organization is that you can easily write other clients of your application, for example a CLI client or a RESTful API.

Are you trying to get id and somedata from url (post/:id/:somedata) ? to construct schema ?
Ideally one should use :
app.post('/reg', function(request, response){
console.log(request.body.name);
console.log(request.body.email);
...
}
which is when form is submitted on the 'reg' HTML form page, where you can set all the variables(name,email) in object. In app.post you can get the schema definition from the request itself without having to scan through the url to get variables.
If you still want to know how to get the variables from the url then do this in app.get:
vars=request.url.split('/');
//vars contains all the variables you have to use.
//use vars to create schema
After you get/create the schema directly pass it to the function / or iterate through the object elements calling that function.

Related

Efficient way to use api request header in controller, models in Node Express Js

I am pretty much new to Node JS. We do have a requirement to use the request header from router to model class.
Let us assume an simple router
router.ts
router.delete(
'/sample/:id',
validateRequest(),
async function (req: Request, res: Response, next: NextFunction) {
try {
const solution: string = req.header('Some Header Value') || '';
await Controller.someMethods(req.params.id, solution);
return res.json(new HttpResponse('SUCCESS', {}, {}));
} catch (err) {
return next(err);
}
},
);
This is our router, Here we should be able to read "solution" in controller, service, and model classes. Right now we have passed this as an argument to different components. Is there any better approach to read the header value which is in the current request scope?
Similarly using components in spring framework, or sesison management or any other better approach other than passing the header value as an argument at each component level.
regards
Eresh
TL; DR; There is no other way than just mapping it manually.
Express is quite minimalistic so we don't such abstractions as Spring, the truth is that Node.js is different from Java. In Java we spawn a thread per request, so every request has a single thread. Whereas Node.js is async and single-threaded, so multiple requests share the same thread so you need to pass the values down your calls because there is no out-of-the-box solution on storing global values for a request.
If you want to have an access to the headers somewhere inside an application you could build a system that does so. The first step is to add the middleware that stores the headers in service with a unique ID associated with it and somehow passes this ID down the road. Then whenever you know the ID you could call the service to get the data for you, though you still will need to pass the ID down the road. I think you should not care about that, and that is okay. I would refactor your code in a way that a Controller methods access req and res then all the logic of working with these objects will be incapsulated here, whereas the service layer will expect raw data that know nothing about the transport layer that controllers operate with. Thus you can call services from another service, because they know nothing about the request and response.
FWIW, if you need a better framework use Nest.js, it is great and advanced, also it uses decorators (in a way similar to annotations in Spring). For instance you could just inject the header value as a call argument for your method in the following way #Header("some-header") solution: string
Best regards.

Sails.js: best way to package DB and surrounding API models as module

I'm new to Sails, but have used Express, and am considering Sails for my upcoming project. I particularly like that it makes the CRUD API for me and connects Socket.io automatically.
The next application I'm planning to work on has an indeterminate size; if it works well, we want to separate our CRUD/JSON API from our Web/HTTP server and load balance the Web/HTTP server. This would allow us to utilize the CRUD/JSON API in other adjacent applications, like code for statistical analysis, or external data parsers which import data, or other things which have nothing to do with Web/HTTP servicing.
In express I would consider making the API section a module then export the express.router with all the api calls like so
//appAPI.js
var routes = require('express').router();
routes.get('/user/:id', function(request, reply){
// assume db is connected database object
// and request.params.id is as expected
db.getUser(request.params.id, function(u){
reply.json(u);
});
})
module.exports = routes;
//app.js
var app = require('express')(),
api = require('appAPI');
app.use('/api', api);
Then in my application, if I want to separate the Web from the API, I can package up the appAPI.js, and associated model code, and make a small connector to redirect all routes /api/* to the ip address and port of the api server, or other possibilities.
Can I do something like this in Sails? It seems that the automated model creation and the socket.io automation would make this difficult. Alternatively, I might be able to make a module for the API with a whole sails server then embed it in the main Web/HTTP server, which has its own sails objects running, but this seems like it either would not work, break the socket.io connections, or work, but be horribly inefficient as it would have multiple instances of sails running.
Any recommendations would be helpful and I'm willing to consider alternative ways of working this. Thank you all for any help you might provide and have a wonderful day.

Want to get crystal clear about NodeJS app structure (Full JavaScript Stack) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I would like to know the structure of a typical NodeJS app, because the more I read and see the projects, the more confused I am, specifically for questions like these (or even more after I updated this question):
Take the MEAN stack for example, from what I know, NodeJS and Express take care of the server part, providing the server interface, etc. MongoDB and Angular are pretty straightforward.
But where should the business logic go? Say if I have a controller.js which contains a function, and the route.js file binds the request with this controller function. My question is: under which module these files belong to/run under (Express or NodeJS?)
Where is the starting point of a NodeJS app? Say index.php is the starting point of a PHP app, but where is it for NodeJS app? I can see that all Nodejs projects have a file called server.js or app.js, etc.(containing something like module.exports = app;) But how can NodeJS know which file to find and execute?
I am a fresh noob on NodeJS, Express, sequelize.js/Mongoose, Jade/EJS but want to get started on a NodeJS project. Could you please elaborate on the actual function that each modules provide and a general introduction of the typical structure for a full JS stacked NodeJS app? Thanks in advance!
Alright, this is a pretty broad question and I'm definitely no expert, but I'll do my best here.
TL;DR
routes are controllers that tell what logic to execute when a user navigates their browser to a certain path within your app, including which views to render and what data to send to those views
models are just that - data models within your application
module.exports = tells a file what exactly it "exports", that is what code needs to be executed or accessible from your main app file.
require(..) includes a module. You can set this on a variable so that you may call module functions later, or simply execute a function if that is all that module.exports returns.
Combining these techniques can help you nail down a solid framework for any of your applications.
Long Answer
Express provides a solid framework for structuring your Node.js application. Node is completely independent of Express, but because of how popular Express is they practically go hand-in-hand. Once installed, Express can be used to generate a scaffold web project (with options) for you to build on top of if you'd like.
Controllers
A generated project will create /routes/index.js, which (if you understand MVC) is essentially your main controller. A route in express is written as so:
app.get('/path', function(req, res, next){ .. } );
Lets break that down: our application variable (app) is being told that on a GET request to '/path' to execute an anonymous callback function with req, res, next variables (request, response, callback respectively). I find it helpful to think of this like a custom event handler.
Its important to note at this point that we could also call app.post with the same syntax for posts to a URL as opposed to gets.
Within our anonymous callback, we handle any incoming data and render a view for the user. This is where most of my business logic ends up, so it actually makes sense to NOT use anonymous functions here. Here's an example of a basic callback that just displays a homepage:
app.get('/', function(req, res, next){
//some business logic
res.render('views/home');
});
When the user tries to GET the index path of our application (/), we simply render our home view that, from the root of our project, is stored in a views folder.
But what if we want to modularize this so that we aren't declaring all of our routes in our main app.js or server.js?
We use module.exports = .. in our modules to tell our server what exactly to include. In my controller, I export a single function that takes the application as an argument and uses that to define our routes like so:
Controllers/User.js
module.exports = function(app){
app.get('/users', function(req, res){
var users = req.db.collection('users').find();
if (!users) {
console.log("no users found");
res.redirect('/');
} else {
res.render('users/index', {users : users});
}
});
};
Don't worry about the req.db code, I attach the database to the request in my application but that isn't done by default. Simply understand that I'm getting a list of 'users' here, and redirecting the user to the index of my app if there aren't any.
Models
Mongoose provides us with a great interface for writing models. With mongoose, writing models is a three step process:
Define a schema
Define model logic
Generate and export the model
Here is an example of a User model:
Models/User.js
var mongoose = require('mongoose'),
userSchema = new mongoose.Schema({
name: { type: String, required: true },
joinDate: {type: Date, default: date.now }
}),
User = mongoose.model('user', userSchema);
module.exports = user;
Server App
module.exports is used to help us define some modularity to our codebase. When we run a node application, we're ultimately running a single JavaScript file (you've already seen that file with server.js or app.js).
To keep this file from getting too big with multiple models and routes, we use require(module) to include code from other JS files. module in our case would be a path to the module we want to require. If you have the following doc structure:
| Controllers
- User.js
| Models
- User.js
| Views
app.js
To include your user controller from app.js, you would write: require('./Controllers/User'). Since our controller modules simply export functions, we can call that function immediately after our require statement by simply adding parentheses at the end (with whatever parameters are required). Including my controllers looks like so:
require('./Controllers/User')(app)
I'm passing in the actual app, because my module (below) simply exports a function that adds business logic to my app's routes. This only needs to be called and never used, so I don't capture my controller as a variable to call methods on later.
Including models is a little different, since we may want to perform some operation that our model defines. We can do this by changing up our require code just a bit:
var User = require('./Models/User');
Now we can call methods of our User model whenever. Mongoose gives us a lot of base functionality for free:
User.find({}, function(err, users){ .. });
The above function will go find all of our users, and then execute an anonymous function with a potential err (is null if no issues) and then a list of our users in JSON format. Pretty nifty.
Combining all of these concepts is how you create a basic web application using Express and Node.js. Please let me know in the comments if there's anything I can clarify about how I use Express. This is very surface level knowledge, and I suggest digging into documentation and looking at plugins to extend the capabilities of your apps. Good luck!

where to code business logic in nodejs with expressjs, mongoosejs and redis

My business logic includes mongodb operations and redis operations on one request. I do not know where we should put logic code to. In Java project, we have DAO, service and controler objects. but in nodejs projects, I don't know where to put my code. shall I put logic code to routes/index.js?
app.post('/deal', function(req, res) {
... //TODO: here
});
or create a kind of service objects such as what we do in Java proejct?
Here's a question that might help: Mongoose-based app architecture
You should also look at the http://mean.io stack, as their templates will show you how to best structure your app, including where to store your logic.

Allowing multiple routes for a single Backbone Model

I am fairly new to Backbone and am creating some basic API for a site. However, I came across a problem that I have yet to find a solution to.
On my front end I have a Backbone Model called Item that has a urlRoot: "/item". Now this urlRoot is used by Backbone to send different HTTP requests to the server correct? So if my backbone model uses Item.fetch() it will send a GET request, and a Item.save() may send a POST request.
My backend then has a bunch of listener functions to handle different cases like "/createItem", "/updateItem", "deleteItem", ect. Can all of these be handled using the basic urlRoot that is provided? Or do I have to specific what route to emit explicitly?
If you want to follow the default way of doing it, your backend should not use different names for each of the CRUD operations. It should use the url you specified using rootUrl + /id of the model, and should handle an HTTP POST, GET, PUT, or, DELETE for that single URL (with the exception that the POST URL has no /id attached).
See: http://backbonejs.org/#Sync
Since you are using an unconventional set of rest endpoints you will need to provide a custom sync method for your model:
sync : function(method, model, options) {
if (method === 'read') {
this.baseUrl = '/item';
return Backbone.sync.apply(this, arguments);
} ...
}

Resources