I'm trying to make node (or express? I'm still a noob so I don't know which is doing the actual work here) render views from multiple folders. So if I have a log in page, then there is a separate folder with index.ejs for log in, and other files for log in. and for my main page, a different folder with index etc...
I found this link which was helpful https://strongloop.com/strongblog/bypassing-express-view-rendering-for-speed-and-modularity/ however they give examples for the jade rendering engine and marko. I'm using ejs and I'm trying the examples they have but they're not working. For example:
I tried this one:
var templatePath = require.resolve('./template.jade');
var templateFn = require('jade').compileFile(templatePath);
app.get('/', function (req, res) {
res.write(templateFn({name: 'Frank'});
res.end();
});
but I replaced require('jade') with require('ejs') but then I get an error on compileFile(templatePath):
undefined is not a function
I also tried the other example with marko
var templatePath = require.resolve('./template.marko');
var template = require('marko').load(templatePath);
app.get('/', function (req, res) {
template.render({name: 'Frank'}, res);
});
but got the same error on load(templatePath). I can't figure out how to make node render views from locations other than the root views folder
ejs only has a compile() function that takes in a string version of the template as an argument. So you will need to load the template from disk manually first:
var fs = require('fs');
var templatePath = require.resolve('./template.ejs');
var template = require('ejs').compile(fs.readFileSync(templatePath, 'utf8'));
app.get('/', function (req, res) {
res.end(template({name: 'Frank'}));
});
Related
I've created a webpage to use it locally. I have a ton of routes like the ones shown below -- 31 .ejs files and 3 .html files. (They are all in the same "views" folder).
//app.js - using node and express
app.get('/page1', function(req, res){
res.render('page1');
});
app.get('/page2', function(req, res){
res.sendFile('views/page2.html', { root: __dirname });
});
I use an app.get for each and every one of these files. I've had a feeling it wasn't DRY code, and so now I'm trying to figure out a more elegant and optimal way to achieve the same result.
I know that many res.sendFile(); could be replaced with a single express.static() middleware statement. I usually use express.static() on a "public" folder which I use to save all my css files -- like this app.use(express.static(path.join(__dirname, 'public')));. But I still don't see how I could use this to simplify all my res.sendFile().
As for the many res.render(); routes, I know that if I don't pass any customized data I could probably replace them with a single middleware that handles either a whole directory of template files (and their corresponding routes) or a list of files. I just don't know how I would do that.
Any help is very much appreciated, thanks!!
[UPDATE]
richie node_modules public
css files, images, etc views
partials
all partial files programmingPublic
all ejs files from a same topic other files (html & other ejs) appjs
packagejson package-lockjson
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const app = express();
// Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// engine
app.set("view engine", "ejs");
// Set static path
app.use(express.static(path.join(__dirname, 'public')));
const fs = require('fs');
function renderStatic(dir) {
return function(req, res, next) {
let target = path.join(dir, req.path);
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "views/programmingPublic")));
Below is the format of my side-menu: (all these files are inside "programmingPublic" folder)
Programming
<li>C</li>
<li>C++</li>
<li>Python</li>
<li>JavaScript</li>
<li>PHP</li>
If you have a bunch of pages that need to call res.render(), but aren't passing custom options to each render, then you could isolate all those templates in their own directory and then use some middleware like this:
const path = require('path');
const fs = require('fs');
function renderStatic(dir, options) {
const regex = /^\.|\.\.|\/\.|\\\./;
options = options || {};
return function(req, res, next) {
let target = path.join(dir, req.path);
if (options.ext && !path.extname(target)) {
target = target + options.ext;
}
// don't allow leading dot or double dot anywhere in the path
if (regex.test(target)) {
next();
return;
}
fs.access(target, fs.constants.R_OK, function(err) {
if (err) {
// file not found, just move on
next();
} else {
res.render(target);
}
});
}
}
app.use(renderStatic(path.join(__dirname, "renderPublic"), {ext: ".ejs"}));
Note, you must isolate these template files in their own directory so that other files are not found there.
For safety completeness, this code also needs to filter out . and .. items in the path like express.static() does to prevent an attacker from going up your directory hierarchy to get access to other files than those in the render static directory.
Then, for the routes you are using res.sendFile() and no other logic, just isolate those HTML files in their own directory and point express.static() at that directory. Then, the express.static() middleware will find a matching HTML file in that directory and do res.sendFile() for you automatically, exactly the same as it does for your CSS files.
I have some server side code in node js, which creates a express js object and runs the server. The app loads the index.html page which is inside the public folder. I have never written the code to serve the home page (mention below), still it works.
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
I have not written this code so how does the index.html gets rendered. My understanding says express JS looks for the first instance of index.html page in all the static folders declared in the code and renders it, in my case the static folder is "publimc" and it has index.html at the root level.
server code follows below, which I have written.
var express = require('express');
var app = express();
var mongojs = require('mongojs');
var db = mongojs('contactlist', ['contactlist']);
var bodyParser = require('body-parser');
app.use(express.static(__dirname + '/publimc'));
app.use(bodyParser.json());
app.get('/contactlist', function (req, res) {
console.log('I received a GET request');
db.contactlist.find(function (err, docs) {
console.log(docs);
res.json(docs);
});
});
app.listen(8000);
console.log("Server running on port 8000");
The home page is rendered as part of the express.static middleware default options.
To disable this logic, set express.static(..., { index: false }).
If you want to change the file served as a home page, set express.static(..., { index: 'yourfile.html' }).
What this option does, in fact, is attempt to serve an index page with given file name for each directory in your public folder, so if you have public/foo/index.html then it will get served when requesting /foo/ path.
I would like to do the following inside a client side java script from a file hosted using node and express
var rootURL = <%= "someurlfromserverconfig" %>;
I simply host a web directory from my node app. I have no need for template engines. I just want to access some simple server properties for examples an API URL. ASP and PHP have a similar feature.
Simple things as that are easy to handle with toString and replace:
var url = 'example.com'
app.get('/', function(req, res, next) {
fs.readFile('index.html', function(err, data) {
if (err) return res.sendStatus(500)
res.set('Content-Type', 'text/html')
res.send(data.toString().replace('<%= "someurlfromserverconfig" %>', '"' + url + '"'))
})
})
This would yield: var rootUrl = "example.com";
For caching purposes you might want to read the file into memory and run your replace beforehand instead of on each request, but that's your choice.
To elaborate on the workflow; fs.readFile returns a Buffer that you can run toString() on which then allows you to run replace().
If you are intent on not having to process a template on every request, and if the data you want to include are not going to change on the fly, you might consider ES6 template strings. You could host your code in a file like this:
'use strict';
const config = require('./server-config');
module.exports = `
var rootURL = "${config.rootURL}";
// ...
`;
And then you would require the file in whatever file is handling the routing. The template will only be processed once, even if it is required by multiple files.
Alternatively, you could just use a lightweight template engine, render it once, and then serve it whenever it is requested. If you want to use exactly that format, I would recommend EJS.
'use strict';
const ejs = require('ejs');
const config = require('./server-config');
let template = fs.readFileSync('./some-template.js', 'utf8');
let rendered = ejs.render(template, config);
app.get('/', (req, res) => {
res.send(rendered);
});
If the data you are sending are constantly changing, you will have to render the template every time. Even ASP and PHP have to do that under the hood.
I keep getting an error that says it cannot find the module reddit.js. I have a folder called "routes" (without quotes) in my directory. In that folder I have reddit.js which is middleware. On the first file below, I did change it to var reddit = require('./routes/reddit.js') and I received the error messsage that says "throw new TypeError('Router.use() requires middleware function but got a
^
TypeError: Router.use() requires middleware function but got a Object
at Function.use "
When I keep the code as shown below I get this error:
Error: Cannot find module 'reddit.js'
my app.js file contains the following code:
var express = require('express');
var app = express();
var fs = require('fs');
var reddit = require('reddit.js');
app.use ('/', reddit);
app.use(express.static('public'));
app.use(express.static('public/js'));
app.use(express.static('public/images'));
app.use(express.static('routes'));
my reddit.js file contains the following code:
var express = require ('express');
var request = require ('request');
var reddit = express.Router();
reddit.get(function (req, res, next) {
request('https://www.reddit.com/r/Showerthoughts/hot.json',function(error, response, body){
console.log(body);
var docs = JSON.parse(body).response;
//var titles = [];
console.log(docs);
res.send(docs);
next;
});
});
what am I doing wrong?
Mentioned below are the list of things that are not correct
You don't need to have .js extensions for including files. Use require('/path/to/reddit'); instead of require('reddit.js');
You need to export the router instance in reddit.js. Add module.exports = reddit; at the end of the file.
Don't call next() after sending out the response using res.send(docs);
Routes are not static content. Remove app.use(express.static('routes'));
app.use(express.static('/public')); handles all static content inside the /public folder. You do not need to add app.use(express.static('/public/js'));
I am new to NodeJS. What I wanted to know is, can I like call 2 JS files using NodeJS & ExpressJS. Basically I want to have 2 seperate files so I can work on one and my partner can work on another one. So I want Server.js to call one file which contains some part of my REST API and other one contains rest of the function.
|--NodeModules[etc..]
|--Server.js
|--Rest/
| |--RestAPI1.js
| |--RestAPI2.js
It will be really helpful in the development of my project, if this is possible.
You can define routes in different files like this:
Server.js
var express = require('express')
var router1 = require("./routers/router1");
var app = express();
.....
app.use("/user", router1);
Router1.js
var router = require("express").Router();
router.get("/", function(req, res) {
...
});
module.exports = router;