URL Generation for Routes in Express - node.js

I'm considering using Express framework in my next node.js project. However, a stumbling block for me is non-existence of URL generation for routes like in most other non-Sinatra based frameworks, examples- Django, Flask, Rails etc.
I tried looking for some Connect middleware to serve my task and I did find Barista, Escort, Sherpa and the likes but looking at their GitHub pages, all seem dead and in-active. So, I don't want to go for something that's not maintained anymore for obvious reasons.
My main concern here is that the project may get really large and it WILL be a pain to update URLs in every page whenever business and/or aesthetic requirements change.
Is there something that I failed to see in the docs/tests? If not, then how do I extend the routing framework in Express to do URL generation and make this wrapper available in my views as well as controller functions?
UPDATE: (22/3/2012) I found this page: https://github.com/clyfe/tweet_express/wiki/TODO which specified some routers that do URL generation and stumbled upon the escort router which can also interface with express.

Or stick with express and use the package reversable-router.
Example from the readme:
app.get('/admin/user/:id', 'admin.user.edit', function(req, res, next){
//...
});
//.. and a helper in the view files:
url('admin.user.edit', {id: 2})

You might try Locomotive, which is built on Express.
It does much more than route generation. From the docs: "Locomotive brings additional MVC-based structure, for architecting larger applications, while leveraging the power of Express and Connect middleware."
Locomotive's router generates helpers that are automatically available to controllers and views.

From #freakish's answer:
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

Related

Node express api routes for multilingual directory like url

Does any one knows an example or could explain here how node.js and express would have to route for a multilanguage site? I'm using i18n-node for translation and folder like routing ( /es/, /de/ , etc ) for different languages. This all are static routes but I also have routes like apiRoutes.route('/user/profile') using 'app' at the begining ( app.get('/app/user/profile') so please consider this in your answer so is NOT necesary route to : app.get('/es/app/user/profile') .
having 15 routes like this now:
app.get('/terms', function(req, res) {
res.render('terms',{
...
});
});
how it have to be set for routes like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Should I duplicate this routes and add for example a locale for
each like:
app.get('/es/terms', function(req, res) {
res.render('terms',{
...
});
});
Or Should do something like:
if cookie['lang'] && cookie['lang'] is in locales
// then redirect to /:lang/terms
else
// show default language in /terms
if req.headers["accept-language"] && req.headers["accept-language"]
// then redirect to /:lang/terms
else
//show default language in /terms
Or there is another way I should approach this that follows good practices or is better respecting standards?
Miro's Answer in :
How can I get the browser language in node.js (express.js)? says I should use app.all('*', ...
Is this all I need?, ..still, it might have a syntax error or i'm not understanding well this two parts
var rxLocal = /^\/(de|en)/i;
...
app.get(/\/(de|en)\/login/i, routes.login);
thanks in advance
You need to consider 2 things :
1. How get the local :
Accept-Language
The HTTP protocole define the Accept-Language header to manage the local. This is a normalized method. You can access it with the req.acceptsLanguages method of express.
+Normalized
+Natively support by brower
-Not easy to by passe by the end user
Path / Cookies
You can get the local from the path. In express it can be do with a parameter patter like /:local/rest/of/path and retrieve in the request object with the req.param method.
You can also get the information from the cookies with the req.cookies properties (don't forgot to set it).
Both
To increase the user experience you can mix the both method. For exemple get the default language from the HTTP header send by the browser but permite to the user to override this in you application and store this parameter in the cookies.
2. Use the local:
Each methods to get the local can be used from different way. I will
use random of them in exemple but they are all compatible.
Top level configuration.
In case of you use a template Engine and you controller can be local agnostic. You can use a middleware to get the local information and configure the render engine.
app.use('/:local' (req, res, next) => {
let localKey = req.param('local');
res.locals = // Some ingenious method to get the locales from localKey
next();
}
Check res.locals and your engine documentation.
Use it in controller.
If the local is part of the contoller process. You can get directly is value in controller.
In case of you use a complexe method to determine the final value of the local, you can also use a middleware to determine this value and enrich the request with it.
app.use((req, res, next) => {
let local = req.cookies.local;
if(!local) local = req.acceptsLanguages();
if(!local) local = 'en-US';
req.local = local;
}
Both
You can use both method too. It depend of what you need. Find the best way to get a maintainable code and avoid replication for your use case.
When you use middle where witch impact the controllers, be sure you declare them before your routes.
You can use a route parameter to get the locale from the URL, like this:
app.get('/:lang/terms', function (req, res) {
if (req.params === 'es') {
res.send('¡Hola!');
else {
res.send('Hi!');
}
});
The colon character tells Express to put whatever is between the first to slashes of the path in req.params.lang.
See express routing documentation for details.

How to serve rendered Jade pages as if they were static HTML pages in Node Express?

Usually you render a Jade page in a route like this:
app.get('/page', function(req, res, next){
res.render('page.jade');
});
But I want to serve all Jade pages (automatically rendered), just like how one would serve static HTML
app.use(express.static('public'))
Is there a way to do something similar for Jade?
"static" means sending existing files unchanged directly from disk to the browser. Jade can be served this way but that is pretty unusual. Usually you want to render jade to HTML on the server which by definition is not "static", it's dynamic. You do it like this:
app.get('/home', function (req, res) {
res.render('home'); // will render home.jade and send the HTML
});
If you want to serve the jade itself for rendering in the browser, just reference it directly in the url when loading it into the browser like:
$.get('/index.jade', function (jade) {
//...
});
https://github.com/runk/connect-jade-static
Usage
Assume the following structure of your project:
/views
/partials
/file.jade
Let's make jade files from /views/partials web accessable:
var jadeStatic = require('connect-jade-static');
app = express();
app.configure(function() {
app.use(jadeStatic({
baseDir: path.join(__dirname, '/views/partials'),
baseUrl: '/partials',
jade: { pretty: true }
}));
});
Now, if you start your web server and request /views/partials/file.html in browser you
should be able see the compiled jade template.
Connect-jade-static is good, but not the perfect solution for me.
To begin with, here are the reasons why I needed jade:
My app is a single page app, there are no HTMLs generated from templates at runtime. Yet, I am using jade to generate HTML files because:
Mixins: lots of repeated / similar code in my HTML is shortened by the use of mixins
Dropdowns: I know, lots of people use ng-repeat to fill the options in a select box. This is a waste of CPU when the list is static, e.g., list of countries. The right thing to do is have the select options filled in within the HTML or partial. But then, a long list of options makes the HTML / jade hard to read. Also, very likely, the list of countries is already available elsewhere, and it doesn’t make sense to duplicate this list.
So, I decided to generate most of my HTML partials using jade at build time. But, this became a pain during development, because of the need to re-build HTMLs when the jade file changes. Yes, I could have used connect-jade-static, but I really don’t want to generate the HTMLs at run time — they are indeed static files.
So, this is what I did:
Added a 'use' before the usual use of express.static
Within this, I check for the timestamps of jade and the corresponding html file
If the jade file is newer, regenerate the html file
Call next() after the regeneration, or immediately, if regeneration is not required.
next() will fall-through to express.static, where the generated HTML will be served
Wrap the ‘use’ around a “if !production” condition, and in the build scripts, generate all the HTML files required.
This way, I can also use all the goodies express.static (like custom headers) provides and still use jade to generate these.
Some code snippets:
var express = require('express');
var fs = require('fs')
var jade = require('jade');
var urlutil = require('url');
var pathutil = require('path');
var countries = require('./countries.js');
var staticDir = 'static'; // really static files like .css and .js
var staticGenDir = 'static.gen'; // generated static files, like .html
var staticSrcDir = 'static.src'; // source for generated static files, .jade
if (process.argv[2] != 'prod') {
app.use(‘/static', function(req, res, next) {
var u = urlutil.parse(req.url);
if (pathutil.extname(u.pathname) == '.html') {
var basename = u.pathname.split('.')[0];
var htmlFile = staticGenDir + basename + '.html';
var jadeFile = staticSrcDir + basename + '.jade';
var hstat = fs.existsSync(htmlFile) ? fs.statSync(htmlFile) : null;
var jstat = fs.existsSync(jadeFile) ? fs.statSync(jadeFile) : null;
if ( jstat && (!hstat || (jstat.mtime.getTime() > hstat.mtime.getTime())) ) {
var out = jade.renderFile(jadeFile, {pretty: true, countries: countries});
fs.writeFile(htmlFile, out, function() {
next();
});
} else {
next();
}
} else {
next();
}
});
}
app.use('/static', express.static(staticDir)); // serve files from really static if exists
app.use('/static', express.static(staticGenDir)); // if not, look in generated static dir
In reality, I have a js file containing not just countries, but various other lists shared between node, javascript and jade.
Hope this helps someone looking for an alternative.

Multi-language routes in express.js?

I'm wondering if there is a best practise example on how to implement multi-lanuage routes in express.js. i want to use the accept-language header to get the browser language and then redirect automatically to the corresponding language route like
www.foo.bar/de/startseite OR
www.foo.bar/en/home
Any advice on this?
i have done the following:
install i18n-node modul and register in the express js. here is code.
var express = require('express')
, routes = require('./routes')
, http = require('http')
, i18n = require("i18n");
var app = express();
i18n.configure({
// setup some locales - other locales default to en silently
locales:['de', 'en'],
// disable locale file updates
updateFiles: false
});
app.configure(function(){
...
app.use(i18n.init);
...
});
// register helpers for use in templates
app.locals({
__i: i18n.__,
__n: i18n.__n
});
after this set the following to get all request
// invoked before each action
app.all('*', function(req, res, next) {
// set locale
var rxLocal = /^\/(de|en)/i;
if(rxLocal.test(req.url)){
var arr = rxLocal.exec(req.url);
var local=arr[1];
i18n.setLocale(local);
} else {
i18n.setLocale('de');
}
// add extra logic
next();
});
app.get(/\/(de|en)\/login/i, routes.login);
maybe this help.
I'd just serve up the content in the detected language directly.
For example, example.com/home serves up the home page in the best available Accept-Language (possibly overridden by cookie if you provide a language selection option on the site itself).
You'd want to make sure that your response's Vary: header includes Accept-Language.
IMO, including language codes in the URI is an ugly hack. The RFC's intent is that a single resource (your home page) is universally represented by a single URI. The entity returned for a URI can vary based on other information, such as language preferences.
Consider what happens when a German-speaking user copies a URL and sends it to an English-speaking user. That recipient would prefer to see your site in English, but because he has received a link that points to example.com/de/startseite, he goes straight to the German version.
Obviously, this isn't ideal for full internationalization of what the user sees in the address bar (since home is English), but it's more in line with the RFCs' intent, and I'd argue it works better for users, especially as links get spread around email/social/whatever.
Middleware recommendation
The answer by #miro is very good but can be improved as in the following middleware in a separate file (as #ebohlman suggests).
The middleware
module.exports = {
configure: function(app, i18n, config) {
app.locals.i18n = config;
i18n.configure(config);
},
init: function(req, res, next) {
var rxLocale = /^\/(\w\w)/i;
if (rxLocale.test(req.url)){
var locale = rxLocale.exec(req.url)[1];
if (req.app.locals.i18n.locales.indexOf(locale) >= 0)
req.setLocale(locale);
}
//else // no need to set the already default
next();
},
url: function(app, url) {
var locales = app.locals.i18n.locales;
var urls = [];
for (var i = 0; i < locales.length; i++)
urls[i] = '/' + locales[i] + url;
urls[i] = url;
return urls;
}
};
Also in sample project in github.
Explanation
The middleware has three functions. The first is a small helper that configures i18n-node and also saves the settings in app.locals (haven't figured out how to access the settings from i18n-node itself).
The main one is the second, which takes the locale from the url and sets it in the request object.
The last one is a helper which, for a given url, returns an array with all possible locales. Eg calling it with '/about' we would get ['/en/about', ..., '/about'].
How to use
In app.js:
// include
var i18n = require('i18n');
var services = require('./services');
// configure
services.i18nUrls.configure(app, i18n, {
locales: ['el', 'en'],
defaultLocale: 'el'
});
// add middleware after static
app.use(services.i18nUrls.init);
// router
app.use(services.i18nUrls.url(app, '/'), routes);
Github link
The locale can be accessed from eg any controller with i18n-node's req.getLocale().
RFC
What #josh3736 recommends is surely compliant with RFC etc. Nevertheless, this is a quite common requirement for many i18n web sites and apps, and even Google respects same resources localised and served under different urls (can verify this in webmaster tools). What I would recommended though is to have the same alias after the lang code, eg /en/home, /de/home etc.
Not sure how you plan on organizing or sharing content but you can use regular expressions with express routes and then server up different templates. Something like this:
app.get(/^\/(startseite|home)$/, function(req, res){
});
One thing that I did was to organize my content with subdomains and then use middleware to grab the content out of the database based splitting the url, but they all shared the same routes and templates.
Write a middleware function that parses any "Accept-Language" headers and sets a request-level local variable to an appropriate code (like a two-letter language code) with a default value (like "en") if there are no such headers or you don't support any language listed. In your routes, retrieve the local and tack it on to any template file names, and branch on it if there's any language-dependent processing other than template selection.

Connect and Express utils

I'm new in the world of Node.js
According to this topic: What is Node.js' Connect, Express and “middleware”?
I learned that Connect was part of Express
I dug a little in the code, and I found two very interesting files :
./myProject/node_modules/express/lib/utils.js
and better :
./myProject/node_modules/express/node_modules/connect/lib/utils.js
These two files are full of useful functions and I was wondering how to invoke them correctly.
As far, in the ./myProject/app.js, that's what I do:
var express = require('express')
, resource = require('express-resource')
, mongoose = require('mongoose')
, expresstUtils =
require('./node_modules/express/lib/utils.js');
, connectUtils =
require('./node_modules/express/node_modules/connect/lib/utils.js');
But I found it a little clumsy, and what about my others files?
e.g., here is one of my routes:
myResources = app.resource(
'myresources',
require('./routes/myresources.js'));
and here is the content of myresources.js:
exports.index = function(req, res)
{
res.render('./myresources.jade', { title: 'My Resources' });
};
exports.show = function(req, res)
{
fonction resourceIsWellFormatted(param)
{
// Here is some code to determine whether the resource requested
// match with the required format or not
// return true if the format is ok
// return false if not
}
if (resourceIsWellFormatted(req.params['myresources']))
{
// render the resource
}
else
{
res.send(400); // HEY! what about the nice Connect.badRequest in its utils.js?
}
};
As you can see in the comment after the res.send(400), I ask myself if it is possible to use the badRequest function which is in the utils.js file of the Connect module.
What about the nice md5 function in the same file?
Do I have to place this hugly call at the start of my myresources.js to use them?:
var connectUtils =
require('../node_modules/express/node_modules/connect/lib/utils.js');
or, is there a more elegant solution (even for the app.js)?
Thank you in advance for your help!
the only more elegant way i came up with is (assuming express is inside your root "node_modules" folder):
require("express/node_modules/connect/lib/utils");
the node installation is on windows, node version 0.8.2
and a bit of extra information:
this way you don't need to know where you are in the path and be forced to use relative paths (./ or ../), this can be done on any file nesting level.
i put all my custom modules inside the root "node_modules" folder (i named my folder "custom_modules") and call them this way at any level of nesting:
require("custom_modules/mymodule/something")
If you want to access connect directly, I suggest you install connect as a dependency of your project, along with express. Then you can var utils = require('connect').utils.

How to Make a Call to Koa.js App Instance for Unit Tests

I don't know how I'd term this maybe 'static call to a koa router'? Does that seem like the right wordage here for what I'm really trying to accomplish if you were to talk about it technically?
Anyway, I'm using koa-router and I'm coding unit tests (not integration tests). So I do not want to invoke .listen() on my koa app because of that reason...it would create an http server which now makes my test an integration tests.
Instead in my test I simply want to make a straight call to the app object instance and call a route and be able to return no results and check that I returned no results in the response.
How can you do that? I can't find an example and I've tried all sorts of pseudo code attemps agains the koa app object.
If you want to test the function that koa-router routes to then just perform a unit test on that function and leave the routing out of it.
To me it sounds like you've got a file such as app.js and it contains all your code. What you can do is create a router.js file to put you route bindings and a services.js file where you can put your application logic.
So for example app.js might look like:
var koa = require("koa");
var app = module.exports = koa();
var router = require('./router.js');
app.use(router.unsecured.middleware());
app.listen(3000);
And router.js might look like:
var router = require("koa-router");
var service = require("./services.js");
var unsecured = module.exports.unsecured = new router();
unsecured.post('/account/signin', service.signinUser);
unsecured.post('/account/register', service.registerUser);
And services.js might look like:
module.exports.signinUser = function*(signinDetails) {
// contains your application signin logic
};
module.exports.registerUser = function*(registerDetails) {
// contains your application register logic
};
So in this manner you can individually test services.js. I don't see any value in individually testing router.js since it is so trivial. As #Dan Pantry shows you can test routing as part of an integration test using supertest.
Edit:
So this is a little experimental test I was playing around with to test that the routing is correct. I'm using mocha as the test runner and the code example I posted in my original code.
// standard library
var assert = require("assert");
// in app objects
var router = require('./router.js');
var service = require('./service.js');
describe("routing tests", function() {
it("test register routing, POST", function*(done) {
// arrange
var unsecured = router.unsecured;
var path = '/account/register';
var httpMethod = 'POST';
var expected = service.register.toString();
var actual;
// act
for (var i = 0; i < unsecured.stack.length; i++)
{
var pathMatch = unsecured.stack[i].path === path;
var methodMatch = unsecured.stack[i].methods.indexOf(httpMethod) >= 0;
if (pathMatch && methodMatch)
{
actual = unsecured.stack[i].middleware.toString();
break;
}
}
// assert
try {
assert.equal(expected, actual);
done();
} catch(err) {
done(err);
}
});
});
There is probably a neater way of doing this (and a more modular way for testing multiple paths) but as I said this is just a basic example to verify the routing is calling the correct service. What I'm doing is delving into the koa-router object to verify what path is bound to what service code depending on the HTTP method (e.g. POST, GET, etc).
If you have your routing and your services in modules this test completely avoids dealing with the main koa app. Although technically this test spans multiple units (the routing and the service code) so it would technically be an integration test but it does mean you don't go near app.listen() which is what you didn't want to call in your tests.

Resources