Node.js Express routing with MongoDB - node.js

Good afternoon,
I recently started working with Node.js + Express + MongoDB.
I set up a simple app containing :
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, mongoose = require('mongoose')
, models = require('./models')
, Document
, db;
// lots of conf ...
models.defineModels(mongoose, function() {
app.Document = Document = mongoose.model('Document');
db = mongoose.connect(app.set('db-uri'));
})
// Routes
app.get('/', routes.home);
app.get('/documents.:format?', routes.list);
// classical end of app.js
I also have a corresponding 'index.js' file within a 'routes' folder, containing:
exports.home = function(req, res){
res.render('index', { title: 'Indx' })
};
exports.list = function(req, res){
Document.find().all(function(documents) {
switch (req.params.format) {
case 'json':
res.send(documents.map(function(d) {
return d.__doc;
}));
break;
default:
res.render('index', { title: 'Indx' });
}
});
};
The routing part is OK, meaning that when I point my browser to localhost:3000, I see the (Jade-template generated) 'index' view. When I point to localhost:3000/documents, the routing works OK, and the code is trying to serve the 'list' part of my 'index.js' route. However, I was hoping that the 'Document' mongoose model I created in the main app would be recognized within 'index.js', but this is clearly not the case as I keep getting the following error :
Express
500 ReferenceError: Document is not defined
at C:\PERSO\DEV\indxjs\routes\index.js:23:2
at callbacks (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:272:11)
at param (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:246:11)
at param (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:243:11)
at pass (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:253:5)
at Router._dispatch (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:280:4)
at Object.handle (C:\PERSO\DEV\indxjs\node_modules\express\lib\router\index.js:45:10)
at next (C:\PERSO\DEV\indxjs\node_modules\express\node_modules\connect\lib\http.js:204:15)
at Object.methodOverride [as handle] (C:\PERSO\DEV\indxjs\node_modules\express\node_modules\connect\lib\middleware\methodOverride.js:35:5)
at next (C:\PERSO\DEV\indxjs\node_modules\express\node_modules\connect\lib\http.js:204:15)
I could obviously define my routing from within the 'app.js' with something like :
app.get('/documents.:format?', loadUser, function(req, res) {
// ...
}
But can anyone see a way of talking with mongoose while retaining the elegant './routes/index.js' separation from the 'app.js' ?
Thanks a lot
EDIT : following kind answer from Wes, I added the following code to 'index.js':
var Document;
function defineRoutes(mongoose, fn) {
Document = mongoose.model('Document');
fn();
}
exports.defineRoutes = defineRoutes;
// then the same as in initial post
And I enclosed the routing definitions within this function in 'app.js' :
routes.defineRoutes(mongoose, function() {
app.get('/', routes.home);
app.get('/documents.:format?', routes.list);
})
Everything is ok when I point to localhost:3000, but when I point to /documents, the browser keeps loading, loading ...

Thanks to Wes Johnson, I found my way out. I was blindly following a dated tutorial, which used deprecated methods from MongoDb. Below is the code snippet I finally used to implement the 'list documents' feature:
exports.list = function(req, res){
Document.find({},function(err, documents) {
switch (req.params.format) {
case 'json':
res.send(documents.map(function(d) {
return d.toObject();
}));
break;
default:
res.render('index', { title: 'Indx' });
}
});
};
Once again, thanks Wes !

Node modules are self-contained in terms of variable scope. Your Document object is not accessible from index.js as you mention. You need to pass this along to your routes logic, or pass along mongoose itself.
require calls are cached for the most part, so requiring mongoose in your index.js file and getting an instance of Document there is one option.
Since you don't really use Document in your app.js, you could always move the db configuration to another module and export the important references back to the scripts that need them.

Related

Getting a 404 on a route alias

I'm new to Node and angular and trying to figure out why my app is throwing a 404.
Here is the main error that I see in Firebug:
Cannot GET /api/user
In my server.js I make a call to the routes:
var express = require('express');
var app = express();
require('./routes')(app)
Within the routes folder I have index.js (which looks like it is called with the above require).
module.exports = function(app) {
app.use(restUtils.buildUrl('/api/user'), require('./user-api'));
}
restUtils just returns: '/api/user' and user-api is just another route within that same routes folder.
I can place a console.log right within the module.exports so I can see its making it there on server startup. It appears like it is generating the alias to api/user. However, when the app goes to find api/user it returns the 404.
Here is a snippet from user-api.js:
var router = require('express').Router(),
config = require('../conf/config'),
logger = require('../lib/logger'),
restUtils = require('../lib/rest-utils');
console.log("Inside User-Api");
router.get('/', function(req,res) {
console.log("Inside user-api - router.get");
if (config.debug && config.secured) logger.info('USER: ', req.user)
var user = {
id: req.user ? req.user.id : '',
displayName: req.user ? req.user.displayname : 'Unknown User',
roles: req.user ? req.user.appRoles : [],
groups: req.user ? req.user.appGroups : [],
profile: req.user ? req.user.profile : {}
};
res.format({
json: function() {
res.send(user);
}
});
});
module.exports = router;
I see the first console.log on server startup but it never falls into router.get.
Please let me know if you think I should show more code at different spots - just not sure what is needed to try and figure out this 404! Maybe the user-api route is failing to load when it should be calling the router.get? I think I just don't know enough about routing and modules just yet.
Thanks!!
The problem exist at
restUtils.buildUrl('/api/user')
which is returning a string 'api/user', it needs to be '/api/user'
The error says:
Cannot GET /api/user
So it tries to get a route which doesn't exists. You could add a get route for /api/user, that would fix the error I guess.
app.get("/api/user",function(req,res){})
I hope thats may help you

Easiest way to pass variables to routes templates in Express?

I've just made an Node.js app modular by splitting up data models and routes into separate files.
My routes are exported by express.Router(). In these routes I would like to import queried values from my app.js to be rendered with the templates.
How would I in the easiest way save things lets say with app.locals or req.variableName?
Since the route using express.Router() ties it together with app.js, should I be using app.params() and somehow make these values accessible?
Using globals seems like a worse idea as I'm scaling up the app. I'm not sure if best practice would be saving values to the process environment either using app.locals.valueKey = key.someValue...
Big thanks in advance to anyone
If I understand the question correctly, you want to pass a value to a later middleware:
app.js:
// Let's say it's like this in this example
var express = require('express');
var app = express();
app.use(function (req, res, next) {
var user = User.findOne({ email: 'someValue' }, function (err, user) {
// Returning a document with the keys I'm interested in
req.user = { key1: value1, key2: value2... }; // add the user to the request object
next(); // tell express to execute the next middleware
});
});
// Here I include the route
require('./routes/public.js')(app); // I would recommend passing in the app object
/routes/public.js:
module.export = function(app) {
app.get('/', function(req, res) {
// Serving Home Page (where I want to pass in the values)
router.get('/', function (req, res) {
// Passing in the values for Swig to render
var user = req.user; // this is the object you set in the earlier middleware (in app.js)
res.render('index.html', { pagename: user.key2, ... });
});
});
});

Issue importing the connect-roles object with require

I am building an app with nodejs and expressjs. For authentication and user roles I am using respectively passport and connect-roles.
I have built the connect-roles object as shown in here: https://github.com/ForbesLindesay/connect-roles
Like so:
var user = new ConnectRoles()
This is in my app.js. Then I have exported such object like so:
exports.user = user;
However, if I import this object with require I see a strange behavior, please check code below:
var express = require('express');
var router = express.Router();
var user = require('../app.js');
//1
console.log(user);
/* GET users listing. */
router.get('/', function(req, res) {
//2
console.log(user);
res.send('respond with a resource');
});
module.exports = router;
The object is undefined in case 1 and is as it should be in case 2. Basically, I get 2 different objects depending if I am inside or outside the router.get function. I have been debugging this for half day, but I can't figure out what is happening.
The issue is that this object should be injected to provide roles management like so:
router.get('/', user.is("admin"), function(req, res) {
Of course this gives an error since user outside the get function object is undefined. The error is "cannot call method is of undefined".
The problem you have is most likely a cyclic dependency. Your router file requires your app.js file and your app.js file requires your router. What this means is that your router file gets a partially initialised object, that is only later filled in. The best way around this is to factor out the roles into a separate module, then require it from both places. i.e. have an authorisation.js file that looks like:
var ConnectRoles = require('connect-roles');
var user = new ConnectRoles();
// set up all the authorisation rules here
module.exports = user;
Then in both app.js and the router file, do var user = require('./authorisation.js');
This is a general problem whenever you create cyclic dependencies, and is not specific to connect-roles.
I can't see how user would be undefined there... but I can suggest that you use
module.exports = user
As this will give you the object:
{ functionList: [],
failureHandler: [Function: defaultFailureHandler],
async: false,
userProperty: 'user' }
Rather than:
{ user:
{ functionList: [],
failureHandler: [Function: defaultFailureHandler],
async: false,
userProperty: 'user' } }
With your current implementation you could do:
router.get('/', user.user.is("admin"), function(req, res) {
If this does not solve your issue you may need to provide app.js in it's entirety.

Express Routes in Parse Cloud Code Module

I am using parse.com cloud code with express to setup my routes. I have done this in the past with node, and I have my routes in separate files. So, in node I do
app.js
express = require("express");
app = exports.app = express();
require("./routes/js/account");
account.js
app = module.parent.exports.app;
app.get("/api/account/twitter", passport.authenticate("twitter"));
All the examples on parses site https://parse.com/docs/cloud_code_guide#webapp show this being done as follows.
app.js
var express = require('express');
var app = express();
app.get('/hello', function(req, res) {
res.render('hello', { message: 'Congrats, you just set up your app!' });
});
So, I would like to change the bottom to include a routes folder with separate routes files, but am not sure how to do this in parse.
I know this post is a little old, but I just wanted to post a solution for anyone still looking to get this to work.
What you need to do, is create your route file, I keep them in 'routes' forlder, for example <my_app_dir>/cloud/routes/user.js
Inside user.js you will have something that looks like this:
module.exports = function(app) {
app.get("/users/login", function(req, res) {
.. do your custom logic here ..
});
app.get("/users/logout", function(req, res) {
.. do your custom logic here ..
});
}
Then, in app.js you just include your file, but remember that you need to append cloud to the path, and pass the reference to your app instance:
require('cloud/routes/user')(app);
Also, remember that express evaluates routes in order, so you should take that into consideration when importing several route files.
I'm using a different method, have the routes in app.js, but you can probably include them in file if you prefer. Take a look at the example app,
anyblog on github
The way it works:
Set up a controller:
// Controller code in separate files.
var postsController = require('cloud/controllers/posts.js');
Add the controller route
// Show all posts on homepage
app.get('/', postsController.index);
// RESTful routes for the blog post object.
app.get('/posts', postsController.index);
app.get('/posts/new', postsController.new);
And then in posts.js, you can use exports, ex.
var Post = Parse.Object.extend('Post');
// Display all posts.
exports.index = function(req, res) {
var query = new Parse.Query(Post);
query.descending('createdAt');
query.find().then(function(results) {
res.render('posts/index', {
posts: results
});
},
function() {
res.send(500, 'Failed loading posts');
});
};
// Display a form for creating a new post.
exports.new = function(req, res) {
res.render('posts/new', {});
};
Pass the app reference to the post controller, and add the routes from there

Using routes in Express-js

So I'm starting to use Node.js. I saw the video with Ryan Dahl on Nodejs.org and heard he recommended Express-js for websites.
I downloaded the latest version of Express, and began to code. I have a fully fledged static view up on /, but as soon as I try sending parameters, I get errors like this:
Cannot GET /wiki
I tried following the guide on expressjs.com but the way one uses routes has changed in the latest version, which makes the guide unusable.
Guide:
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
Generated by Express:
app.get('/', routes.index);
My problem arises when I try and add another route.
app.get('/wiki', routes.wiki_show);
I've tried a bunch of approaches, but I keep getting the Cannot GET /wiki (404) error.
routes/index.js looks like this:
exports.index = function(req, res) {
res.render('index', { title: 'Test', articles: articles, current_article: current_article, sections: sections })
};
The only thing I did there was add some parameters (arrays in the same file) and this i working. But when I copy the contents and change exports.index to exports.wiki or exports.wiki_show I still get the Cannot GET /wiki error.
Can anyone explain to me what I'm missing here? - Thanks.
So, after I created my question, I got this related list on the right with a similar issue: Organize routes in Node.js.
The answer in that post linked to the Express repo on GitHub and suggests to look at the 'route-separation' example.
This helped me change my code, and I now have it working. - Thanks for your comments.
My implementation ended up looking like this;
I require my routes in the app.js:
var express = require('express')
, site = require('./site')
, wiki = require('./wiki');
And I add my routes like this:
app.get('/', site.index);
app.get('/wiki/:id', wiki.show);
app.get('/wiki/:id/edit', wiki.edit);
I have two files called wiki.js and site.js in the root of my app, containing this:
exports.edit = function(req, res) {
var wiki_entry = req.params.id;
res.render('wiki/edit', {
title: 'Editing Wiki',
wiki: wiki_entry
})
}
The route-map express example matches url paths with objects which in turn matches http verbs with functions. This lays the routing out in a tree, which is concise and easy to read. The apps's entities are also written as objects with the functions as enclosed methods.
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);
Seems that only index.js get loaded when you require("./routes") .
I used the following code in index.js to load the rest of the routes:
var fs = require('fs')
, path = require('path');
fs.readdirSync(__dirname).forEach(function(file){
var route_fname = __dirname + '/' + file;
var route_name = path.basename(route_fname, '.js');
if(route_name !== 'index' && route_name[0] !== "."){
exports[route_name] = require(route_fname)[route_name];
}
});
You could also organise them into modules. So it would be something like.
./
controllers
index.js
indexController.js
app.js
and then in the indexController.js of the controllers export your controllers.
//indexController.js
module.exports = function(){
//do some set up
var self = {
indexAction : function (req,res){
//do your thing
}
return self;
};
then in index.js of controllers dir
exports.indexController = require("./indexController");
and finally in app.js
var controllers = require("./controllers");
app.get("/",controllers.indexController().indexAction);
I think this approach allows for clearer seperation and also you can configure your controllers by passing perhaps a db connection in.
No one should ever have to keep writing app.use('/someRoute', require('someFile')) until it forms a heap of code.
It just doesn't make sense at all to be spending time invoking/defining routings. Even if you do need custom control, it's probably only for some of the time, and for the most bit you want to be able to just create a standard file structure of routings and have a module do it automatically.
Try Route Magic
As you scale your app, the routing invocations will start to form a giant heap of code that serves no purpose. You want to do just 2 lines of code to handle all the app.use routing invocations with Route Magic like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.

Resources