Change views directory per route in Express? - node.js

I want to set the views directory for each route individually, rather than using /views/ for all of them.
So I have a main folder for each view, which has the route and the view in it:
var main = require('./main/main.js');
var test = require('./test/test.js');
app.use('/', main);
app.use('/test', test);
How do I tell that route to look in /main and /test for the view files, rather than in /views for all of them?
I want to be able to just pass the file name in res.render(), rather than a relative path.
Adding multiple views with app.set('views', ['main','test']); is okay, but I'd rather have the views linked to just that route, in case there are files with the same name in different folders. There's also the problem that it expects the layout file to be in the same folder, when I want them all to use the same layout file.

Use Express middleware to change view directory based on the router, You can write a middle either in application level or router level based on your application needs.
Below is the example for application level middleware to change view directory based on route,
app.use(function (req, res, next) {
if (req.path === '/main') app.set('views', './views2');
else app.set('views', './views');
next()
})
Code snippet will check for the request path, If it is /main route it will use views2 directory otherwise it will use views directory.

Related

Skip express js view for API routes

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.

Express JS serve public folder via GET endpoint

I have a folder called my-folder, and inside it, many other folders. I'm trying to serve HTML and assets like JS and CSS when I go to a route using Express JS. I'm trying:
router.get('/serve/test', (req, res) => {
app.use(express.static(path.join(__dirname, 'my-folder/test/public')))
})
Inside of my-folder is various folders, and in each of those is again different folders, and I'd like to be able to serve the folder's public directory from any folder within my-folder in order to link to JS, HTML and CSS when going to: http://example.com/serve/test
Right now, I've hard-coded a folder of test, but this would be changed via a param...
Any thoughts as to why this doesn't work?
If you know the parent folders beforehand. You could use multiple express.static outside router.get. Like this:
app.use(express.static(path.join(__dirname, 'my-folder/test/public')))
app.use(express.static(path.join(__dirname, 'my-folder/test2/public')))
...
app.get(...)
If you don't know the folder names beforehand then you will need to build it as a route and return files with res.sendFile:
app.get('/my-folder/:uid/*', function(req, res){
var uid = req.params.uid, path = req.params[0] ? req.params[0] : 'index.html';
res.sendFile(path, {root: `./my-folder/${uid}/public/`});
});
The example above will serve the requested path file or "index.html" if no path is provided.

Express App Serving Unwanted Static Files

I have a react app that is served by express. The entire app is included in public/index.html.
Right now my server.js looks like this:
1 const express = require('express');
2 const path = require('path');
3 const port = process.env.PORT || 8080;
4 const app = express();
5
6 // the __dirname is the current directory from where the script is running
7 app.use(express.static(__dirname));
8
9 app.get('*', (req, res) => {
10 res.sendFile(path.resolve(__dirname, 'public/index.html'));
11 });
12
13 app.listen(port);
However, somehow, files like package.json and /.ssh/known_hosts are being served, things that obviously shouldn't be avaialible.
I'm not sure why app.get('*', (req, res)... isn't catching all requests, and why app.use(express.static(__dirname)); seems to be the only configuration that allows my app to server ANY static files.
I haven't had any luck with app.use(express.static(__dirname+'/public')); or
app.use( '/', express.static(__dirname + '/public'));
or anything else I can find.
EDIT---
My project directory looks like:
/myproject
package.json
server.js
/public
index.html
I want ALL requests to simply serve index.html
What I'm still not understanding is why
app.use('*', express.static(path.resolve(__dirname,'/public/index.html')));
does not serve anything.
Also, why in the above example, res.sendFile() does nothing without first having called express.static(). If I delete line 7, then nothing is served.
So, never ever do this:
app.use(express.static(__dirname));
in your main server directory. That exposes ALL your server files to be viewed.
When using express.static() to serve a whole directory, you should create a directory that contains ONLY files intended for public consumption and then point express.static() at that.
I'm not sure why app.get('*', (req, res)... isn't catching all requests
Because that app.get('*', ...) is AFTER your express.static() so if the express.static() finds a matching file, the app.get('*', ...) never even sees the request. It's already handled and routing doesn't continue any more.
As always with express.static() to advise on exactly what you should do, we need to know the precise configuration you have. Where are the public files in your file system relative to your main server directory and what URLs are you intending to use in the client to refer to those publicly available files.
Here's a theoretical example (since you haven't provided your specifics):
Let's suppose you have files like this:
/myproject
app.js
/public
main.css
And, suppose you want to be able to use /main.css as the URL from the client. For that, you would just do this from within app.js:
app.use(express.static(path.join(__dirname, "public")));
In this first example, where you're serving these static files at the top level path, then you have to make sure there are no naming conflicts between these top level resources and any actual routes you want to serve.
If you wanted the client-side URLs to be /assets/main.css, then you would do this:
app.use("/assets", express.static(path.join(__dirname, "public")));
In this example, you must make sure that the /public sub-directory (and any sub-directories it might have) contains only files intended to be publicly accessible.
Adding a path to the public URL such as /assets removes the chance of a naming conflict between your static assets and your top level routes. This can be a good thing because in any multi-person project, it's not uncommon that the person working on the static assets (like CSS files) is different than the person working on server routes so the two main not be directly aware of what names each other is using. In a single person project, you would certainly just keep it all in your head and avoid accidental naming conflicts that way.
FYI, my preference for folder organization is more like this:
/myproject
/server
app.js
/public
main.css
Where it's 100% obvious which are server files and which are public files. Then, to serve the public files from within app.js with a URL of /assets/main.css, I'd do this:
app.use("/assets", express.static(path.join(__dirname, "../public")));
From your comments:
I just want to serve public/index.html for all ('*') GET requests. All other data comes from separate apis. it seems that res.sendFile() doesn't work without first using express.static. The above code resides in server.js
If all you want to do is to serve public/index.html for all requests (where public is a sub-directory below where app.js is located), then remove the express.static() middleware entirely.
Just use this from app.js:
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
remove app.use(express.static(__dirname));
as that tells express to load all files that exist
app.use(express.static(__dirname)); will make your app insecure.
Always print out the URLs in the console like path.resolve(__dirname, 'public/index.html') to find out if there is something wrong in it. Seems aiming to the root directory for static files makes problem. Put them in the conventional public directory then try it out again

app.set('views', __dirname + '/views') in Express & node.js

I'm confused with the app.set() method.
As far as I know, app.set() is like this
app.get('title');
// => undefined
app.set('title', 'My Site');
app.get('title');
// => "My Site"
but in tutorials, make 'views' folder and use like this.
app.set('views', __dirname + '/views')
app.get('/') or app.get('/admin')
shouldn't it be like this?
app.get(views)
app.set(name, value)
Assigns setting name to value, where name is one of the properties from the app settings table.
views
Type:String or Array
A directory or an array of directories for the application's views. If an array, the views are looked up in the order they occur in the array.
app.set('views', path.join(__dirname, 'views'));
This will set your apps view folder to something like:
/Users/adil/Project/myApp/views
When you actually go to use the view, the view name becomes the file path, minus the root directory and the file extension. For example, if you had the following file structure:
/views/
/views/index.hbs
/views/news/
/views/news/index.hbs
/views/news/article1.hbs
/views/news/article2.hbs
You would render the views as follows:
res.render('index', {});
res.render('news/index', {});
res.render('news/article1', {});
res.render('news/article2', {});
The methods app.get() and app.set() in express.js are not what we are used to using in OOP. When we use app.get('key') or app.set('key', 'value') in OOP like java, we just want to set/get a member of a object.
In express.js, however, app.set() is used to set one of the application settings. see: http://expressjs.com/en/4x/api.html#app.set . The app.get() and app.post() methods used here refer routes and request received by node.js server. eg: app.get() refers to GET request and app.post() refers to POST request
The views is a configuration variable that sets folder from which express will grab templates. app.get('/admin') also differs from app.get('variable'). First is a GET route, that would listen HTTP Server, the second is just environment variable of express.

Access to mountpath variable from inside a template using express

Is there a clean, recomended way to access the mountpath from inside a template, so an express app can be run both standalone and as a part of another app (by means of app.use), with the paths pointing to the correct destination either way?
Something like:
{{mountpath}}route/to/file
So in the case that the app is running standalone, mountpath will be /, and in case is running as a submodule, mountpath could be /foo/
Note: I'm using handlebars.
express.static middleware is responsible for serving the static assets of an Express application.
How it works:
Serve static content for the app from the "public" directory in the application directory
// GET /style.css etc
app.use(express.static(__dirname + '/public'));
Mount the middleware at "/static" to serve static content only when their request path is prefixed with "/static"
// GET /static/style.css etc.
app.use('/static', express.static(__dirname + '/public'));
Serve static files from multiple directories, but give precedence to "./public" over the others
app.use(express.static(__dirname + '/public'));
app.use(express.static(__dirname + '/files'));
app.use(express.static(__dirname + '/uploads'));
In your case you can use the second case i.e.; mounting files on path '/static', so whatever files are under the folder public can be accesses with src='/static/filename.xyz'
Create a folder name public with sub folders css, js, images
Or if you want your css to be mounted on path /css then you do :
app.use('/css',express.static(path.join(__dirname, 'module1/css')));
Now you can use all your css files in your entire application as :
<link href="css/style.css" rel="stylesheet">
There is an event that is fired after the module is mounted, so I can do:
app.locals.modulePath = "/";
app.on('mount', function (parent) {
app.locals.modulePath = app.mountpath;
});
The locals are directly accessible from inside all the views, so this will be accessible like {{modulePath}}
I had the same issue - needed to use mountpath from inside a template.
Solution that works for me is based on 2 facts
mountpath is available in request - req.baseUrl
response locals object gets exported to the template - res.locals
First, create a one-line middleware - this will apply to all routes inside the module/subapp
router.use(function (req, res, next) {
res.locals.baseUrl = req.baseUrl;
next();
});
Then inside the template use #{baseUrl}/some/route - note that this is jade syntax, but I suppose it works the same way with handlebars.

Resources