Does anybody know proper way to pass context to sails controllers action?
Here is my case why I want to do it:
--- AbstractPageController.js ---
module.exports = {
_getPageData: function(req, res) {
return {
data: {
title: this._getTitle(req, res), // if do nothing "this" is global object
menu: this._getMenu(req, res)
}
};
},
_getTitle: function(req, res) { return 'Cool Page'; },
_getMenu: function(req, res) { return [{ href: '/logout' }]; }
};
--- ConcretePageController.js ---
var _ = require('lodash');
_super = require('./AbstractPageController.js');
module.exports = _.merge({}, _super, {
'main': function(req, res) {
res.view('pageTemplate', this._getPageData(req, res));
},
_getTitle: function(req, res) {
return 'Absolutely - ' + _super._getTitle(req, res);
},
_getMenu: function(req, res) {
return [{ href: '/main/'}].concat(_super._getMenu(req, res));
}
});
That's why I need context.
For this particular case I found this solution:
--- routes.js ---
var concreteController = require('../api/controllers/ConcretePageController.js');
module.exports.routes = {
'/concrete_page': function(req, res) { concreteController.main(req, res); }
}
But it seems a little bit ugly and sails hooks (for example policies) stop works.
I was thinking about the other way. The main point of this, is to move all logic to services and to use a simple inheritance. But this seems strange for me too
Any ideas about a better way to reach the cases I have wrote?
P.S. All code I have wrote above is just an example.
I think you whant to pre-set some data to send to view template, right?
is yes you can use one Before hook ( like midleware ) or sails police
I use the sails hook for preload sails features from npm modules in we-plugin https://github.com/wejs/we-plugin
Check this hook for how load user locale in all requests after controllers :
Link: https://github.com/wejs/we-example/blob/master/api/hooks/we-locale/index.js#L15
You should just reference the controller directly
sails.controllers.yourControllerName.getTitle()
https://stackoverflow.com/a/20994036/1821723
Related
I'm pretty new to node.js and express and I was wondering if there's a way to define a route that calls upon another route simply to collect data and not to completely reroute.
I've got a route set up as follows:
app.get("/databases/list", function(req, res) {
db.listDatabases().then(names => {
res.send(names);
});
});
Subsequently I'd like to have a different route, say:
app.get('/whatever', function(req, res) {
// here I'd like to make a call to retrieve the information from the first route
// then I'd like to do something with that information, I want to stay in the same route.
}
Is this possible?
Expanding #marcobiedermann answer, In your case simply make a controller and and use the FUNCTION in both the routes. You don't need to fetch anything.
/// --- Controller ----
class SimpleController {
constructor(db){
this.db = db;
}
listDatabase(/*maybe optional callback*/){
return this.db.listDatabases();//or something....
}
whatever(/*maybe optional callback*/){
return this.listDatabase()
.then(process)
}
}
/// --- Routes ----
const sController = new SimpleController(db);
app.get("/databases/list", function(req, res) {
sController.ListDatabase().then(names => {
res.send(names);
});
});
app.get('/whatever', function(req, res) {
sController.whatever()
.then(....)
}
Yes this is possible.
You have to fetch the data from your first endpoint.
fetch('/databases/list')
.then( … )
This requires the /databases/list route to be defined before your /whatever route.
However, I would strongly advice you to NOT do this.
You should abstract your logic into a controller and call this controller in both of your routes:
const fetchController = {
fetchData: () => {
return fetch('path/to/data/to/fetch')
.then( … )
// or database call or wherever you might get the data from
}
}
app.get('/databases/list', (req, res) => fetchController.fetchData());
app.get('/whatever', (req, res) => fetchController.fetchData());
app.get("/databases/list", async function(req, res) {
return await db.listDatabases();
});
app.get('/whatever', async function(req, res) {
const result = await fetch('path/databases/list');
console.log(result)
});
It might help you, But it's not recommended way. You can create method (common somewhere in the controller) and use that where ever you need.
Here below there are two servers and two gqlServers. All combinations of them work.
The challenge is to extend express with some additional predefined code patterns shared across several apps, exposed through additional methods.
Which combination of a server and gqlServer is considered best practice and best for performance?
server:
server_A is a function that returns a class
server_B is a function that returns a function
gqlServer:
gqlServer_01 uses req.pipe
gqlServer_02 has the original express() passed into it
function gqlServer_01(options) {
let gqlApp = express();
gqlApp.use(options.route, function(req, res, next) {
res.send('gqlServer 01');
// next();
});
gqlApp.listen(8001, err => {
if (err) throw err;
console.log(`>> GQL Server running on 8001`);
});
}
function gqlServer_02(app, options) {
app.use(options.route, function(req, res, next) {
res.send('gqlServer 02');
// next();
});
}
// THIS SERVER ?
function server_A(config = {}) {
config = deepmerge(def_opt, config);
let app = express();
app.get('/', function(req, res, next) {
res.send('root');
// next();
});
class Server {
constructor(opt) {
this.opt = opt;
}
gql(props = {}) {
// THIS GQL SERVER ?
gqlServer_01({ route: '/gql-01' });
app.use('/gql-01', function(req, res) {
req.pipe(request(`http://localhost:8001/gql-01`)).pipe(res);
});
// OR THIS GQL SERVER ?
gqlServer_02(app, { route: '/gql-02' });
}
}
app.listen(8000, err => {
if (err) throw err;
console.log(`>> Server running on 8000`);
});
return new Server(app, config);
}
// OR THIS SERVER ?
function server_B(config = {}) {
config = deepmerge(def_opt, config);
let app = express();
app.get('/', function(req, res, next) {
res.send('root');
// next();
});
app.gql = function(props = {}) {
// THIS GQL SERVER ?
gqlServer_01({ route: '/gql-01' });
app.use('/gql-01', function(req, res) {
req.pipe(request(`http://localhost:8001/gql-01`)).pipe(res);
});
// OR THIS GQL SERVER ?
gqlServer_02(app, { route: '/gql-02' });
};
app.listen(8000, err => {
if (err) throw err;
console.log(`>> Server running on 8000`);
});
return app;
}
The goal is to have the best solution in order to create an npm package out of this and reuse the methods over several projects easily. The project was highly simplified for the sake of clarity.
I don't think you will have performance issues in any of these examples, so the question remains which of them is more modular.
If you are willing to make an npm package out of these, you shouldn't be calling express() inside your server code. Instead you should be passing the app as a parameter. This will allow you to reuse existing express apps initialized elsewhere. For this reason I would go for gqlServer_02
You also want to create a new server each time you call the module function, so I'd go with server_A for this reason. However it needs to receive the express app as parameter, in order to reuse existing express objects. I would also put the app.listen call inside a function in the Server class.
express.js provides you with a decent barebone system to implement a standard MVC development pattern. However most tutorials i've seen apply controller logic in a routes file or a global app file.
In an ideal world:
Model - Manages fundamental behaviours and data
Controller - Sends commands to the model and the view
View - Renders data from the model
currently i have the following:
routes/index.js - route pointing to an action
router.get('/hotels', function(req, res, next) {
hotels.run(req, res, next);
next();
});
controllers/hotels.js - controller sending a command to a model
module.exports = {
run: function(req, res, next) {
var users = new require('../models/hotels');
users.run(function(callback) {
res.render('hotels', { title: 'Hotels page', users: callback });
});
}
}
models/hotel.js - model requesting data
module.exports = {
run: function(callback) {
connection.query(sql, function(err, rows, fields) {
callback(rows);
//console.log(rows);
});
}
}
No matter what i try, i can't get the data from the model to return to the controller to then be passed to the view. I understand there are probably multiple errors within the above code as i'm new to express. But the fundamentals should be ok, and i'm hoping it's something obvious as to why i can't return the model data,as all logic above other than the callback works.
I believe i have solved this issue, if people are looking to use a similar MVC approach on an express.js project.
routes.js - changed to match Kevin's cleaner method.
router.get('/hotels', hotels.run);
controller/hotels.js -
module.exports = {
run: function(req, res, next) {
var users = new require('../models/hotels');
users.run(function(err, callback) {
res.render('hotels', { title: 'Hotels page', users: callback });
});
}
}
models/hotel.js
module.exports = {
run: function(callback) {
connection.query(sql, function(err, rows, fields) {
callback(err, rows);
});
}
}
so now the model returns the query as requested by the controller, giving the controller the ability to pass the data to the view.
I've been using Sails.js for quite some time and was wondering if there is a way to manually change the localization from the controllers depending on the url.
Example: http://example.com/en will return the English version and http://example.com/de will return the German one.
Thanks for your help!!
You can always change the locale in a controller action by using req.setLocale() or by setting the value of req.locale. You can also handle this more globally by using a policy:
// config/routes.js
module.export.routes = {
'/:lang/': 'MyController.index',
'/:lang/help': 'MyController.help',
'/:lang/contact': 'MyController.contact',
...etc...
}
// config/policies.js
module.exports.policies = {
'*' : 'localize'
}
// api/policies/localize.js
module.exports = function(req, res, next) {
req.locale=req.param('lang');
next();
};
Update 2020 to #sgress454's answer
// api/policies/localize.js`
module.exports = function(req, res, next) {
// This worked for testing
// You can use req.param('lang') instead of 'in'
req.setLocale('in');
next();
};
URL pattern is www.example.com/product-name/pid_010101. URL's first segment is Product name and second segment is Product number. My app router code is below
app.get("/:name?/:id", routes.index);
Now my all urls redirect to same page. For example some url like www.example.com/homepage/banner.html also redirect to www.example.com/product-name/pid_010101
Need to add some Filters in router. How to route the url to correct page?
I assume your routes will be:
/coffeemaker/pid_0101222
/bluemarker/pid_121121
etc. ?
You can use regular expressions for this OR
this is an example of in method filtering:
app.get('/rest/:collection', function(req, res) {
return routes.rest[req.params.collection](req, res);
});
in routes object:
exports.rest = {
tweets: function(req, res) {
return twitter.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
pics: function(req, res) {
return instagram.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
},
repos: function(req, res) {
return github.data.load(function(data) {
return res.json(data);
}, req);
},
links: function(req, res) {
return delicious.data.load(function(data) {
return res.json(data);
}, config.site.tag, req);
}
};
:colection is then tweets, pics , or links string
Can you make a list of all your pages?
Maybe I can help you with your routes...
This is because of /homepage/banner.html complies to your route as well.
Therefore you must also specify those routes you want to trigger.
Say for example you have a welcome page.(/welcome/guest) or something like that.
You can add another route above the general route. specific for that page.
app.get("/welcome/guest", routes.welcome);
Now this is too much work for all your pages. So you can avoid this with a couple of techniques, one would be to put a static part in the url say:
app.get("/product/:name?/:id", routes.product);
I don't really get your problem. Is /homepage/banner.html a static page? Middlewares work like a filter, you can simply put express.static above express.router:
app.use(express.static('public'));
...
app.use(app.router);
app.get("/:name?/:id", routes.index);
banner.html is in (APP_DIR)/public/homepage/banner.html
Write own middleware function that handles the legacy URLs, and place it above the Express router.
app.use(function(req, res, next) {
if (legacySystemHandles(req.url)) {
// do legacy stuff
}
else next(); // pass to the next middleware function
});
app.use(app.router);