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.
Related
In my node.js server conf file, I set app.use('/user', user.js), which maps /user route to a user.js file.
Then I create subrouting in my user.js file to handle my get or post requests.
My question is: what's the responsibility of module.exports=router at the end of this file?
If I remove it, routing stops working, so I don't understand if it is here to tell my server conf file that there are sub paths in user.js?
var express = require('express');
var user = require('../../models/user');
var db = require('../../models/index');
var router = express.Router();
router.get('/addUser',function (req, res, next) {
db.user.create(req.body)
.then(user => res.json({
data: user,
}))
.catch(error => res.json({
error: true,
data: [],
error: error
}));
});
module.exports = router;
When you do
var user = require('../../models/user');
the user object would get whatever is being exported from the module in user.js. So in user.js, the module.exports=router is mapping a router and all logic that's required to map /user (along with the right callbacks etc...)
If you remove it, your require statement can't acquire an exported object from that module, which is why it would fail. Your user object will be nullified effectively.
Check out here for more info: https://www.tutorialsteacher.com/nodejs/nodejs-module-exports
A router cannot listen(PORT) for requests on its own. The router is useful when you have lots of routes. It's useful for separating your app into multiple modules.
const app = express()
app.listen(port)
app is listening for the requests(not the Router) while your user.js is just a separate js file with some codes.
In module.export ,module is a variable that represents the current module and export is an object. Anything you assign to the module.exports will be expose as a module.
copied: Module in Node.js is a simple or complex functionality organized in single or multiple JavaScript files which can be reused throughout the Node.js application.
Once you do module.export = Router Now you have a newly created module. In nodeJs, you need to require a module before use it. const user = require('./user.js') will do that process. Once you require the node module into your app, you need to tell it to execute by app.use('/' , user)
Or you can do something like below too
in your user.js file,
var user = require('../../models/user');
var db = require('../../models/index');
module.export = (app) =>{
app.get('/addUser',function (req, res, next) {
db.user.create(req.body)
.then(user => res.json({
data: user,
}))
.catch(error => res.json({
error: true,
data: [],
error: error
}));
});
}
in your main index.js,
const app = express()
require('./user.js')(app)
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, ... });
});
});
});
I want to verify that a user has a role which allows him/her to use an endpoint in my API. Usually I would go about doing so by taking the userId sent as part of a JWT and do a lookup on the DB to see what the user's role is. It would happen inside an API call and would look something like this:
var userId = getUserIdFromJwt();
app.models.User.findOne({_id: userId}, function (err, user) {
...check if user is in role...
});
Now I want to try and move that code to a piece of middleware, which would look like this:
exports.isUserInRole = function(app, allowableRoles) {
var userId = getUserIdFromJwt();
app.models.User.findOne({_id: userId}, function (error, user) {
if (error) {
return res.status(500).json(error);
}
return function (req, res, next) {
if(_.includes(allowableRoles, user.Role)) {
next();
} else {
return res.status(401).json({"error": "User not in role"});
}
}
});
};
The middleware would be implemented like this:
const allowableRoles = ['admin', 'implementor'];
app.get('/getStuff/', isUserInRole(app, allowableRoles), function (req, res) {
... do stuff if user is in role ...
});
At this point I am running into a problem where the app.models.User value is always undefined.
I do not understand why app.models.User is undefined at this point as I can access it within the anonymous function inside the get call.
How would I go about about access the DB from within my middleware if I cannot send it app.models.User?
For reference, I am using Mongoose and exposing it to my app in the server.js from which I access the a MongoDB db.
I think you're problem is that you are trying to get models before they are actually initialized, as you are binding the app to the parameters on initialization of the app.
So in your main app file, I would do module.exports = app; and then in your middleware file, simply include the app by doing var app = require('./path/to/app');. Then remove the app from the middleware. You might end up with something like this:
var app = require('./path/to/app');
exports.isUserInRole = function(allowableRoles) {};
And in your route change it to this:
const allowableRoles = ['admin', 'implementor'];
app.get('/getStuff/', isUserInRole(allowableRoles), function (req, res) {}
And lastly in your app file, add the app to the exports:
module.exports = app;
EDIT:
If you're using Mongoose, you can also do something more simple like this:
var mongoose = require('mongoose');
var User = mongoose.model('User');
My first guess would be b/c you are actually calling the function instead of just passing it in. Additionally, you're not actually passing in middleware. Middleware functions look like
function(req,res,next){}
Additionally, you probably want to leverage sessions instead of hitting the database on each request
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
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.