What is the best way to conditionally serve static files in Express? - node.js

I'm building an application that has two separate parts to it, which on the frontend I am building as two separate Angular apps. I am doing it this way so that I can better divide control access to he codebase and not unnecessarily give some team members access to code they do not need.
So there are two separate applications, served from the same NodeJS server. The app that is served depends on whether the user is logged in. If they are a guest user, they get one version app, if they are registered user they get a privileged version of the app with more features.
How can I conditionally/dynamically serve static files in Express, so as to say, "if User1 is a guest, serve Application A, otherwise serve Application B"?

If it's just one file you're talking about serving (e.g. app-1.js or app-2.js) then you don't need express.static. You can just handle the request in a normal route handler with res.sendFile like so:
app.get('/get-app', function(req, res) {
if (userIsLoggedIn()) {
res.sendFile('/path-to-logged-in-app.js')
} else {
res.sendFile('/path-to-logged-out-app.js')
}
})
More about res.sendFile here.
If you want to serve multiple files via express.static, you'll need two instances of express.static middleware, and another piece of middleware on top of that to modify the request url depending up on the user's logged in status. Maybe something along the lines of the following:
// Middleware function to modify the request url based on user's status
function checkUser(req, res, next) {
if (userIsLoggedIn()) {
req.url = `/app-1/${req.url}`
} else {
req.url = `/app-2/${req.url}`
}
next()
}
// Set up the middleware stack
app.use(checkUser)
app.use('/app-1', express.static(path.join(__dirname, 'app-1')))
app.use('/app-2', express.static(path.join(__dirname, 'app-2')))
More about writing your own middleware here. You also might want to add some logic in checkUser to only prepend to urls requesting static files (e.g. request urls to /assets/foo.js get the prepend treatment but requests to /api/some-api-function do not).
All that said, I'm not that familiar with Angular but I'd suggest investigating other ways to serve logged in / logged out content. I'm assuming there is some concept of components or "views" in Angular, and it's probably a lot cleaner to just render a "LoggedIn" or "LoggedOut" component/view vs. sending a whole different "app".

Related

Serving Angular2 app at a base URL with Express

I have an Angular2 app up and running and a very simple express server. Is it possible to only serve my application when a user visits '/app' (for example)?
If yes how can it be implemented?
I intend to have multiple Angular apps on different URLs and all of these need scripts. Hence a solution which takes care of which script to handle when would be desired.
Edit- Along with the accepted answer, if using the angular-cli, the 'base-href' has to be set to the URL of the app when building the app.
Yes, that's possible.
You have to define that endpoint as the only one:
app.get('/app', function (req, res) {
res.send(/* your application */)
})
You can use this:
// serve the angular web page
app.get('/', app.use(express.static(path.join(__dirname+'/../../public'))));
Note: change the dir name to reach your directory where contains the angular static files.

Expose routes on different domains

I am struggling with something that doesn't look that hard : let's say I have 2 urls to access my server :
http://localhost:80/
and an external url
http://domain.com/internal/
Is there a way to do add a basepath internal if the forwarded host is equal to the external url host?
Something like :
app.use(function(req, res, next) {
if (req.headers['x-forwarded-host'] === 'domain.com') {
app.use('/internal', routes);
} else {
next();
}
})
There wont be any direct method as in a shortcut to work your way around for personal use cases.
I suggest this simple method though. Let's take example of app.get('/xyz') route.
This can be accessed locally via http://locahost:80/xyz or yourdomain.com/xyz via any application not hosted locally (Unless you make a call using your domainname in your own application).
Add a header element with every request when the call is internal.
Now, whenever our/xyz route is called check for that header element using a if condition and if the request is made internally you'll have that header element there and then you can simply use either res.redirect or any other method that you find useful (Exporting function in current route or anything else you find easy and needful).

KeyStone JS Account Controller

I understand MVC structure when coding in NodeJS. I started using Keystone JS recently, and I really like it. But, the way they set their controllers up, it seems that the controllers ONLY serve the purpose of rendering a view.
In an earlier project, I had an Account.js model and an Account.js controller. I'm trying to see how it would copy over to keystone.
So: How would I allow users to signup/signin/logout in a Keystone project (not into the Admin UI, but like a member of a regular site)? How would I make an Account controller (obviously with no view to render)?
There are lots of ways you can implement your own methods of authentication and account management in keystone since it is based on express.js.
You can then add an array of 'middleware' functions to routes which will run before passing the request to the controller.
e.g
Route before middleware added
app.get('/admin', routes.views.userAdmin);
Middleware Function
function isAuthenticated(req, res, next) {
// do any checks you want to in here
// CHECK THE USER STORED IN SESSION FOR A CUSTOM VARIABLE
// you can do this however you want with whatever variables you set up
if (req.user.authenticated)
return next();
// IF A USER ISN'T LOGGED IN, THEN REDIRECT THEM SOMEWHERE
res.redirect('/');
}
Route with middleware added
app.get('/admin', isAuthenticated, routes.views.userAdmin);
It's a very broad questions so I'd recommend you go and decide on the best way you'd like to do it yourself as everyone has their own personal preference. The search terms you want are 'express middleware authentication'. A lot of people use PassporJS http://passportjs.org/
Hope that helps :)

How to use static folder but also use routing in Express?

I'm having a major issue with Routing in Express.
In my project, there is a folder /public.
Inside /folder I have some other folders like
- public
|- user
|- common
Initially, the only way pages were served by my Node.js server was through res.sendFile(../..). The problem was that the .js files and .css files did not know where to go.
So I added
app.use('/', express.static(__dirname + '/public'));
But now the problem is, if I try to visit /user what happens is, that the static version of the index.html file in that folder is returned, and my route defined in the node app is ignored!
This route - and most importantly the auth middleware is never touched!
How do I reconcile the need to serve files from a static folder, but also may have routes of the same name that I want to control more closely?
app.use('/user', userauth, userRoutes);
Assuming /user is some sort of API I don't think express.static was intended to be used that way. I think the best solution would be to use different routes. You could add something like /api/ to the beginning of all your routes or make sure your static files are organized under /css, /js, /partials, etc.
If you absolutely must support /user for static files AND your api calls you will need to ditch express.static and use something like the Accept header with custom middleware that determines what the browser is asking for. Depending on the scenario this may get complicated to support all the variables. A simple example would be something like this:
app.use(function(req, res, next){
// check Accept header to make sure its a JSON request
if(req.accepts('application/json') && !req.accepts('html')) {
// JSON request so forward request on to the next middleware(eventually hitting the route)
return next();
}
// NOT a JSON request so lets serve up the file if it exists...
// TODO use req.path to check if file exists - if not 404 - will also need to map paths
// to /users to check for /users/index.html if you need to support that
var fileExists = false;
return fileExists ? res.sendFile('...') || res.status(404).send('Not Found');
});
You would have to make sure when the client calls /users it sets the Accept header to application/json. Most client side frameworks have a way to do this for all AJAX requests.

Express.js or angular for handling routes in a MEAN application?

I am totally new to everything Nodejs/express/angular, and I just ran into a question that bothers me.
When you have a MEAN stack, it seems that routes can be handled by both Express.js and Angular.
Angular:
For instance, if I define a route in Angular, I can do it like this:
var app = angular.module("app", []).config(function($routeProvider) {
$routeProvider.when('/login', {
templateUrl: '/templates/login.html',
controller: 'LoginController'
});
$routeProvider.when('/front', {
templateUrl: '/templates/front.html',
controller: 'FrontController'
});
$routeProvider.otherwise({redirectTo: '/front'})
});
But with express.js I do:
app.get('/',function(req,res){
res.sendfile('templates/angular.html');
});
So my question is:
When do you use angular routing, and when do you use express routing?
(I might miss something very obvious here, but I hope you can point it out)
Those two serve different purposes on a single page app.
The app would do all the CRUD (endpoints where you create/read/update/delete your stuff, for example: projects, users, bills, etc). Also it would do all the authentication stuff (like /login and /register).
All of that needs routes, because you would want something like /api/users to grab all your users. All those routes, AKA CRUD routes and authentication routes goes into express.js router. Why there? Because those are routes of the backend.
On the other hand, you have your angular application, which contains the visual part of your application and there you want some routes. You want / to point to your home, you would want /users to have a page where you list your users or even /users/add to have a page with a form to add new users.
You could see it this way:
Backend routes (express ones): Those are the routes that an end user won't have to know about or even use them (your angular app will use them to communicate with the backend to work with its data but an end user wouldn't put them directly on the browser)).
Frontend routes (angular ones): Are the routes that maps to different pages of your application and because of that, end users can use them to access some parts of your application directly.

Resources