I'm using shrinkroute https://npmjs.org/package/shrinkroute to make links in nodejs. I get error 500 ReferenceError: shrinkr is not defined
How to pass shrinkroute to routes/index.js? Is there a better way to create url by passing query string args?
//app.js
var app = express();
var shrinkr = shrinkroute( app, {
"user": {
path: "/user/:id?",
get: routes.showOrListUsers
}
});
//url method works in app.js
var url = shrinkr.url( "user", { id: 5, page:40, type:'a' } );
console.log(url);
app.use( shrinkr.middleware );
//routes/index.js
exports.showOrListUsers = function(req, res, next) {
console.log(req.params);
//shrinkr errors out in index.js
var url2 = shrinkr.url( "users", {name: "foo"});
console.log(url2);
}
One solution would be to store shrinkr in your app object using app.set:
// app.js
...
app.set('shrinkr', shrinkr);
...
In routes/index.js, you can access it through the req.app or res.app objects:
exports.showOrListUsers = function(req, res, next) {
var shrinkr = req.app.get('shrinkr');
...
};
A bit late to the party, but the following works as well:
app.js
var my_var = 'your variable';
var route = require('./routes/index')(my_var);
app.get('/', route);
and meanwhile in route.js
var express = require('express')
, router = express.Router()
// Router functions here, as normal; each of these
// run only on requests to the server
router.get('/', function (req, res, next) {
res.status(200).end('Howdy');
});
module.exports = function(my_var){
// do as you wish
// this runs in background, not on each
// request
return router;
}
Two easy ways to achieve what you want:
1. Accessing your shrinkroute instance from within your route
Simple as that. Nothing else is required after Shrinkroute is setup.
exports.showOrListUsers = function(req, res, next) {
var shrinkr = req.app.shrinkroute;
console.log( "Route: " + req.route.name ); // ta-da, made available by Shrinkroute
// do your URL buildings
};
2. Using the middleware
If you don't want be tempted with non URL building methods of Shrinkroute, you can use the middleware, which will make available to you some helpers in your route and in your template (via locals):
// app.js
app.use( shrinkr.middleware );
// routes/index.js
exports.showOrListUsers = function(req, res, next) {
console.log( "Route: " + req.route.name ); // ta-da, made available by Shrinkroute
req.buildUrl( "users", { name: "foo" } );
// or, if you want the full url with the scheme and host...
req.buildFullUrl( "users", { name: "foo" } );
};
And maybe you want to use them in your templates as well?
// templates/index.jade
a( href=url( "users", { name: "foo" } ) ) Foo profile
a( href=fullUrl( "users", { name: "foo" } ) ) Foo profile
This method has the advantage that you don't get direct access to route setters inside a route.
Disclaimer: I'm the author of Shrinkroute.
you should import it. add following line to the very beginning of your code
var shrinkroute = require('shrinkroute');
Related
Suppose I have two routes in my expresjss project: users.js and profiles.js.
users.js:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { three: 'four', extra: extra } );
});
module.exports = router;
profiles.js:
var express = require('express');
var router = express.Router();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Notice how I have something_nice() method there, which ideally I would define in a 'super class' if this were regular OOP like rails controllers.
How do I go about this with node + expressjs? My assumption was I should create a new module, and require it here, but is this the best practice?
You solve it in the same way. Using a class:
class SomethingCool {
somethingNice() {
return 'cool!';
}
}
module.exports = SomethingCool;
In your route:
var express = require('express');
var router = express.Router();
var SomethingCool = require('./something-cool.class');
const something = new SomethingCool();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something.somethingNice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Same principles should apply regardless of language (as long as they have support for classes, or class-like objects).
Also you don't even need a class here:
function somethingNice() {
// some logic
}
// inside file1
router.get('/', (req, res, next) => {
const extra = somethingNice();
res.json({ one: 'two', extra });
});
You can reuse functions where you like/need, just ensure they're exported using module.exports (if using in a different file/module).
Have a read over this when you have time:
https://dev.to/santypk4/bulletproof-node-js-project-architecture-4epf
It may be able to answer some of the questions you later have about design, structure and reusing logic in different areas.
EDIT: an explanation on how middleware can help in certain situations.
https://expressjs.com/en/guide/using-middleware.html
function isUserAdmin(user) {
// some logic for determining
}
app.use((req, res, next) => {
if (isUserAdmin(req.user)) {
req.role = 'Admin';
}
next();
});
This is a simplified example, in reality you'd also need to add the req.user.
The thing to understand here, is your ability to use middleware to reuse functionality.
However, if you wanted something a little more specific to the route, then I'd opt to use a class (following the Service pattern from other reference link).
Another (more common) example, consider a logger, it outputs which endpoint was requested and with what method:
// file: service/logger.service.js
class LoggerService {
log(message) {
console.log(message);
}
}
module.exports = MyLogger;
// file: middleware/logger.middleware.js
const logger = new MyLogger();
app.use((req, res, next) => {
const path = req.path;
const method = req.method;
logger.log(`${path} ${method}`);
return next();
});
This way, your route never needs to know about the logger, or what function it has, you can plug an unlimited amount of additional functionality this way.
Although it's more suited for generic tasks, like checking a user has authenticated (for example), or is authorized, but it's certainly not limited to only that type of use.
If you really want a super class, then use a singleton:
class SuperClass {
constructor() {
this.someOtherClass = new UserClass();
this.someSecondClass = new ProjectClass();
}
doSuperWork() {
}
}
module.exports = new SuperClass();
I'll say though, it may not be the best solution (super classes in general).
You can create a function that accepts the router instance as an argument and can implement your common logic inside that function. Javascript uses composition instead of inheritance.
module.exports=function (router){
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
}
I would like to pass additional data from app.js to express-resource routes and I have not figured out yet. How would you do that? Note that I'm using express-resource
// app.js
var myAddOnData = 'abc';
app.resource('users', './routes/user');
// user.js
exports.index = function (req, res) {
console.log(myAddOnData);
};
Thanks
These are the three approaches I can think of. Without the little I know about your specific problem, it sounds like middleware might be the way to do it.
With a global app variable
Create a value using app.set in app.js and then retrieve it using app.get in user.js.
Using a module
Store the information in an isolated module, then require() as needed. If this is running across multiple instances, you'd obviously want to store the values to disk as opposed to in memory.
// keystore.js
// -----------
module.exports.set = function(id, val) { /* ... */ };
module.exports.get = function(id) { /* ... */ };
// app.js
// -----------
var ks = require('./keystore');
ks.set = function("userInfo", "abc");
module.exports.get = function(id) { /* ... */ };
// user.js
// -----------
var ks = require('./keystore');
ks.get = function("userInfo", "abc");
(Maybe check out pot?)
Using Middleware
Use custom middleware to attach data to the request object which can then be accessed later in the route handlers.
//app.js
//------
var express = require('express')
, cookieSessions = require('./cookie-sessions');
var app = express();
app.use(express.cookieParser('manny is cool'));
app.use(cookieSessions('sid'));
// ...
//cookie-sessions.js
//------------------
module.exports = function(name) {
return function(req, res, next) {
req.session = req.signedCookies[name] || {};
res.on('header', function(){
res.signedCookie(name, req.session, { signed: true });
});
next();
}
}
via https://gist.github.com/visionmedia/1491756
Basic route is like this:
app.get('/', function(req, res){
res.send('hello world');
});
Is it possible to name that route and have it available in any template so it can be used like this:
app.get('/', name="index", function(req, res){
res.send('hello world');
});
Go to site index page.
Inspiration comes from Django :)
There is no out of the box mechanism for that. However you can mimic Django's style like that: define urls.js file which will hold an array of URLs. First start with:
myviews.js
exports.Index = function( req, res, next ) {
res.send( "hello world!" );
};
urls.js
var MyViews = require( "mywviews.js" );
module.exports = [
{ name : "index", pattern : "/", view : MyViews.Index }
]
Now in app.js ( or whatever the main file is ) you need to bind urls to Express. For example like this:
app.js
var urls = require( "urls.js" );
for ( var i = 0, l = urls.length; i < l; i++ ) {
var url = urls[ i ];
app.all( url.pattern, url.view );
};
Now you can define custom helper ( Express 3.0 style ):
var urls = require( "urls.js" ), l = urls.length;
app.locals.url = function( name ) {
for ( var i = 0; i < l; i++ ) {
var url = urls[ i ];
if ( url.name === name ) {
return url.pattern;
}
};
};
and you can easily use it in your template. Now the problem is that it does not give you fancy URL creation mechanism like in Django ( where you can pass additional parameters to url ). On the other hand you can modify url function and extend it. I don't want to go into all details here, but here's an example how to use regular expressions ( you should be able to combine these to ideas together ):
Express JS reverse URL route (Django style)
Note that I posted the question, so I had the same problem some time ago. :D
I found express-reverse to nicely solve this issue
https://github.com/dizlexik/express-reverse
It augments the standard routing allowing you to pass the route's name as first argument
app.get('test', '/hello/:x', function(req, res, next) {
res.end('hello ' + req.params.x);
});
Let you build the url from inside a template by name
Test
Let you redirect to an url by name
app.get('/test-redirect', function(req, res, next) {
res.redirectToRoute('test', { x: 'world' });
});
Another option that I don't see here is to just extract the route function out with a function name. If all you are trying to do is writing self-documenting code.
function updateToDo(req, res, next) { /*do router stuff*/};
router.put('/', updateToDo);
I had the same problem and decided to make a library to help me out.
Besides not hardcoding your routes, it allows me to build navigation components like breadcrumbs and I can use it server side with express or client side with Backbone or whatever.
https://github.com/hrajchert/express-shared-routes
I thing this is what you are looking for: Named routes
Example code:
var express = require('express');
var app = express();
var Router = require('named-routes');
var router = new Router();
router.extendExpress(app);
router.registerAppHelpers(app);
app.get('/admin/user/:id', 'admin.user.edit', function(req, res, next){
// for POST, PUT, DELETE, etc. replace 'get' with 'post', 'put', 'delete', etc.
//... implementation
// the names can also be accessed here:
var url = app.namedRoutes.build('admin.user.edit', {id: 2}); // /admin/user/2
// the name of the current route can be found at req.route.name
});
app.listen(3000);
As you can see you can name the route as admin.user.edit and access it in you views
Check this Gist please
var env="http://localhost:3000/"
var route='users/:id/profile/'
var routes=[
{
'name':'profile',
'path':'users/:id/:profile/'
}
]
function RouteName(route,arg){
let targetRoute = routes.find(e=>e.name==route).path
for(var key in arg){
targetRoute=targetRoute.replace(`:${key}`,arg[key])
//console.log(targetRoute)
}
return targetRoute
}
console.log(env+RouteName('profile',{'id':3,'profile':'loaiabdalslam'}))
I began using node.js+express combo very recently and I stumbled at a need to use dynamicHelpers not only in my views but also inside my routes setup (routes/index.js in default express config). Should I use some different pattern?
app.js
app.dynamicHelpers({
translate : function(req, res) {
return translate;
},
language : function(req, res) {
return req.session.language || "en";
},
});
Below I would like to have a convenient access to whatever I set for my dynamicHelpers because in my mind it is the same context .. so why set it up two times?
var routes = {};
routes.index = function(req, res) {
res.render('index', {
title : 'My webpage',
categories : categoryPositions,
referrer : req.header("Referrer"),
languages : ["pl", "en", "de"],
<----- here I would like to use my dynamicHelpers (for example translate)
})
};
I know I can pass my data in many ways but I do not want to repeat my code
and want to set up the common context only once and as cleanly as polssible. I welcome any criticism and good advice!
functions.js
module.exports = {
translate : function(req, res) {
return translate;
},
language : function(req, res) {
return req.session.language || "en";
},
};
helpers.js
var functions = require('./functions');
app.dynamicHelpers({
translate : functions.translate,
language : functions.language
});
Depending on what you need you can also write the helpers like this
var functions = require('./functions');
app.dynamicHelpers( functions );
routes.js
var functions = require('./functions');
var routes = {
index: function(req, res) {
res.render('index', {
title : 'My webpage',
categories : categoryPositions,
referrer : req.header("Referrer"),
languages : ["pl", "en", "de"],
stuff: functions.translate(req, res) // <----- here I would like to use my dynamicHelpers (for example translate)
})
}
};
So actually the solution is to use not yet released express 3.0, modify:
npm install -g express#3.0
Follow https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x , http://www.devthought.com/code/use-jade-blocks-not-layouts/
The new express simplified dynamicHelpers to the use of res.locals which are both available in the routes setup and then bound to the view.
Example:
// app.
app.locals.use(function(req, res) {
var language = req.session.language || "en";
res.locals.language = language;
res.locals.translate = function(clause) {
return translate(clause, language);
};
});
In the routes setup I can now access both res.locals.language and res.locals.translate.
In my views I simply can use translate('something').
So I'm starting to use Node.js. I saw the video with Ryan Dahl on Nodejs.org and heard he recommended Express-js for websites.
I downloaded the latest version of Express, and began to code. I have a fully fledged static view up on /, but as soon as I try sending parameters, I get errors like this:
Cannot GET /wiki
I tried following the guide on expressjs.com but the way one uses routes has changed in the latest version, which makes the guide unusable.
Guide:
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
} else {
next();
}
});
Generated by Express:
app.get('/', routes.index);
My problem arises when I try and add another route.
app.get('/wiki', routes.wiki_show);
I've tried a bunch of approaches, but I keep getting the Cannot GET /wiki (404) error.
routes/index.js looks like this:
exports.index = function(req, res) {
res.render('index', { title: 'Test', articles: articles, current_article: current_article, sections: sections })
};
The only thing I did there was add some parameters (arrays in the same file) and this i working. But when I copy the contents and change exports.index to exports.wiki or exports.wiki_show I still get the Cannot GET /wiki error.
Can anyone explain to me what I'm missing here? - Thanks.
So, after I created my question, I got this related list on the right with a similar issue: Organize routes in Node.js.
The answer in that post linked to the Express repo on GitHub and suggests to look at the 'route-separation' example.
This helped me change my code, and I now have it working. - Thanks for your comments.
My implementation ended up looking like this;
I require my routes in the app.js:
var express = require('express')
, site = require('./site')
, wiki = require('./wiki');
And I add my routes like this:
app.get('/', site.index);
app.get('/wiki/:id', wiki.show);
app.get('/wiki/:id/edit', wiki.edit);
I have two files called wiki.js and site.js in the root of my app, containing this:
exports.edit = function(req, res) {
var wiki_entry = req.params.id;
res.render('wiki/edit', {
title: 'Editing Wiki',
wiki: wiki_entry
})
}
The route-map express example matches url paths with objects which in turn matches http verbs with functions. This lays the routing out in a tree, which is concise and easy to read. The apps's entities are also written as objects with the functions as enclosed methods.
var express = require('../../lib/express')
, verbose = process.env.NODE_ENV != 'test'
, app = module.exports = express();
app.map = function(a, route){
route = route || '';
for (var key in a) {
switch (typeof a[key]) {
// { '/path': { ... }}
case 'object':
app.map(a[key], route + key);
break;
// get: function(){ ... }
case 'function':
if (verbose) console.log('%s %s', key, route);
app[key](route, a[key]);
break;
}
}
};
var users = {
list: function(req, res){
res.send('user list');
},
get: function(req, res){
res.send('user ' + req.params.uid);
},
del: function(req, res){
res.send('delete users');
}
};
var pets = {
list: function(req, res){
res.send('user ' + req.params.uid + '\'s pets');
},
del: function(req, res){
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
}
};
app.map({
'/users': {
get: users.list,
del: users.del,
'/:uid': {
get: users.get,
'/pets': {
get: pets.list,
'/:pid': {
del: pets.del
}
}
}
}
});
app.listen(3000);
Seems that only index.js get loaded when you require("./routes") .
I used the following code in index.js to load the rest of the routes:
var fs = require('fs')
, path = require('path');
fs.readdirSync(__dirname).forEach(function(file){
var route_fname = __dirname + '/' + file;
var route_name = path.basename(route_fname, '.js');
if(route_name !== 'index' && route_name[0] !== "."){
exports[route_name] = require(route_fname)[route_name];
}
});
You could also organise them into modules. So it would be something like.
./
controllers
index.js
indexController.js
app.js
and then in the indexController.js of the controllers export your controllers.
//indexController.js
module.exports = function(){
//do some set up
var self = {
indexAction : function (req,res){
//do your thing
}
return self;
};
then in index.js of controllers dir
exports.indexController = require("./indexController");
and finally in app.js
var controllers = require("./controllers");
app.get("/",controllers.indexController().indexAction);
I think this approach allows for clearer seperation and also you can configure your controllers by passing perhaps a db connection in.
No one should ever have to keep writing app.use('/someRoute', require('someFile')) until it forms a heap of code.
It just doesn't make sense at all to be spending time invoking/defining routings. Even if you do need custom control, it's probably only for some of the time, and for the most bit you want to be able to just create a standard file structure of routings and have a module do it automatically.
Try Route Magic
As you scale your app, the routing invocations will start to form a giant heap of code that serves no purpose. You want to do just 2 lines of code to handle all the app.use routing invocations with Route Magic like this:
const magic = require('express-routemagic')
magic.use(app, __dirname, '[your route directory]')
For those you want to handle manually, just don't use pass the directory to Magic.