We are planning a multi tenant app in node.js and express with mongodb. We would have some core functionality exposed via REST APIs. We would have customized functionality for each customer which would process some data and in turn call the REST APIs. The customized functionality as well as the UI(views and public folder) for each customer would reside in a separate folder for each customer under a parent folder. My questions are:
When I add a new customer and create a module for him, I should be able to start using it without having to restart the main module (app.js).
I need to redirect the customer to his UI files based on the urls say example.com/cust1/home would go to the parentfolder/cust1/views and example.com/cust2/home would go to parentfolder/cust2/views.
For the prototypes we have node running behind an apache proxy/reverse proxy and run the custom apps on their own ports. But in production we cant have the apps running on so many individual ports or modify the default.conf each time. For the UI we have tried the dynamic routing of express but that didnt work. We tried doing this in app.js:
var cust1 = require('./cust1/custommodule'); //this needs to be done for each new customer module added;
var app = express();
// view engine setup
var basePath = ''; //works if we hard code it to cust1 or cust2 here;
app.use('/', function(req, res, next){
// basePath = path.dirname(req.originalUrl); //this does not work since the app.set is executed before this is assigned
app.engine('html', require('ejs').__express);
app.set('view engine', 'html');
app.set('views', path.join(__dirname, basePath + '/views'));
next();
});
app.use('/:custom', express.static(__dirname + basePath + '/public'));
app.use('/cust1', cust1); //request coming with /cust1 should use this module, but this again needs to be done for each new customer module added;
Not sure if this is the right way. Any pointers would be appreciated.
Related
I have my API and Website in same Express Js Project and needs to use ejs view for the website only. But not for the API to return JSON for API routes.
const app = express();
// For static website
app.use(express.static(path.join(__dirname, "public"), {
extensions : ['html']
}));
// API Routes
const bookRoutes = require("./routes/event.route");
app.use("/v1/books", bookRoutes);
// Website
// Set the view engine for dynamic header, footer on website
const ejs = require('ejs');
app.set('view engine', 'ejs');
API
/v1/books
Website
/index.html
How can I skip the view engine for my API routes and apply to /public folder only or even for selected files?
Error message, when I open /v1/books in Postman
{"message":"Failed to lookup view \"C:\\Users\\admin\\github\\test-app\\public\\v1\\books\" in views directory \"C:\\Users\\admin\\github\\test-app\\views\""}
The JSON was expected for /books API
{
id : 1,
name : 'book name'
}
For starters, routes are matched in the order you declare them. So, if you put this first:
// API Routes
const bookRoutes = require("./routes/event.route");
app.use("/v1/books", bookRoutes);
first, then it will get processed before it tries to match any of the static routes. But, this really shouldn't be an issue because there shouldn't be any files in your public folder that would match a request for /v1/books/xxx anyway.
As for the error for /v1/books, you will have to show us what the code is for bookRoutes. It appears that error is coming from that router. express.static() does not make errors like that.
How can I skip the view engine for my API routes and apply to /public folder only or even for selected files?
The app.set('view engine', 'ejs'); line just configures what view engine will be used if/when some code calls res.render(). There's nothing happening with the view engine on one of your API requests because you should never be calling res.render() in an API route. You are presumably calling res.json() instead.
You should ONLY have files in your public folder that you want express.static() to match. That's how you control it. Don't put something in that folder that shouldn't be served statically.
I have express on back-end and react.js on frontend, but i also have admin page with pug view engine, working on express routes, how can i use these in one domain
Expressjs is composable in a really nice way. You can have a top level express application which routes off to sub-express apps and serve your individual services.
Lets say you want to serve your react frontend from www.example.com, your admin (pug views) from www.example.com/admin, and you also want to have an api which serves the react frontend at www.example.com/api`.
You would want something a bit like the following code sample which demonstates the composition of express applications. I've not run the code but it should be enough to get you going.
// This parent app acts as a parent layer and router
// for all your "sub apps". Any middleware you apply
// to this express app will apply to *all your other
// sub-apps*.
const parentApp = express();
// We now create another express instance, this will
// house the API. It can be in another file and you
// could require in something like "require('api');"
// instead but for brevity we'll keep it all in one
// file.
const apiApp = express();
apiApp.get('/info', (req, res, next) => {
console.log('/info');
return res.sendStatus(200);
});
// Mount the sub app on the /api route. This means
// you can how hit 'www.example.com/api/info' and
// you'll get back a 200 status code.
parentApp.use('/api', apiApp);
// Now we setup the admin app which we'll add pug
// views into. This is an example so just pretend
// the views exist.
const adminApp = express();
adminApp.set('views', './views');
adminApp.set('view engine', 'pug');
adminApp.get('/login', (req, res, next) => {
return res.render('login', { title: 'Hey' });
});
// Mount the sub app on the /admin route. This way
// we can hit www.example.com/admin/login to get
// our login page rendered.
parentApp.use('/admin', adminApp);
// Now we create and mount the frontend app that
// serves our fully built react app. You could do
// this with nginx instead but you wanted to do
// it with express so lets do it that way.
const frontendApp = express();
frontendApp.use(express.static('/frontend));
parentApp.use('/', frontendApp);
If you'd rather not create yourself a top level express app (and thus creating a bit of a monolith application) then I'd recommend checking out the nginx documentation, or the docs for the HTTP server you use. You should be able to direct requests to particular endpoints to different node applications running on different ports. Static files can then be served natively by your HTTP server. This is definetely a more efficient and elegant approach, but since you asked about express I wanted to showcase that approach primarily.
I used express as a middleware to serve my angular application (SSR), but as I used internationalisation (spanish & english) on my angular app, I prefixed my urls with /sp and /en to switch between both distributed forlder for both languages.
The prefix has just to be taken into account to pick the right folder and then forward the request to the right angular dist folder, but once done, I have to remove the language prefix /sp /en on every url before rendering the result.
Here's what I has
const DIST_FOLDER = join(process.cwd(), 'dist');
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'en')); // <-- the default language I want to set
app.namespace('/en/', function(){
app.get('*', (req, res) => {
app.set('views', join(DIST_FOLDER, 'en'));
req.url = req.url.slice(4); // <-- here's how I tried to edit the request's url
res.render('index', { req });
})
});
app.namespace('/sp/', function(){
app.get('*', (req, res) => {
app.set('views', join(DIST_FOLDER, 'sp'));
req.url = req.url.slice(4); // <-- same workaround here
res.render('index', { req });
})
});
But it doesn't work as expected as I still have the language prefix on the url of the request I forward to my dist files.
What did I miss?
Thank you
I think your approach is wrong here, the only way the browser will change the URL in this scenario is if the server returns a redirect response i.e. 30x. However, based on your code if the server removes the lang prefix and redirects they effectively lose the preference from the previous response.
Given each user will most likely get to choose their preferred language, and the fact the server has to remember what they chose, it seems logical to me that this needs to be a session-based option.
I've no idea as to what backend you are using to store data but a good place to start would be to look at express-session, it uses an in-memory store by default to get you started but supports various production-ready ones out the box.
I am new to Node. I tried a hello world Express/Node application in which I am displaying a small text and some static images and some styling in a static css file. I am running my application on a hosted virtual machine using Vagrant on a computer I own.
The issue I am facing is that when I try the application from another remote computer in the same LAN, some images are not displayed. The corresponding GET requests are logged in Node with 200 status code but my browser is waiting forever for the answer. I tried Firefox, Safari and Chrome. The weird thing is that if I call the same application from the same computer hosting the virtual machine but outside the virtual machine then I can see the images.
Also if when I deployed the same application on a heroku account I could see all the images from the computer which could not display them.
So I really don't understand what could be wrong.
Please tell me if you faced a similar issue before.
Here is the code
var express = require('express');
var logger = require('morgan');
var path = require('path');
var morgan = require('morgan');
var app = express();
var router = express.Router();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(router);
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
router.all('/', function (req, res, next) {
console.log('Someone made a request!');
next();
});
router.get('/', function (req, res) {
res.render('index');
});
app.listen(3000);
module.exports = app;
Here is the simple page layout. The two images are in the same folder.
doctype html
html(lang="en")
head
title Express Routing
body
h1 Express Routing
p The Definitive Guide
img(src='/images/test.ico')
img(src='/images/giraffe.jpg')
And here is the network trace when I call the application click here.
Eventually, I tried to use another sample application from an existing public git repository and had the same issue when I deployed it on my virtual machine (not all the static files are downloaded from a REMOTE machine whereas they all download if I call the application from the host machine).. As you can see 3 static files are pending download:
click here.
For your information when I use Rails instead of Node I don't have such issue.
the error was gone after I installed the latest ubuntu updates.
Thanks for your time!
In my node app, I configured the views folder, later simply I am passing the html name alone. now the html file need to load from using the views config + html file right. ( am I wrong!)
But it's not working. any one give me the suggestion please?
here is my code :
var express = require('express'),
http = require('http'),
jade = require('jade'),
app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); // i configured the path so i am passing file name alone on get.
app.get('/', function(req,res){
res.sendfile('index.html'); //it's not working
res.sendfile('views/index.html') //it works
});
http.createServer(app).listen(3000, function () {
console.log('Express server listening on port ');
});
thanks in advance
You appear to have a misconception about what the view engine is. The view engine takes some non-HTML code, and transforms it into HTML. Here, you have it set to use jade.
The view engine is only good with the res.render() function. res.sendfile() merely sends a file from the current directory -- not the views directory.
Using express if you want to serve some static HTML files. You can just put those files directly in public folder.
When server will get a GET request of / it will search for /public/index.html serve that as response. You don't have to add router for that /.
Other wise if you want to use some template views then you have to use some views engine.