Using app.use to set up some routes in sails.js - node.js

First of all, the context. I'm using agenda to schedule tasks in my sails.js app. Agenda starts in a hook, after orm and some other hook have finished. So far everything is good. Then I discovered agendash, a web interface to manage agenda tasks. And I don't manage to make it work with sails.js.
The problem is in following. This is how agendash should be used (from the doc):
var express = require('express');
var app = express();
// ... your other express middleware like body-parser
var Agenda = require('agenda');
var Agendash = require('agendash');
var agenda = new Agenda({mongo: 'mongodb://127.0.0.1/agendaDb'});
app.use('/agendash', Agendash(agenda));
And I can't find where I should put this. As I said, agenda is initialized in a hook, and then I save it as sails.agenda. So the only thing I really have to do is
app.use('/agendash', require('agendash')(sails.agenda))
But I'm not sure how I could add a new route like this, outside of routes.js (I cannot refer to sails in that file, it's undefined), and then protect this route with some policies (I want agendash to be available only to admin).
If I get it right, this line should be run only once, so I cannot put it in config.http as middleware. But otherwise the router of sails.js will overwrite the route (at least if I put sails.hooks.http.app.use('/agendash', require('agendash')(agenda)) in a hook, the route is not exposed).
So what should I do to make it work?

First you should not init agenda in a hook but in config/bootstrap.js which is executed once on startup. Then you create a file called config/myApp.js where you put your global variables:
/**
* Expose global variables.
*/
module.exports.myApp = {
agenda: null
};
In config/bootstrap.js you set this variable once:
var Agenda = require('agenda');
sails.config.myApp.agenda = new Agenda({mongo: 'mongodb://127.0.0.1/agendaDb'});
Now you have a reference to agenda and you can access from everywhere in your app using sails.config.myApp.agenda.
To register the agendash route you have to use a custom middelware as explained here How to use custom route middleware with Sails.js? (ExpressJS) and discussed here https://github.com/balderdashy/sails/issues/814

Ok, I found the solution by using the customMiddleware in config/http.js where I could do app.use, protect the route by middlewares and eventually call Agendash(agenda)(req, res, next).
For this to work I had to move agenda initialization out of the hook and put it inside customMiddleware. This is what my code looks like:
customMiddleware: function(app){
var Agenda = require("agenda");
var Agendash = require('agendash')
var agenda = new Agenda({db: {address: 'localhost:27017/dbName', collection: 'collectionName'}});
//protect the route with several policies
app.use('/agendash', function(req, res, next){
require('../api/policies/policy1')(req, res, function(){
require('../api/policies/policy2')(req, res, function(){
require('../api/policies/policy3')(req, res, function(){
Agendash(agenda)(req, res, next)
}
)
})
})
});
sails.agenda = agenda; //saving the reference
agenda.on('ready', function(){
var eventsToWaitFor = ['hook:orm:loaded', 'hook:email:loaded'];//if need to wait for some events before starting tasks
sails.after(eventsToWaitFor, function(){
require('./../api/services/jobs')(agenda);//loading all the jobs
agenda.on('fail', function(err, job){
//log error here
});
agenda.every('10 minutes', 'task1');//schedule tasks
agenda.every('10 minutes', 'task2');
agenda.start();//start agenda
});
})
}

Related

Nodejs, Express, routes

I have build an api using express. In my routes file I have:
app.route('/getBalances')
.post(api.getBalances);
api.getBalances, depending on a parameter send through post called "vehicle" gets first which is the correct controller to load and to invoke its getBalances method, in example:
var controller = commonModel.getController(query.vehicle.toLowerCase());
controller.getBalances();
getBalances is not the only entry point I have, so I was wondering if it was possible to call a "global" method which is call for every entry point, in that way I wouldn't need to identify the correct controller on each method but on the global method.
Thanks in advance for your help.
Use a preliminary middleware which will run before adding any api route. Example:
// This middleware has to be added first.
app.use(function(req, res, next) {
var query = req.query; // or `req.body`, whatever you like
if (query && query.vehicle) {
req.controller = commonModel.getController(query.vehicle.toLowerCase());
}
next(); // delegate request to the next routes
});
// Now add specific api middlewares.
app.route('/getBalances')
.post(function(req, res) {
var controller = req.controller; // we've populated this earlier
res.send(controller.getBalances());
});
app.route('/anotherMethod')
.post(function(req, res) {
var controller = req.controller;
// etc.
});

NodeJS - Send result of a database query to a view or route before render

I'm making a webapp with NodeJS using Express JS, Socket IO and Handlebars, but i am pretty new to these technologies.
I'm struggling to find a way to pass the result from a query to my menu(a partial), mainly because Node is async, so by the time the result from my query returns, the page has already rendered, and the values never passed.
main.handlebars (Main layout):
(...)
{{> menu}}
(...)
{{{body}}}
router.js
var express = require('express');
var router = express.Router();
var index_controller = require('../controllers/index_Controller');
router.get('/', index_controller.index);
module.exports = router;
index_controller.js
exports.index = function(req, res) {
res.render('main_page_html');
};
This menu will appear in every page, and i want to show in this menu names from people online, that it's the result from the query.
I tried putting the code for the query inside the route function, and it works, but i would have to copy the same code to every route that i have, because as i said, this menu appears in all of them.
If i try to do a function outside the route function, async kicks in and no data is sent to the page.
There's definitely a better solution for this.
P.s.: Emit my data via socket to the client is one way, but i would like to do things server-side.
-Solution-
I did as Tolsee said, i created a middleware, so now i can call this middleware in every route that is needed. This is how i have done:
menu.js
exports.onlineUsers = function (req, res, next) {
// database query {
// res.locals.onlineUsers = queryResult;
// next();
// }
}
router.js
var menu_midd = require('../middleware/menu');
router.get('/', menu_midd.onlineUsers, index_controller.index);
module.exports = router;
index_controller.js
exports.index = function(req, res) {
res.render('main_page_html', {data: res.locals.onlineUsers});
};
Well, at least works for me. :)
There are two solutions to this problem. If you want to do it server side then you need to pass the menu data to handlebar/view. If you want it through all the pages then you can make a middleware function and employ it to all the router like below:
function middleware(req, res, next) {
// your code
// define your menu variable
// And assign it to res.locals
res.locals.menu = menu
next()
}
// let's employ it to all the routes
app.use(middleware)
// You can employ it to separate routes as well
myRouter.get('/sth', middleware, function(req, res) {
// your code
})
With that being said, If your menu is showing online users, you will need to use socket.io(even if you rendered it through server-side at the start) because you will need to update the online user list in real-time.

How to chain middlewares using routers

I'm using express 4.0 and I'm having trouble to chain middlewares. I have 2 routers : a job router and a recruiter router.
Everything works fine at the moment ( I can use CRUDs on both of these routers) but I'd like the POST method for a job to call a method withing the recruiter router and I don't know to achieve this.
router/recruiter.js :
var express = require('express');
var router = express.Router();
/* GET recruiters listing. */
router.get('/', function(req, res, next) {
var recruiters = [];
//get recruiters
res.json(recruiters);
});
function(err, req, res, next) {
console.info("pseudo code for a function I'd like to call in the job.js file");
});
module.exports = router;
router/job.js :
var express = require('express');
var uuid = require('uuid4');
var router = express.Router();
var jobs = [];
/* GET job listing. */
router.get('/', function(req, res, next) {
jobs = [];
//get jobs
res.json(jobs);
});
/* add jobs . */
router.post('/', function(req, res, next) {
console.info('add job', req.body);
var body = req.body;
//I omit the parts where I check the req and save the object
//At the moment I do this but I'd like to call a method within the recruiter router before sending the json back to the client.
res.json({'jobs': []});
});
module.exports = router;
and here are the relevant parts in the app.js :
var job = require('./routes/job');
var recruiter = require('./routes/recruiter');
app.use('/job', job);
app.use('/recruiter', recruiter);
Turning my comment into an answer...
Express does not offer any special way to share code among multiple routes. If you want the code always executed before your route handler, you can, of course, use a common middleware function.
But, if your code sharing case is that you just want two or more routes to be able to execute some common code from within their route implementations, then this is really just a plain Javascript issue. You put the common code into a shared function and you call that function from two or more routes. In other words, you just share code among routes that same way you share code among any other Javascript functions. Express doesn't require anything special in this regard. Do it that way you always do it in Javascript.
It is fairly common that people coding in Express get caught up in the way you structure your code for Express and somehow forget that you can still use regular shared, common functions to share code (I've seen many people caught by this) - expecting their to be an "Express" way to share code. There isn't. Just do the normal Javascript method of sharing common code by creating a function with the common code in it and calling it from more than one place.

Express: how can I get the app from the router?

I know I can get the Express app from inside an individual route with:
req.app
However I need to start a single instance of a module inside routes/index.js, i.e.:
var myModule = require('my-module')(propertyOfApp)
How can I get the express app from the router?
It really depends on your own implementation, but what I suggested in the comments should be working:
// index.js
module.exports = function(app) {
// can use app here
// somehow create your router and do the magic, configure it as you wish
router.get('/path', function (req, res, next) {});
return router;
}
// app.js
// actually call the function that is returned by require,
// and when executed, the function will return your configured router
app.use(require('./index')(app));
p.s.
Of course this is just a sample - you can configure your router with path, and all kind of properties you wish. Cheers! :)

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

Resources