Using passport local authentication, login works, I click getStatus button and it works, then logout works. But after logout, I click BACK in the browser, it can still display the full content of getStatus. The console log in isAuthenticated() still says "you are logged in". This is the simplified code:
var express = require('express');
var passport = require('passport');
var net = require('net');
var bodyParser = require('body-parser');
var http = require('http');
var multer = require('multer');
var cp = require('child_process');
var exec = require('child_process').exec;
var sys = require('sys');
var path = require('path');
var util = require('util');
var session = require('express-session');
var crypto = require('crypto');
var sqlite3 = require('sqlite3');
/////////////////////////////////////////////////
var LocalStrategy = require('passport-local').Strategy;
var db = new sqlite3.Database('./myPassword.db');
passport.use(new LocalStrategy(function(username, password, done)
{
console.log("step 2: Client sent you user: " + username + " password: " + password);
db.get('SELECT slat FROM users WHERE username = ?', username, function(err, row)
{
if (!row) return done(null, false);
console.log("step 4");
db.get('SELECT username, id FROM users WHERE username = ? AND password = ?',
username, password, function(err, row)
{
console.log("step 6");
if (!row) return done(null, false);
console.log("step 8");
return done(null, row);
});
});
}));
passport.serializeUser(function(user, done) {
return done(null, user.id);
});
passport.deserializeUser(function(id, done) {
db.get('SELECT id, username FROM users WHERE id = ?', id, function(err, row)
{
if (!row)
return done(null, false);
return done(null, row);
});
});
/////////////////////////////////////////////////
var isAuthenticated = function(req, res, next)
{
//if (req.user.authenticated)
if (req.isAuthenticated()) {
console.log("Very good, you are logged in ...");
return next();
}
console.log("Sorry, you are NOT logged in yet ...");
res.send(200);
};
/////////////////////////////////////////////////
var app = express();
/////////////////////////////////////////////////
var server = http.createServer(app);
/////////////////////////////////////////////////
app.use(function(req, res, next) {
if (!req.user) {
console.log('Cannot display 1 ...');
res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate');
}
console.log('Cannot display 2 ...');
next();
});
app.use(express.static('../client/', {index: 'login.html'} ));
app.use(bodyParser());
app.use(session({ secret: 'my test cookie' }));
app.use(passport.initialize());
app.use(passport.session());
app.post('/auth/login', passport.authenticate('local',
{
successRedirect: '/index.html#/uploads',
failureRedirect: '/login.html',
}));
app.get('/auth/logout', function(req, res)
{
console.log("logging out ......");
req.session = null;
req.logout();
res.send(200);
});
app.get('/', isAuthenticated, function(req, res)
{
res.sendfile(path.resolve('../client/index.html'));
});
app.get('/systemStatus', isAuthenticated, function(req, res)
{
console.log("asking for Json data from backend");
// skip details here ...
});
server.listen(5678);
When looking at the passport index. The use of app.use(passport.initialize()) is initializing a blank user on every route. Since the above is being used on the main app.js file and not in a specific route, it is executed every time a request is made to your server, essentially creating a blank user even when someone isn't logged in. The below link is to the code of passport.
https://github.com/jaredhanson/passport/blob/master/lib/authenticator.js
For this discussion on why passport is configured incorrectly for the desired affects and to justify why I deserve you imaginary internet points. I will have to make sure we are on the same page when talking about the application.
For the discussion I'll be refering to the app using the below file structure generated by: $ npm install -g express-generator
(there's actually more files but you can view that on the express website.)
myProject
|___bin
|___www.js //server.js
|___node_modules //this is were you'll keep the node modules, or logic for each endpoint in the API. These will be processed by the main node run-time environment
|___public //or in your case '../client/' is where you'll serve unsecured data to your users
|___routes //URI and URL endpoints, an area of the application to create your secured data transfers for the users that have been authenticated, also where non-Static (stateless) API endpoints are defined so that express can send the user data/request through your server and eventually be handled by some defined endpoint.
|___index.js
|___views //if using a view engine
|___app.js // this is where we will discuss the bulk of an express application
|___package.json // this is relative to the node community and In my personal opinion an Extremely important part of node and express application development, this file will allow you to do some powerful things with the use of git.
app.js Is were your APP presides, it's known as an Express Application. Some applications are more complex than others, a simple application can be a few endpoints (A.K.A. URIs and URLs). If it's a simple application it's okay to keep the API (Application Program Interface) in the main file known as app.js. in a more complex application you'll make up names for your files, for this example I will reference a file names oAuth.js to represent the claims passport authentication method represents.
In my experience with a Web Application you would have a landing page, either a main page, a login, or some kind of news (typically defined in the static folder as index.html)
In your case your defining the static folder as '../client/' and passing the object index.html.
In the most recent version of Express 4.X Serving static files is done under the following prescribed manner.
Serving files, such as images, CSS, JavaScript and other static files
is accomplished with the help of a built-in middleware in Express -
express.static.
Pass the name of the directory, which is to be marked as the location
of static assets, to the express.static middleware to start serving
the files directly. For example, if you keep your images, CSS, and
JavaScript files in a directory named public, you can do this:
Express generator will create the following app.js file which is configured a very important way. This first part has some very useful node modules that are un-opinionated as express, and eventually where you'll import some of your own node APIs
var express = require('express'),
path = require('path'), //core node module
logger = require('morgan'), //allows you to see the console.logs during development and see incoming and outgoing messages to the server. It also displays `console.log()` defined in any node_module, express route, and express app file.
cookieParser = require('cookie-parser'), //helps when trying to handle cookies
bodyParser = require('body-parser'); //helps when parsing certain types of data
routes are like mini express applications, remember when we first discussed how some applications can become more complex than others? This is how you manage the complexity so your application can grow and flourish. Assuming you wish to add new features for your loving and wonderful users.
var route = require('.routes/index',// this is importing the the logic of the URI and URL enpoint to this file. It will be referenced as the second argument in the route and configuration references below.
oAuth = require('.routes/oauth') // this is the file where you'll place all of the passport logic you have previously wrote. Any other authenticated routes need to be defined in this file.
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'); //alot of people are also using EJS equally if not more
Now setup the basic middlewares provided by express-generator
app.use(logger('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public'))); //this style declaration ensures you hit what ever directory you want to by changing public as long as that directory exists.
route URI and URL endpoints being defined by you to -->express. It takes the form of strings and a reference to it's route at the top of this file
app.use('/', routes); //unsecured landing page
this express object usage will allow you to define APIs, authorized and unAuthorized routes. This is where you will declare your reference to the authenticated portion of the Express Application. Any portion of the application declared above app.use('/auth/',oAuth) will NOT be authenticated. Any portion declared below the /auth/ portion of your URIs and URLs. will be authenticated.
app.use('/auth/', oAuth);
some extras that the express generator will place in the app file which are extremely useful.
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
Because you are polluting your applications namespace with un-needed complexity it is inadvertently causing you undesired authentication affects. This goes deeper since it's relative to formatting and configuring your application program interface and how javascript files are executed in the Node run-time environment, as well as how the express application framework is meant to be used and configured when building complex applications that require authentication to access.
Now back to your question of why you keep getting an authenticated user although no one is logged in? It's because your use of app.use('some string or some middleware'). To fix the issue remove all of your authentication processing and move it to a route. In the above example it's referenced as oAuth.js. Define any routes that need authentication behind the passport middleware.
Now because your question is also regarding node and you mentioned in the comments that you are part of a scrum it's important to state that all of this information is contained on the express website which is where i initially linked my answer. Despite me telling you that you need a route and that passport was configured incorrectly. So any inflammatory comments "read the manual" are made because I feel that you didn't even investigate the link I sent in my initial answer, nor did you read any other part of the express frame work and their website. If you plan to understand any node_modules and complex frame works its equally important to read about them and do their tutorials, actually go through the node_modules when there is a getting started and/or they have API references. By blasting into application development without trying any portion of the framework basics will just make you spend more time coding bad breakable code. It will slow your development significantly if you aren't understanding how the node_modules function. The best way to understand their functionality is by reading them. That's all for my rant/advice for learning how to develop a web application. Ultimately you'll be able to reuse a lot of your tutorial code in a application.
Related
I'm developing part of a system where we have two applications sharing the same domain so nginx makes exampleurl.com go to one application and example.com/admin/* go to the second.
The /admin/* part is going to a NodeJs app using express.
Is there an elegant way of making sure that node can add in the /admin without having to do
app.get('/admin/endpoint', ...)
?
you can use http://expressjs.com/fr/api.html#router
var router = express.Router();
router.get('/', function(req, res) {
res.send('One page in admin website');
});
// router.get('/adminWebsite'); // idem for all routes(you always use this router)
app.use('/admin', router);
you would would to use routers (preferably) but you can also create another express app. using different Apps allow you to have more global middlewear control whereas using different routes mean that they share the same express instance.
var express = require('express');
var admin = express(); // <- this is now your admin application
var app = express(); // <- this is your main applicaiton regular users
//you will need to set up middlewear like body-parser and stuff for both of them now
//but it allows you to use different logging and authentication system or w.e you
//want
//once everything is done you can 'MOUNT' virtually the admin app to the regular app
app.use('/admin', admin); //<- this will nest apps together but allow the sub-app admin
//to be it's own instance.
//app.js
const admin = require('admin'); //you admin module (where you routes wiil be)
app.use('/admin', [functions... example authCheck], admin);
//index file of admin module
`var express = require('express');
var router = express.Router();
router.use(function authCheck(req, res, next) {
//check if user is logged
});
router.get('/main', require('./main').get);
module.exports = router;`
//main.js
`exports.get = function(req, res, next){
res.render('admin/admin', {
title: "Main panel"
});
};`
and now you can access site.com/admin/main
In my express app I've set static files to be served from the /public directory with this line:
app.use(express.static(__dirname + '/public'));
Now I need to add a middleware for authentication before serving the static content and if the user is not authenticated to be redirected to a route for authentication (e.g., /login).
I'm not really sure how I have to do it. Any ideas?
Since you didn't specify it, I'm going to assume that you already have some kind of authentication system.
In Express, the order of the middlewares in the code matters: if you want to have middleware 1 executed before middleware 2, you should place them accordingly in your code. Since express.static is a middleware, if you want authentication before serving your static files you can simply write your authentication middleware before the call to express.static
app.use(function (req, res, next) {
if (!userAuthenticated(req)) {
return res.redirect('/login');
}
next();
});
app.use(express.static(__dirname + '/public'));
I am assuming you have a userAuthenticated function which is for instance checking if the HTTP requests contains a valid access-token.
Read more about middlewares.
Check out Passport.
Passport has many authentication strategies.
Here's an example with basic HTTP authentication:
var express = require('express');
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var db = require('./db');
// Configure the Basic strategy for use by Passport.
//
// The Basic strategy requires a `verify` function which receives the
// credentials (`username` and `password`) contained in the request. The
// function must verify that the password is correct and then invoke `cb` with
// a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use(new BasicStrategy(
function(username, password, cb) {
db.users.findByUsername(username, function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false); }
if (user.password != password) { return cb(null, false); }
return cb(null, user);
});
}));
// Create a new Express application.
var app = express();
var authenticate = passport.authenticate('basic', {
session: false,
failureRedirect: '/login'
});
app.use(authenticate, express.static(__dirname + '/public'));
Depends on what kind of authentication you are looking for, but if you just want some login-feature, this is what you need: http://passportjs.org/
It has support for local login strategies, as well as a whole bunch of 3rd party strategies like facebook, twitter, etc.
If you need something else, simpler or self-made, just write a middleware to use before you declare the static endpoint, and call next() if everything checks out, and res.redirect if user needs to retry.
Think what I am trying to do should be relatively easy, but I am loosing the thread, and potentially the will to do this.
Setting up a node application using node and express 4. And I use passport for authentication. Followed an absolutely amazing guide by scott.io which did the trick nicely https://scotch.io/tutorials/easy-node-authentication-setup-and-local
And it works a charm. However, I want to separate my routes, because I like keeping things tidy (thats a lie, but I intend to keep the lie living).
My plan was to have four sets of routes.
api (mapped to /api, using the file ./routes/api.js)
index (mapped to /, using the file ./routes/index.js)
auth (mapped to /auth, keeps track of all authentication, callbacks as well as some activator and other bits)
Now to my issue, I need to make the passport available to app (or get api.js and indes.js to be able to call functions in passport.js) and I can't quite figure out how.
My plan was to initiate passport like so:
var passport = require('passport');
app.use(session({secret: 'Not-telling-you)',
saveUninitialized: true,
resave: true
})); // session secret
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
//Configuring the passports
require('./config/passport')(passport);
That should give me passport available in app
Next to load the route modules
var auth = require('./routes/auth')(app, passport);
var users = require('./routes/users')(app,passport);
var activator = require('./routes/activator')(app,passport);
This SHOULD allow me to access them in the modules?
Map all toutes in app
app.use('/api', api);
app.use('/auth', auth);
app.use('/', index);
And then write the modules as follows (this is a super simple version of auth)
var bodyParser = require('body-parser');
var activator = require('activator');
var express = require('express');
var router = express.Router();
//Lets read the configuration files we need
var activatorCfg = require('../config/activator.js')
var cfgWebPage = require('../config/webpage.js');
//So we can read the headers easily
router.use(bodyParser.json()); // support json encoded bodies
router.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
//Activating activator, so we can actively activate the actives
activator.init({user: activatorCfg, transport: activatorCfg.smtpUrl , from: activatorCfg.fromEmail, templates: activatorCfg.templatesDir});
router.get('/login', function(req, res) {
res.render('login.ejs', { title: 'Betchanow - Social betting as it should be' , loginUrl: cfgWebPage.loginUrl, trackingID: cfgWebPage.googleTracking.trackingID, message: req.flash('loginMessage') });
});
module.exports=function(app, passport) {
router
}
My problem is that if I do that, express complains that
throw new TypeError('Router.use() requires middleware function but got a
^
TypeError: Router.use() requires middleware function but got a undefined
If I just return the router (skip wrapping it in a function) I end up with a
var search = 1 + req.url.indexOf('?');
^
TypeError: Cannot read property 'indexOf' of undefined
So is there a right, simple or preferably right and simple way of achieving this?
Think the trick would be to pass app and passport (or only passport), think is I need access to either data or functions from passport in all three, and as I was planning to play with ACL as well, wanted to add that to auth to make my life simple as well.
============== EDIT =============
So here is my issue.
If I now do a post to the authentication route (code below)
//Lets load the modules, note the missing passport
var bodyParser = require('body-parser');
var activator = require('activator');
var express = require('express');
var router = express.Router();
//Lets read the configuration files we need
var activatorCfg = require('../config/activator.js')
var cfgWebPage = require('../config/webpage.js');
//So we can read the headers easily
router.use(bodyParser.json()); // support json encoded bodies
router.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
//Activating activator, so we can actively activate the actives
activator.init({user: activatorCfg, transport: activatorCfg.smtpUrl , from: activatorCfg.fromEmail, templates: activatorCfg.templatesDir});
//Lets start with our routes
// process the login form
router.post('/login', passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
module.exports=function(app, passport) {
return router;
}
I end up with the issue that the route code (./routes/auth.js) have no clue what passport is. (loded in the app as follows):
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
app.use(flash()); // use connect-flash for flash messages stored in session
//Configuring the passports
require('./config/passport')(passport);
You'll be getting the error because you're not returning the router.
module.exports=function(app, passport) {
return router;
}
EDIT:
You won't be able to access the passport property because you're not passing it around or setting it anywhere. As I'm not sure how passport works (whether it acts as a singleton or not), so you have a couple of options in your routes file:
var passport = require('passport')
which may "just work", or
var passport; // at the top of your routes file
// your routes
module.exports = function(app, _passport) {
passport = _passport;
return router;
}
A third option is to wrap your entire routes in the exports method:
// your requires here
module.exports = function(app, passport) {
//So we can read the headers easily
router.use(bodyParser.json()); // support json encoded bodies
router.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
//Activating activator, so we can actively activate the actives
activator.init({user: activatorCfg, transport: activatorCfg.smtpUrl , from: activatorCfg.fromEmail, templates: activatorCfg.templatesDir});
//Lets start with our routes
// process the login form
router.post('/login', passport.authenticate('local-login', {
successRedirect : '/', // redirect to the secure profile section
failureRedirect : '/login', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
return router;
}
I am an intermediate level developer on MEAN Stack, but I wanted to understand this, as to what exactly happens after I press run node app.js. After reading a lot, this is what I found out. Correct me if I am wrong.
Step 1 : All of the JavaScript code written is complied and looked for syntactic errors. If that is error free, the node starts to listen on the mentioned port. By listening I mean in layman languages is, it kinda opens a door where anything could come from that door.
Step 2: All the routes are listed somewhere like these are GET, these are POST request etc. A guard on that door will check the incoming request, and see if it matches any of the routes. If the HTTP header and routes and permissions(CORS) are matched, the guard gives it permission to go and execute that particular route, and then the response is sent back through the same door, with the required fields like status, data etc.
Mid-Step: When the request is being processed, all the IO tasks are given to underlying threads by libev and when they complete, they come back to the event table and then are processed by the event loop.
Is this understanding correct? Would somebody like to add something to it. Probably something that I am missing.
EDIT
/*Writing var once and declaring all dependencies at once is considered a
good practice.*/
var express = require('express'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
passport = require('passport'),
session = require('express-session');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
/*All my confidential data, keys go in here*/
require('./config/credentials.js').db(app, mongoose);
require('./config/environment.js')(app, express, bodyParser, cookieParser, session, passport, mongoose);
/*Models*/
Users = require('./models/users.js');
var checkauth = function(req, res, next) {
console.log("In checkauth");
console.log(req.user);
if (req.isAuthenticated()) {
console.log("In isAuthenticated");
next();
} else {
console.log("Doesn't authenticate");
res.status(401);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': false
}));
}
};
/*Routes to all my files*/
require('./routes/index.js')(app, express, io);
require('./routes/vehico.js')(app);
/*Mobile Routes.*/
require('./routes/mobile/auth.js')(app, passport, mongoose);
require('./routes/mobile/settings.js')(app, checkauth);
require('./routes/mobile/cars.js')(app, checkauth);
require('./routes/mobile/devices.js')(app, checkauth);
require('./routes/mobile/locations.js')(app, checkauth);
require('./routes/mobile/group.js')(app, checkauth);
require('./routes/mobile/analytics.js')(app, checkauth);
/* Device Route */
require('./routes/device.js')(app, io);
require('./routes/trip.js')(app, checkauth);
require('./routes/mobile.js')(app, express, checkauth);
app.listen(app.get('port')); // port should be in config folder in a file not out here in open.
console.log('Express server listening on port 4000');
I want to be able to host multiple NodeJS apps under the same domain, without using sub-domains (like google.com/reader instead of images.google.com). The problem is that I'm always typing the first part of the url e.g. "/reader" in Express/NodeJS.
How can I set up an Express app so that the base URL is something.com/myapp?
So instead of:
app.get("/myapp", function (req, res) {
// can be accessed from something.com/myapp
});
I can do:
// Some set-up
app.base = "/myapp"
app.get("/", function (req, res) {
// can still be accessed from something.com/myapp
});
I'd also like to configure Connect's staticProvider to behave the same way (right now it defaults to serving static files to something.com/js or something.com/css instead of something.com/myapp/js)
The express router can handle this since 4.0
http://expressjs.com/en/api.html#router
http://bulkan-evcimen.com/using_express_router_instead_of_express_namespace.html
var express = require('express');
var app = express();
var router = express.Router();
// simple logger for this router's requests
// all requests to this router will first hit this middleware
router.use(function(req, res, next) {
console.log('%s %s %s', req.method, req.url, req.path);
next();
});
// this will only be invoked if the path ends in /bar
router.use('/bar', function(req, res, next) {
// ... maybe some additional /bar logging ...
next();
});
// always invoked
router.use(function(req, res, next) {
res.send('Hello World');
});
app.use('/foo', router);
app.listen(3000);
Previous answer (before express 4.0) :
The express-namespace module (dead now) used to do the trick :
https://github.com/visionmedia/express-namespace
require('express-namespace');
app.namespace('/myapp', function() {
app.get('/', function (req, res) {
// can be accessed from something.com/myapp
});
});
At the moment this is not supported, and it's not easy to add it on your own.
The whole routing stuff is buried deep inside the server code, and as a bonus there's no exposure of the routes them selfs.
I dug through the source and also checked out the latest version of Express and the Connect middleware, but there's still no support for such functionality, you should open a issue either on Connect or Express itself.
Meanwhile...
Patch the thing yourself, here's a quick and easy way with only one line of code changed.
In ~/.local/lib/node/.npm/express/1.0.0/package/lib/express/servers.js, search for:
// Generate the route
this.routes[method](path, fn);
This should be around line 357, replace that with:
// Generate the route
this.routes[method](((self.settings.base || '') + path), fn);
Now just add the setting:
app.set('base', '/myapp');
This works fine with paths that are plain strings, for RegEx support you will have to hack around in the router middleware yourself, better file an issue in that case.
As far as the static provider goes, just add in /mypapp when setting it up.
Update
Made it work with RegExp too:
// replace
this.routes[method](baseRoute(self.settings.base || '', path), fn);
// helper
function baseRoute(base, path) {
if (path instanceof RegExp) {
var exp = RegExp(path).toString().slice(1, -1);
return new RegExp(exp[0] === '^' ? '^' + base + exp.substring(1) : base + exp);
} else {
return (base || '') + path;
}
}
I only tested this with a handful of expressions, so this isn't 100% tested but in theory it should work.
Update 2
Filed an issue with the patch:
https://github.com/visionmedia/express/issues/issue/478
Just to update the thread, now with Express.js v4 you can do it without using express-namespace:
var express = require('express'),
forumRouter = express.Router(),
threadRouter = express.Router(),
app = express();
forumRouter.get('/:id)', function(req, res){
res.send('GET forum ' + req.params.id);
});
forumRouter.get('/:id/edit', function(req, res){
res.send('GET forum ' + req.params.id + ' edit page');
});
forumRouter.delete('/:id', function(req, res){
res.send('DELETE forum ' + req.params.id);
});
app.use('/forum', forumRouter);
threadRouter.get('/:id/thread/:tid', function(req, res){
res.send('GET forum ' + req.params.id + ' thread ' + req.params.tid);
});
forumRouter.use('/', threadRouter);
app.listen(app.get("port") || 3000);
Cheers!
I was able to achieve this using a combination of express-namespace for the routes and a fix from the below google group discussion for the static assets. This snippet will treat a request to /foo/javascripts/jquery.js like a request to /javascripts/jquery.js:
app.use('/foo', express.static(__dirname + '/public'));
Source:
https://groups.google.com/forum/#!msg/express-js/xlP6_DX6he0/6OTY4hwfV-0J
I know this is a very old question but Express has changed a lot since most these answers were posted so I thought I'd share my approach.
You can, of course, use Routers with Express 4 to group together related functionality behind a particular path. This is well documented and has already been covered by other answers.
However, it is also possible to mount an entire application at a particular path. As an example, let's assume our application (the one we want to host at /myapp) looks like this, in a file called myapp.js:
var express = require('express'),
path = require('path'),
app = express();
app.use(express.static(path.join(__dirname, 'public')));
app.get('/hello', function(req, res) {
res.send('Hello');
});
// Lots of other stuff here
exports.app = app;
In our main js file we could then mount this whole application at the path /myapp:
var express = require('express'),
app = express(),
myApp = require('./myapp').app;
app.use('/myapp', myApp);
app.listen(3000);
Note that we've created two applications here, one mounted on the other. The main application could have further sub-apps mounted at different paths as required.
The code in myapp.js is completely independent of where it was mounted. It's similar to the structure used by the express-generator in that regard.
Some documentation about sub-apps can be found here:
https://expressjs.com/en/4x/api.html#app.mountpath
https://expressjs.com/en/4x/api.html#app.onmount
There are also reliability issues. If reliability is important, a common solution is to use a front-end reverse HTTP proxy such as nginx or HAProxy. They both use single-thread evented architecture and are thus very scalable.
Then you can have different node processes for different subsites, and if one site fails (uncaught exception, memory leak, programmer error, whatever) the rest of sub-sites continue to work.
I was looking for this feature but for API routes, not for static files. What I did was that when I initialized the router, I added the mount path. So my configuration looks like this
//Default configuration
app.configure(function(){
app.use(express.compress());
app.use(express.logger('dev'));
app.set('json spaces',0);
app.use(express.limit('2mb'));
app.use(express.bodyParser());
app.use('/api', app.router); // <---
app.use(function(err, req, res, callback){
res.json(err.code, {});
});
});
Notice the '/api' when calling the router