Express static css not served - node.js

I have been trying to figure this out for hours and my head is about to explode. I really hope it's not some stupid little detail I missed...
I have a server-side rendering react application set-up. Everything is going fine. The only problem I have is that I can't seem to get the css to load.
Here is my file tree (without node_modules): https://i.stack.imgur.com/xevUb.png'
I have the following code in my server.js file
app.use('static', express.static(__dirname + '/public'));
app.use('dist', express.static(__dirname + '/dist'));
app.get('*', (req, res) => {
match({
routes,
location: req.url
}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message)
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search)
} else if (renderProps) {
var html = renderToString( < RouterContext { ...renderProps
}
/>);
res.status(200).send(template({
body: html
}));
}
else {
res.status(400).send('Not found.')
}
});
});
And this is my template.js file :
export default ({ body }) => {
return `
<!DOCTYPE html>
<html>
<head>
<title>test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" href="/static/css/style.css" />
</head>
<body>
<div id="root">${body}</div>
</body>
<script src="dist/client.js"></script>
</html>
`;
};
When I go on my local server. I get the html delivered and the bootstrap styles are applied to it. However, I get a 400 bad request error for the style.css and client.js linked in my template.js file.
I really hope someone can help me out on this one...
EDIT
Here is what I see on the developer console :

Your server.js file appears to be inside your dist folder, which means that __dirname would be ./dist instead of ./ like your code seems to be written. What you need to do is something like this:
const path = require('path')
const projectRoot = path.resolve(__dirname, '../')
app.use('static', express.static(projectRoot + '/public'))
app.use('dist', express.static(__dirname))

Related

Bundler similar to webpack that can compile node.js express and EJS files

After trying to set up webpack5 + express + ejs to no avail, I'm looking for some kind of "bundler" that will optimize my server code (files) so those will be automatically minified/uglified/obfuscated/bundled/converted to ES5/ etc. when there is any code change. It must support server side EJS partials (cause I have almost finished project that use EJS from scratch), has option to modify (for example bundle/not bundle) or leave intact specific files.
I know what bundlers there are on the market but I did not try them - I don't know which one is best suited for my needs (if any, besides webpack ofcourse - cause I'm to stupid to properly set it up, cause of How do I serve Webpack bundles with EJS templates? or
Webpack 5 + express + EJS templates ...).
Ejs render loaders seems to require that you pass in your inline variables in the config file, rather than in your Express routes, which doesn't make much sense to me.
webpack: 5.38.1
express": 4.17.1
ejs-render-loader: 1.0.0*
I have difficulties configuring Webpack 5 to properly use EJS template files. Setup looks like this:
INDEX.JS
var express = require('express');
var app = express();
var page1 = require('./routes/page1.js');
var page2 = require('./routes/page2.js');
app.set('view engine', 'ejs');
app.set('views', __dirname + '/ejs');
var server = https.createServer(options, app).listen(port, host, function()
{
console.log('server is running!');
}
app.use('/page1', page1);
app.use('/page2', page2);
// + bla bla bla
PAGE1.JS
router.get('/', function(req, res, next)
{
res.render('page1', {data : data}); // here we render ejs file
res.end();
}
router.post('/page2', function(req, res, next)
{
// + bla bla bla
res.render('page2', {data : data}); // here we render ejs file
res.end();
}
PAGE1.EJS
<!DOCTYPE html>
<html>
<head>
<title>PAGE1</title>
<%- include ('head.ejs'); %>
</head>
<body>
<%- include ('header.ejs'); %>
<%- include ('menu.ejs'); %>
<div class="errors">
<% if (typeof errors == "undefined") { %>
<% } else { %>
<%- errors %>
<% } %>
</div>
</body>
</html>
PAGE2.JS
router.get('/', function(req, res, next)
{
res.render('page1', {data : data}); // here we render ejs file
res.end();
}
PAGE2.EJS
<!DOCTYPE html>
<html>
<head>
<title>PAGE2</title>
<%- include ('head.ejs'); %>
</head>
<body>
<%- include ('container.ejs'); %>
</body>
</html>
WEBPACK.CONFIG.JS
module.exports = {
target: "node",
entry: {
main: './SRC/index.js'
},
externalsPresets: {node: true},
externals: [webpackNodeExternals()],
module: {
rules: [
{
test: /\.ejs$/,
use: {
loader: 'ejs-render-loader'
}
}
]
}
}
Can somebody who has working config, show his settings, so I can adjust mine? Cause now EJS files are not bundled - seems webpack has problem with included data inside those files. Thanks!

Node js, i can't get my browser button to get a list of teams in the json file

I'm new to Node js, and i'm cuurently writing a program to fetch data from a local json file and display in the browser, but nothing happens when i click the button. Not getting errors either. The program runs as follows: 1). node app.js 2.) opens index.html on the server (127.0.0.1:3000) 3.) click the button "Get list of the team". the button has an action = "teams" and the Controller should invoke the action to fetch the teams from the json file. The Controller invokes the Model, then renders the data in the index.htm property called "teamList". Then i expect to see the teams displayed in the browser. Here is my code:
app.js:
const path = require("path");
const express = require('express');
const cors = require('cors');
const fetch = require('node-fetch');
const bodyParser = require('body-parser');
const router = require('./routes/router');
const app = express();
app.use(express.urlencoded({extended: false}));
app.use(express.json());
app.use(express.static("public"));
app.set("views" , "views");
app.set("view engine", "hbs");
const host = "127.0.0.1"
const port = 3000
app.use(cors());
app.use(bodyParser.json());
//app.use('/', router);
app.get('/', (req, res) =>{
res.render("index", {
teamsList: ""
})
})
app.get('/add', (req, res) =>{
res.render("post-tal", {
Sum: ""
})
})
app.listen(port, host, () => {
console.log(`The server is running at: http://${host}:${port}`);
});
index.html:
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>WebApp</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
<style>
.eNavAction {cursor:pointer}
.buttons {margin-top: 20px}
</style>
<script src="js/simple-helper.js"></script>
<script src="../controller/MainController.js"></script>
<script src="apiServer/controller.js"></script>
<script src="apiServer/model/apiServer-model.js"></script>
<script src="apiServer/routes/router.js"></script>
<script>
var Current = {};
const Model = new TeamsModel();
const View = new TeamView();
const ViewTal = new TalView();
const Controller = new MainController();
document.addEventListener('DOMContentLoaded', function() {
// Controller.init();
Helper.onClassClick('eNavAction', Controller.navAction);
});
</script>
</head>
<body>
<nav class="navbar is-link" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/">
<span style="font-weight:bold; font-size:20px">My Web App</span>
</a>
</div>
<div id="navbar" class="navbar-menu">
<div class="navbar-start">
<a class="eNavAction navbar-item" action ="teams">Teams</a>
<a class="navbar-item" action= "tal" href="http://127.0.0.1:3000/add">Sum</a>
</div>
</div>
</nav>
<div class="section">
<div id="main-container">
<button class="eNavAction navbar-item" action ="teams">Get list of the team</button>
<div id="listContainer">
{{teamsList}}
</div>
</div>
</div>
</body>
</html>
MainController.js:
class MainContainer {
teamList = (req, res) => {
Model.loadTeams()
.then(function (data) {
res.json(data);
//res.send(data);
res.render("index", {
teamList: {data} // A property called teamList to be displayed on the browser
})
})
.catch(error => console.log(error));
}
navAction() {
let action = this.getAttribute('action');
if (action == "teams") {
Controller.teamList();
}else if(action == "tal")
Controller.calculateSum();
}
}
Model.js
class TeamsModel {
async loadTeams() {
try {
const json = await fetch('./json/prov-nodes.json', 'utf8')
.then(function(response){
return response.json();
})
.then(function(data){
console.log(data);
});
}catch (error) {
console.log(error)
}
}
}
I tried to reconstruct your problem. First of all I show that I fixed code and get result:
that code in controller.js (Model.js)
class TeamsModel {
async loadTeams() {
try {
const json = await fetch('/data/prov-nodes.json')
return json.json(); // Important! return must be in loadTeams!
}catch (error) {
console.log(error) // it'll never been shown!
}
}
}
As you can see I used '/data/prov-nodes.json' as path. Yes now I can access to file that I had not before by './json/prov-nodes.json'.
I just add two strings in app.js:
app.use('/js', express.static(__dirname + '/controllers')); // allows an access
app.use('/data', express.static(__dirname + '/json')); // allows an access
so import of scripts looks like:
<script src="/js/MainController.js"></script>
<script src="/js/controller.js"></script>
in index.hbs file I just do:
const Model = new TeamsModel();
const Controller = new MainController();
Controller.teamList()
I renamed MainContainer class to MainController class (just for test)
...
teamList = (req, res) => {
Model.loadTeams()
.then(function (data) {
console.log('DATA', data); // just output to console if success
})
.catch(error => console.log(error));
}
...
I believe that this helps you continue coding!
I'm not sure that I reconstruct your code right (models/controllers), but I have showed you how to read json file in express.
├── app.js
├── controllers - just modules not real controllers :)
│ ├── MainController.js
│ └── controller.js - I think this would be a model, sorry :)
├── json
│ └── prov-nodes.json
├── package-lock.json
├── package.json
└── views
└── index.hbs

Express Javascript file won't including with get method with parameters

I'm trying render the index file with Express Node.js successfully, but if I'm using namespaces with parameter, without parameter render twig file and included own scripts, if I use try with parameter, render ok bu problem with script files path so script files in head in html cannot including while path not correct
for example, without parameter, in html file style.css look like
<link rel="stylesheet" href="style.css"> path http://127.0.0.1:3000/style.css
with parameter, in html file style.css look like <link rel="stylesheet" href="style.css"> path http://127.0.0.1:3000/mynamespace/style.css <--- and this not found!
say browser path not found!
Server.js
const port = 3000;
const express = require('express');
const app = express();
const http = require('http');
const socketIO = require('socket.io');
const server = http.Server(app);
server.listen(this.port, () => {
console.log(`Server running on: http://127.0.0.1:${port}`);
});
const io = socketIO(server);
app.set('view engine', 'twig');
app.set('views', "views");
app.use(express.static('public'));
app.use(express.static('scripts'));
app.use(express.static("styles"));
/// Routing
/**
* This work fine
* Render client.twig
* Including Scripts in head
*/
app.get('/mynamespace', function (req, res, next) {
res.render("client");
});
/**
* This work with error
* Render client.twig
* don't Including Scripts in head
*
*/
app.get('/mynamespace/:id', function (req, res, next) {
res.render("client");
});
io.of("/mynamespace").on('connection',(socket)=>{
socket.on('online_users', (data) => {
console.log(`Online Users ${data}`);
});
});
client.js
let url = `http://127.0.0.1:3000/mynamespace`;
console.log("Url", url);
this.socket = io(url);
this.socket.on("connect", ()=>{
try{
this.socket.emit('welcome', {
message:"Welcome guest"
});
} catch (e) {
alert(e.message);
}
});
client.twig
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" const="text/html;charset=UTF-8"/>
<title>RestoCommend</title>
<script src="/socket.io/socket.io.js"></script>
<link rel="stylesheet" href="style.css"
</head>
<body>
<h3>Client</h3>
<script src="helper.js"></script>
<script src="client.js"></script>
</body>
</html>
From your code it looks like the style.css file is in the same directory as client.twig which is the views directory. But you have told express that the static directories are public, scripts and styles. There is no instruction for express to know where to serve the css from. Try moving the style.css file into styles directory.
Good luck.
Sounds like you want to serve your static files under a relative path. Try the following:
app.use('/mynamespace', express.static('public'))
app.use('/mynamespace', express.static('scripts'))
app.use('/mynamespace', express.static('styles'))
And also
<link rel="stylesheet" href="mynamespace/style.css" />
My hierarchy of files
I Solved my problem, but not good idea
Server.js
app.get('/mynamespace1', function (req, res, next) {
app.use(express.static('public'));
app.use(express.static('scripts'));
app.use(express.static("styles"));
res.render("client1");
});
one parameter based namespace!
app.get('/mynamespace2/:clientId', function (req, res, next) {
app.use(express.static(path.join( __dirname + "/../", 'public')))
app.use(express.static(path.join( __dirname + "/../", 'scripts')))
app.use(express.static(path.join( __dirname + "/../", 'styles')))
res.render("client2");
});
more as one parameter based namespace!
app.get('/mynamespace3/:roomName/:clientId', function (req, res, next) {
app.use(express.static(path.join( __dirname + "/../", 'public')))
app.use(express.static(path.join( __dirname + "/../", 'scripts')))
app.use(express.static(path.join( __dirname + "/../", 'styles')))
res.render("client3");
});
client1.twig
<link rel="stylesheet" href="style.css">
<script src="helper.js"></script>
<script src="client.js"></script>
Everything normal
client2.twig
<script src="/socket.io/socket.io.js"></script>
<link rel="stylesheet" href="../style.css">
<script src="../helper.js"></script>
<script src="../client.js"></script>
with one prameter, script paths defined to parent
client3.twig
<script src="/socket.io/socket.io.js"></script>
<link rel="stylesheet" href="../../style.css">
<script src="../../helper.js"></script>
<script src="../../client.js"></script>
with more prameters, parent folder defined, until number of parameter
My Problem solved, but I don't like it so

Why is app.use() not serving up the 'public' directory when I save even though the path is appears to be correct?

I'm following a slightly outdated tutorial on node and express, and my code is identical to the tutorial, but app.use is not serving up the public directory as I wish. When I go to the root localhost:3000 I still see Weather like in the tags on line 19. When I delete it, I don't see anything, including the public directory's index.html file.
Here is my index.html file:
<!DOCTYPE html>
<html lang="en">
<head>
<!-- <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> -->
<title>Document</title>
</head>
<body>
<h1>From a static file.</h1>
</body>
</html>
Here is my app.js script:
/* nodejs script that will create, configure, and start the server.
run script: node src/app.js
keep server running: nodemon src/app.js
*/
const path = require('path');
const express = require('express');
const app = express();
const publicDirectoryPath = path.join(__dirname, '../public'); // abs path to serve
// STACKOVERFLOW - WHY ISN'T THIS SERVING UP PUBLIC DIRECTORY?
app.use(express.static(publicDirectoryPath)); // serve 'public' directory
// create root route
app.get('/', (req, res) => {
res.send('<h1>Weather</h1>');
});
// create a help route
app.get('/help', (req, res) => {
res.send([
{name: 'Barack H. Obama'},
{name: 'George W. Bush'},
{name: 'William J. Clinton'}
]);
});
// create an about route
app.get('/about', (req, res) => {
res.send('<h1>About</h1>');
});
// create a weather route
app.get('/weather', (req, res) => {
res.send({
forecast: 'rain',
location: 'Los Angeles'
});
});
// port 3000 is common development port, starts server
app.listen(3000, () => {
console.log('Server is up on port 3000'); // never displays in browser
});
it should be public instead ..public like this
const publicDirectoryPath = path.join(__dirname, 'public')
As app.js and public share the same parent directory at the same level. So ..public will point out to dir public outside src which is not available.

Stylesheet not loaded, even though statics has been configured

I'm going crazy over this...
I have this simple NodeJS server running - it serves the ./start.html file fine, but the CSS-file is not loaded.
My folder structure looks like this:
/public/css/styles.css
/interface.js (the Node-file)
/start.html
Node is running this code:
const app = express();
const hostname = '127.0.0.1';
const port = 3000;
let serials;
app.use(express.static('public'));
// Make these folders accessible by our HTML-files
app.use('/css', express.static(__dirname + '/public/css'));
//app.use('/js', express.static(__dirname + '/public/js')); // Not used at the moment
//app.use('/images', express.static(__dirname + '/public/images')); // Not used at the moment
app.get('/start', (req, res) => {
const fs = require('fs');
var content;
// We load the file asynchronously and pass on the content to the screen when loaded.
fs.readFile('./start.html', function read(err, data) {
if (err) {
throw err;
}
res.writeHead(200, { 'Content-Type': 'text/html', 'Content-Length': data.length, 'Expires': new Date().toUTCString()});
res.end(data);
});
});
The start.html file looks like this:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>start</title>
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo="
crossorigin="anonymous"></script>
<link href="css/styles.css" rel="stylesheet" type="/text/css">
<script type="text/javascript">
$(document).ready(function() {
etc...
When accessed, using localhost:3000/start, it only shows the HTML-code.
When opening localhost:3000/css/styles.css it displays the stylesheet just fine.
The browser console also does not show any CSS-file loaded.
Any suggestions, please?
Simple mistake: the CSS linkage had a "/text/css" instead of "text/css" and not an error in the JS after all. Now it works perfectly.

Resources