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.
Related
Is there a way to dynamically remove a path from express so that the endpoint will return 404. For example:
app.get('./test', handle);
Later I would like to do something like:
app.get('./test').remove();
I would put a test in your handler rather than removing the path, something like this:
var handle = function(req, res) {
if (yourCondition) {
res.status(404);
res.send('This path has been removed');
}
}
I want to create a route which can change while the program is running.
Example : app.get('/',function(req,res){/*Something here*/}; This is a normal route.
I want to replace the '/' with a variable which can be replaced with a random number. After that I'll create a qrcode with a nodejs module and the user who scans this qrcode will confirm a kind of transaction.
If you understand my idea and you have a solution, I'll take it.
As #Louy said, use parameters:
var getQRCode = require('./yourQRCodeModule');
app.param('qrcode', function(req, res, next, qrcode) {
// qrcode will be "1234" if your request path was "/1234"
console.log('checking qrcode: %s', qrcode);
// get the qrcode from some asynchronous function
getQRCode(qrcode, function callback(err, qrcode) {
// if this number was not a valid dynamic path, return an error from your module
console.log('qrcode was %s', (!err && qrcode) ? 'valid' : 'invalid');
if (err) {
next(err);
} else if (qrcode) {
req.qrcode = qrcode; // object from your module
next();
} else {
next(new Error('failed to load QR code'));
}
});
});
app.get('/:qrcode', function (req, res) {
// req.qrcode will be the object from your module
// if the number was invalid, this will never be called
});
What I'm trying to point out is that you're thinking of this scenario differently than how express approaches the problem. You want a one-time route with a specific qrcode, but these kind of routes don't exist in express. So here's what I understand your ideal solution to look like:
server creates "azjzso1291084JKioaio1" for a qrcode
you register something like app.getOnce("azjzso1291084JKioaio1", function(req, res){...})
first time the request gets called, it's removed from your express router
Here's what I'm suggesting:
server creates "azjzso1291084JKioaio1" for a qrcode
your module stores this qrcode either in a database or in memory, within your module, e.g. var qrcodes = {}; qrcodes["azjzso1291084JKioaio1"] = {some: 'object'};
your app.param asynchronous function based on the example given in step 2 could look like this:
// yourQRCodeModule.js
var qrcodes = {};
qrcodes["azjzso1291084JKioaio1"] = {some: 'object'};
module.exports = function getQRCode(qrcode, callback) {
if (qrcodes[qrcode]) {
var obj = qrcodes[qrcode]; // copy object
delete qrcodes[qrcode]; // remove from memory here
callback(null, obj);
} else {
// invalid path
callback(new Error('invalid QR code'), null);
}
};
Now notice if you request /azjzso1291084JKioaio1 twice, the second time fails. This is how you intend it to work, if I am not mistaken.
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);
}
}
}
If I've made some middleware that works together, what's the best convention for grouping and managing the functionality?
In my server.js file, I've currently just got them listed one after another with app.use calls.
It's occurred to me however that if the first one in my set doesn't produce any data, the subsequent ones in the group are fine to skip. I guess this is ultimately an aggregation although I haven't seen any examples of such in other projects.
The connect middleware has a good example for this kind of problem. Take a look at the bodyParser:
app.use(connect.bodyParser()); // use your own grouping here
is equivalent to
app.use(connect.json());
app.use(connect.urlencoded());
app.use(connect.multipart());
Internally the bodyParser function just passes the req and res objects through each of the before mentioned middleware functions
exports = module.exports = function bodyParser(options){
var _urlencoded = urlencoded(options)
, _multipart = multipart(options)
, _json = json(options);
return function bodyParser(req, res, next) {
_json(req, res, function(err){
if (err) return next(err);
_urlencoded(req, res, function(err){
if (err) return next(err);
_multipart(req, res, next);
});
});
}
};
The full code can be found at the github repo
edit
Informed in the comments bellow that passing an array will acieve the exact same thing, so no need for an additional module. :-)
I was looking for a way to do this too as my application is very granular, but I didn't want to nest everything as in the other answer.
I'm sure there is something more comprehensive out there already, but I did this in the end:
/**
* Macro method to group together middleware.
*/
function macro (...middlewares) {
// list of middlewares is passed in and a new one is returned
return (req, res, next) => {
// express objects are locked in this scope and then
// _innerMacro calls itself with access to them
let index = 0;
(function _innerMacro() {
// methods are called in order and passes itself in as next
if(index < middlewares.length){
middlewares[index++](req, res, _innerMacro)
} else {
// finally, next is called
next();
}
})();
}
}
And then use it like this:
var macro = macro(
middleware1,
middleware2,
middleware3
);
app.post('/rout', macro);
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.