I am trying to decorate render function in restify's router with this code...
decorate.js
module.exports = function (server) {
return function (req, res, next) {
function newRender(orig) {
return function(path, params, query) {
return "http://localhost:3000" + orig(path, params, query);
}
}
server.router.render = newRender(server.router.render);
next();
}
}
and in my server.js
var restify = require("restify");
var decorate = require("./decorate");
var server = restify.createServer();
server.pre(decorate(server));
server.get({name: "get_user", path: "/users/:id"}, function(req, res){
res.send(req.params.id);
});
server.get("/decorate", function(req, res){
res.send({data: server.router.render("get_user", {id: 2})});
});
server.listen(3000);
but I only get this error. Please help.
{"code":"InternalError","message":"Cannot read property 'get_user' of undefined"}
You are passing the original render method with global this as context. Just bind the server.router as context when passing it:
original_render = server.router.render;
function decorate(){
...
server.router.render = newRender(original_render.bind(server.router));
...
}
Update: You keep the reference of original render method outside the middleware function. Otherwise there will be a stack of newRender calls with increasing requests.
Related
I want to keep routes separate from controller.
My route is:
'use strict';
module.exports = function(app) {
var controller = require('../controllers/controller');
app.route('/').get(controller.index);
};
And controller is:
exports.index = function() {
request = new Request(
"MYQUERY",
function(err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
}
);
request.on('row', function(columns) {
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
connection.execSql(request);
};
I am able to see the result in the terminal console but I want to return it as JSON to http. I can use the following if I am using controller and routes all together:
router.get('/about', function (req, res) {
res.send('About this wiki');
})
The callback function to .get (or any router request handler) takes at least two arguments: request and response. You can see this with your example:
router.get('/about', function (req, res) {
res.send('About this wiki');
})
You could rewrite this to make the callback a named function rather than an anonymous function:
const aboutHandler = function (req, res) {
res.send('About this wiki');
});
router.get('/about', aboutHandler);
Your controller.index is the same kind of function, so it will take those two arguments. You just have to change your function to take them:
exports.index = function (req, res) {
This will give you access to res, and you can use it as you need to do send the response via res.send or res.json if you build a JSON object by accumulating the row results. You can use request.on('end' ... to know when the query has emitted all its results.
I might be misunderstanding your question, but do you mean res.json(...);?
Is there a way that I can assert the middleware is actually attached to the route in my unit test below?
The server ..
var express = require('express');
var http = require('http');
var app = express();
var server = http.createServer(app);
var PORT = 5000;
var myModule = require('myModule');
var handler = require('handler');
var myMiddleware = myModule.doStuff(server);
app.use('/', myMiddleware);
app.route('/')
.get(function(req, res) {
handler.respond(req, res);
});
if (!module.parent) {
server.listen(PORT, function(err) {
if (err) {
logger.error(err);
return;
}
});
}
module.exports = app;
handler.js contains ..
var handler = {
respond: function(req, res) {
res.sendStatus(200);
}
};
module.exports = handler;
myModule.doStuff is implemented using ..
function doStuff(server) {
const SERVER = server;
return function (req, res, next) {
if (server.connections === 2) {
res.send({
message: 'Overloaded'
});
} else {
next();
}
}
}
I test the routes are setup using ..
describe('/', function() {
it('should test endpoint', function(done) {
sandbox.stub(handler, 'respond', function(request, response) {
response.send('called');
});
request(app).get('/')
.expect('called')
.end(done);
});
});
Is there a way that I can assert the middleware is actually attached
to the route in my unit test?
Well that question actually has two sides to it.
Testing that Express.js works as its supposed to
I would personally consider it a waste of time to test Express.js. It already has it's own test suite and it has been around for ages and therefore you should be able to trust that app.use() works as it is supposed to.
Testing that you are passing the expected middleware to app.use()
This is more relevant and you can lay the groundwork for being able to perform tests by structuring your code this way:
var myMiddleware = {
m1: {
path: 'somePath',
callback: 'someCallback'
},
m2: {
path: 'anotherPath',
callback: 'anotherCallback'
}
}
function mountMiddleware () {}
for (var middleware in myMiddleware) {
app.use(middleware.path, middleware.callback);
}
}
Now you can perform unit testing on the contents of myMiddleware and the workings of mountMiddleware() and thereby assert that all your middleware is correctly passed to app.use() (which you expect to work).
If you can change your middleware to something like this:
function doStuff(server) {
const SERVER = server;
return function (req, res, next) {
req.doStuffRun = true;
if (server.connections === 2) {
res.send({
message: 'Overloaded'
});
} else {
next();
}
}
}
Then you will be able to test for req.doStuffRun in your callbacks.
But here it doesn't seem that it is called because you are not running the actual request but you are calling the handler which does not contain the middleware.
what´s the best way if I want to make a GET request in a route?
api.js
api.route('/guests')
.get(function(req, res) {
Guest.find(function(err, guests) {
if (err)
res.send(err);
res.json(guests);
});
});
routes.js
app.get('/export', requiresLogin, function(req, res) {
/* make a GET request to my api (eg.: 'api/guests') */
/* and save the 'guests' to a variable */
});
First Solution
Instead of calling internal apis, you can define a controller guestCtrl.js and call the function from guestCtrl.js in api.js and routes.js
guestCtrl.js
module.exports = {
getGuests : function(){
Guest.find(function(err, guests) {
if (err)
//handle error
return [];
else
return guests;
});
}
}
api.js
//path of guests.js
var guestCtrl = require('guestCtrl.js');
api.route('/guests').get(function(req, res) {
return guestCtrl.getGuests();
});
routes.js
var guestCtrl = require('guestCtrl.js');
app.get('/export', requiresLogin, function(req, res) {
var guests = guestsCtrl.getGuests();
// do whatever you like to do with guests
});
Second Solution
If you really want to work with internal api, then you can use request module.
e.g.
routes.js
var request = require('request');
app.get('/export', requiresLogin, function(req, res) {
// you can put the hostname and port here
request('http://127.0.0.1:3000/api/guests', function(err, body, response){
var guests = body; // and save the 'guests' to a variable
});
});
In express, everything else held constant, is there a difference between:
app.all('/', mongoProxy(config.mongo.dbUrl, config.mongo.apiKey));
and
app.all('/', function (req, res) {
mongoProxy(config.mongo.dbUrl, config.mongo.apiKey);
});
The former is able to return the return value from mongoProxy while the latter is not, where mongoProxy looks something like this:
module.exports = function(basePath, apiKey) {
basePath = url.parse(basePath);
// Map the request url to the mongolab url
// #Returns a parsed Url object
var mapUrl = module.exports.mapUrl = function(reqUrlString) {
//use the basePath to Parse the URL
return newUrl;
};
var mapRequest = module.exports.mapRequest = function(req) {
var newReq = mapUrl(req.url);
// Make a new request and return it..
return newReq;
};
var proxy = function(req, res, next) {
try {
var options = mapRequest(req);
// Create the request to the db
var dbReq = https.request(options, function(dbRes) {
// Save result
});
// { send result }
res.send(data);
res.end();
});
});
// send request
dbReq.end(JSON.stringify(req.body));
} catch (error) {
//..
}
};
return proxy;
};
The documentation is not clear on explaining the conceptual difference between the two; in the examples I've seen, the former function
app.all('/', mongoProxy(config.mongo.dbUrl, config.mongo.apiKey));
is able to access the req and res object without having it actually passed in as done in the latter, function (req, res).
What is the difference between the two, and is one preferable?
tl;dr
Yes, there is a difference: the first will work, whereas the second will hang (you don't invoke the anonymous function returned by mongoProxy). The first is preferable because it is more idiomatic to express (you're using middleware).
First, note how in mongoProxy, you return proxy, an anonymous function:
module.exports = function(basePath, apiKey) {
/* snip */
var proxy = function(req, res, next) { // <-- here
/* snip */
};
return proxy; // <-- and here
};
Let's break it down:
var proxy = mongoProxy(config.mongo.dbUrl, config.mongo.apiKey)
// proxy is an anonymous function which accepts: (req, res, next)
app.all('/', proxy);
// express will use proxy as the callback (middleware), which means this is the same as:
app.all('/', function (req, res, next) {
proxy(req, res, next)
})
Let's rewrite the second example—which should make clear why it doesn't work:
var proxy = mongoProxy(config.mongo.dbUrl, config.mongo.apiKey)
app.all('/', function (req, res) {
proxy // nothing happens because you don't invoke the function
});
If you want to use the second example, you could invoke proxy with proxy(req, res, next), but this is not idiomatic (in general and especially for express). Express is all about middleware, so use the first example.
Here's another example, which uses a closure (much like your mongoProxy function):
function getPermissionLevelMiddleware (level) {
// returns an anonymous function which verifies users based on `level`
return function (req, res, next) {
if (req.isAuthenticated() && req.user.permission.level > level)
return next()
return res.redirect('/no/permission')
}
}
var isAdmin = getPermissionLevelMiddleware(9000)
// `isAdmin` only allows users with more than 9000 `user.permission.level`
var isPleb = getPermissionLevelMiddleware(1)
// `isPleb` allows users with more than 1 `user.permission.level`
app.get('/admin', isAdmin, function (req, res) {
res.render('admin.jade')
})
Obviously the first one will return result because req and res objects are accessible to it, where as second case you need to send req and res in the method params of mongoProxy. If you won't send, req and res won't be accessible to it. So, for second scenario to work, the method signature need to be changed to :
module.exports = function(basePath, apiKey, req, res) {
I have done
I'm trying to avoid callback hell by breaking down my Express / Kraken.js controller into smaller callback functions.
I was processing a request and had about 6 levels of nested anonymous callback functions.
so now I have my main function that looks like this:
// ugh, I know this isn't right
var globalProducts = {};
module.exports = function (server) {
server.get('/project', function (req, res) {
var data = req.query;
globalData = data;
if(!data.projectId || !data.ruleSetId)
res.json({error: "Incomplete input data."});
// pass response to products call back
Project.findOne({ _id: mongoose.Types.ObjectId(data.projectId) }, setUpProducts);
});
};
function setUpProducts(err, project){
// get all products and pass them down the pipe
project.findAllChildren(setUpRules);
}
function setUpRules(err, products) {
// we need to access products in another function
globalProducts = products;
// find the rule set and build the rule Flow
RuleSet.findOne({ _id: mongoose.Types.ObjectId(globalData.ruleSetId) }, function(err, ruleSet) {
ruleSet.buildFlow(processFlow);
});
}
My question is what is the best way to pass around info between callbacks ? My solution was var globalProducts = {}; but to me, the controller contain any 'global state' .. whats the best way to handle this ?
Doing this is a bad idea. It will cause race condition type issue — basically it's the same as sharing data in multithreaded environment. Instead you can use req or res to store data. To do that you need them in scope, so you can define all functions inside route handler or make each function a middleware so it will have req and res as arguments. Here is an example of this approach:
function check (req, res, next) {
if(!req.query.projectId || !req.query.ruleSetId) return res.json({error: "Incomplete input data."});
next()
}
function findProject (req, res, next) {
Project.findOne({ _id: mongoose.Types.ObjectId(req.query.projectId) }, after);
function after (err, project) {
if (err) return next(err);
req.project = project;
next();
}
}
function findProducts (req, res, next) {
req.project.findAllChildren(after)
function after (err, products) {
if (err) return next(err);
req.products = products;
next();
}
}
function respond (req, res) {
res.render('view', {
products : req.products,
project : req.project
});
}
module.exports = function (server) {
server.get('/project', check, findProject, findProducts, respond);
};