I'd like to have a node module, which exports a factory that returns instances of a class function.
//myModule.js
function MyClass(options) {
this.options = options || {};
}
MyClass.prototype.handle = function(req, res, next) {
//I want to access the instance here in the middleware function
console.log(this.options);
}
module.exports = function(options) {
return new MyClass(options);
}
Then, inside server.js, I'm attaching this middleware:
var myInstance = require("./myModule")({
foo: "bar"
});
app.use(myInstance.handle);
Inside the middleware function, this refers to something else (the global object maybe?), but I want to access the instance containing options. The only solution I could come up with was calling myInstance.handle.bind(myInstance), but that's not too friendly to the module's users. Is there another way to solve this problem, or is there a better way to do this kind of pattern entirely?
It's better to avoid using prototype whenever you can, and you can achieve what you're doing using something like this:
module.exports = function(opts) {
// private methods/vars
var options = opts || {};
return {
handle: function(req, res, next) {
console.log(opts);
}
}
}
Related
How do I call the function expression "extractUserProgress" which is situated in an external module from server.js?
EDIT
I have clarified further what is happening in my code.
I have a chain of function expressions in my module that follow from "extractUserProgress". The last function returns an array which is what I'm after.
//setGen.js (module file)
module.exports = function(app, db) {
var extractUserProgress = function() {
//Access mongoDB and do stuff
nextFunction(x)
}
var nextFunction = function(x) {
let y = [];
//calculate y
return y // this is what i'm after
}
}
//server.js
const setGen = require("./setGen")
app.get("/setGen", function(req, res){
//data here from select input
extractUserProgress //How to call from here?
console.log(y) //array from module
});
I have required the module in server.js but not sure how to export function in this scenario where the functions in module also needs to access mongoDB.
Thanks
You can achieve this easily if you change the structure of your exports a little.
const extractUserProgress = function (app, db) {
console.log('This can be called');
//Access mongoDB and do stuff
}
module.exports = {
extractUserProgress
};
you can call this function from the otherside this way.
const newFile = require('./server');
newFile.extractUserProgress(); // you can pass arguments APP and DB to this function
With the code as-is, you can't - extractUserProgress is not accessible, it's declared inside the exported function scope.
If you need it accessible, and also need to keep the exported signature, then you can return a hash of functions e.g.
module.exports = function(app, db) {
...
return {
extractUserProgress(...) {
...
},
// More functions
}
}
// Usage
const setGen = require('./setGen')(app, db)
setGen.extractUserProgress(...);
If you don't need to maintain the existing exported function, then you can export the functions as a hash instead
module.exports = {
extractUserProgress(...) {
...
},
// More functions
}
// Usage
const setGen = require('./setGen')
setGen.extractUserProgress(...);
I'm working on a webserver, which will load various modules for various 'web applications' on the same domain.
In the following example,
createServer(function (req, res) { // crude request routing example
var path = url.parse(req.url).pathname
if(path.match(/^\/cats(?:\/|$)/))
require('./cats.js')(req, res)
else if(path.match(/^\/dogs(?:\/|$)/))
require('./dogs.js')(req, res)
else
require('./404.js')(req, res)
})
the if statement checks for a matching context path, and routes it to the appropriate module.
Now the module cats.js looks something like this:
module.exports = function (req, res) {
var path = url.parse(req.url).pathname
// HOW can I get context path and path info? What variables should I use?
if(req.path == '/cats/add') {
//....
} else if(req.path == '/cats/delete') {
//....
} else
// 404
}
The problem is, down the road I will be updating the context path, so instead of /cats, users will have to go to /somethingelse to get to cats.js
For this reason, cats.js should not be checking for /cats/add, but should instead look for $CONTEXT/add.
What is a good way to pass the 'context path' and the 'path info' to cats.js? I'm thinking about making something up and tacking it onto the req object, but surely this is a common problem and there is a generally accepted correct solution?
You could have cat.js export a function which takes a path arg and uses it to create the request handler on the fly:
module.exports = function(context){
return function (req, res) {
var path = url.parse(req.url).pathname
if(req.path == '/'+context+'/add') {
//....
}
//....
}
}
As others mentioned, Express.JS has this capabilities built in and would be a generally accepted solution, other than that you're free to roll however you want.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I am trying to convert a node.js with express framework app to meteor.
Essentially doing the reverse way of https://github.com/onmodulus/demeteorizer
Definitely not automatically, but there are a bunch of tricks you can chain together to almost get it automatically.
I've been through just that and here are all of my tricks for this.
Let's start with your express app main .js file. This one you need to add the following at the top:
/server/main.js:
routes = {};
var app = {
get: function(route, foo) {
// routes.get[route] = foo;
routes[route] = foo;
},
all: function(route, foo) {
// routes.all[route] = foo;
routes[route] = foo;
}
};
All this does is to define the app functions you need, and record the defined routes in an object, which we will later use to define those routes using iron-router. So this makes sure that things like the following get recorded in routes:
/server/main.js:
app.get('/show', function(req, res) {
res.render('mytemplate');
});
That's really the main trick. From here on its just labor.
In good meteor style, we will wrap all route rendering calls into a fiber, to make them synchronous like everything else on the meteor server. For that, we define a wrapping function waiter that we can reuse over and over again to wrap the route functions. And while we are add it, we will massage the connect request and response that we will get from the iron-routes on the meteor server into the res and req objects express would like to see. Mind you: this is not complete by any stretch. It's just the signatures I wanted to use from these objects.
/server/main.js:
/** create an sync version for meteor */
waiter = function(foo, req, res) {
var waiter_aux = Meteor._wrapAsync(function(foo, req, res, callback) {
res.set = function(header, value) {
res.setHeader(header, value);
};
res.send = function(codeorhtml, html) {
if (html) {
// two arguments provided, treat as described
res.statusCode = codeorhtml;
} else {
// no code, just html
html = codeorhtml;
}
callback(null, html);
};
res.render = function(name, data, callback) {
callback = callback || function(err, html) {
res.send(html);
};
var html = Handlebars.templates[name](data);
callback(null, html);
};
res.json = function(object) {
res.send(JSON.stringify(object));
}
res.redirect = function(URL) {
res.writeHead(302, {
'Location': URL
});
res.end();
};
req.header = function(x) {
return this.header[x];
};
TemplatesObject = Handlebars.templates;
// these objects need to be extended further
foo(req, res);
});
return waiter_aux(foo, req, res);
};
Finally, the real deal: creating routes for each specified express route. For this we will use iron-router. The following code will go through each defined route (caught by our redefined app functions and stored in routes), and wrap it in a fiber using our waiter, which will also take care of translating between this.request/this.response and the req and res objects express apps assume.
/routes.js:
if (Meteor.isServer) {
// create routes for all the app.get's and app.all's in bibbase.js
// (server)
console.log("setting routes:", routes);
_.each(routes, function(foo, route) {
Router.map(function () {
this.route(route, {
path: route,
where: 'server',
action: function() {
this.request.params = this.params;
var html = waiter(foo, this.request, this.response);
if (!this.response.statusCode) {
this.response.statusCode = 200;
}
if (!this.response.getHeader('Content-Type')) {
this.response
.setHeader('Content-Type', 'text/html');
}
this.response.end(html);
}
});
});
});
}
These are the most essential things I've done to accomplish what you are asking about. I'm sure I've missed a few details here, but this should give you an idea.
Update for post-Spacebars (I forget which version of Meteor that was):
In order to make this work, you now need to add handlebars-server:
meteor add cmather:handlebars-server
As it is not possible in nodejs to use variables in required files directly i want to create one model object through which i would be able to reach all my models. For example i have something like this:
function User() {
this.get = function () {
console.log('getSomeUser');
}
}
function Post() {
this.send = function () {
console.log('sendSomePost');
}
}
module.exports = function Model () {
this.User = User();
this.Post = Post();
};
Then i require it like and should use like this:
var Model = require('model.js');
var model = new Model();
model.User.get();
model.Post.send();
Is it possible to do?
This works except your module.exports would have to look like:
module.exports = function Model () {
this.User = new User();
this.Post = new Post();
};
User and Post would need to be instantiated objects in order to use them they way that you are. You may also need to require model.js like so:
var Model = require('./model.js');
Personally I like this approach better:
module.exports = function(app){
var somelib = require('../path/somelib')(app),
//a 'private' method
function somethingPrivate(callback){
callback();
};
return {
User: function (req, res) {
somethingPrivate();
console.log('getSomeUser');
},
Post: function(req, res) {
console.log('sendSomePost');
}
}
}
now note that I am passing (app) into this function. this is so that if need be I can access app specific things that have already been loaded on init. It's not required though.
I then call this by doing this somewhere else, say in my controller:
module.exports = function(app){
var Model = require('../path/to/Model')(app);
app.get('/user', Model.User);
}
So lets look at this closer, if for example you don't want to pass app along the chain, then just do the following:
module.exports = function(){
//a 'private' method
function somethingPrivate(callback){
callback();
};
return {
User: function (req, res) {
somethingPrivate();
console.log('getSomeUser');
},
Post: function(req, res) {
console.log('sendSomePost');
}
}
}
then elsewhere you do:
var Model = require('../path/to/Model');
I'm building my node.js app which has the following structure:
server.js
controllers/user.js
server.js require the user.js controller with:
require('./controllers/user.js').route(app, mongoose);
the controller/user.js file is like:
function route(app, mongoose){
function route(app, mongoose){
// Create new User item
app.post('/user/create', function(req, res){
...
}
// Edit user
app.put('/user/:id/edit', function(req, res){
...
}
...
}
module.exports.route = route;
This is working fine.
I know want to had middleware in the Edit user function for instance so it looks like:
...
app.put('/user/:id/edit', loadUser, function(req, res){
...
If I define loadUser function right above this line it's working fine. When I add all the middleware fonction in a file './lib/middleware.js' and when I try to load that file in user.js with:
require('../lib/middleware.js').create(); // Create is the exported function
this does not work and I have the error message saying that loadUser is an unknow function.
Any idea ?
** UPDATE **
I have updated the files such that, in server.js (main file) I have:
...
var middleware = require('./lib/middleware.js');
...
require('./controllers/user.js').route(app, mongoose, middleware);
...
In middleware.js, I then have:
function create() {
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
return module;
}
In controllers/user.js I have
function route(app, mongoose, middleware){
...
// Modify an user
app.put('/user/edit', middleware.loadUser, function(req, res){
...
}
...
}
When I run the app (node server.js) I then have the following error:
Error: PUT route /user/edit requires a callback
I am not sure to return the correct thing within middleware.js, not really familiar with module stuff yet. I also tried the "module.exports.create = create;" but same thing.
UPDATE WITH ANOTHER TRY
what if I create a module for the function ? In ./lib/middleware.js I would have:
(function(){
var middleware = {};
middleware.loadUser = function () {
console.log("loadUser");
}
return middleware;
}());
And in server, I call it:
var middleware = require('./lib/middleware.js');
middleware.loadUser;
It seems to me that should work but it does not...
"global" scope in a file is actually module scope. Just by creating a function in a different file it does become in scope in your original file.
What you want to do instead is
// routes.js
var middleware = require("../lib/middleware.js").create();
app.put('/user/:id/edit', middelware["loadUser"], function(req, res){
You will find that global variables actually write to module in their scope.
Then your function loadUser() { ... } should exist in the module property.
// middleware.js
function create() {
...
return module;
}
If you return module from your create function your returning global scope.
[Edit]
function create() {
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
return module;
}
You either need to add module.loadUser = loadUser or define loadUser in module scope. I.e. outside the create function.
[Further Edit]:
A standard setup would be something like:
// middleware.js
(function() {
function loadUser(...) {
...
}
...
module.exports.loadUser = loadUser;
})();
//otherfile.js
var middle = require("middleware");
middle.loadUser();
When code is loaded using require() it doesn't put anything into the global context. You have to bind it to a variable.
For example when you require your middleware file, you are able to call create() only because the module.exports object is returned from the require() function and you are able to access the call site directly. Here is how you would keep a reference to the require()d code so you could access it later:
var middleware = require('../lib/middleware.js');
middleware.create();
and probably this is what you want:
app.put('/user/:id/edit', middleware.loadUser, function(req, res) ...
update:
As raynos pointed out, no function wrapper is advised in this case. See his comment.
Note that I wrapped the loadUser function reference in an anonymous function. Leaving the code as you had it would have worked as long as your code didn't rely on the value of 'this' internally.
update:
I think I misunderstood what you were trying to do with create(). The above works if you define your loadUser() function as directly part of the module. If you need to do something else tricky in your Create() function you can use what I wrote above by doing something like
var loadUser = middleware.create();
Then you'll have the loadUser function defined in scope.