Can't get second level routes working in express - angularjs

I'm sorry I'm reasonably new to node. I've been stuck on this for a couple hours.
server.js
app.use(express.static(__dirname + "/public"));
app.get('/', function(req, res){
res.sendFile(path.resolve(templatesPath + 'index.html'));
});
app.get('*', function(req, res){
res.sendFile(path.resolve(templatesPath + 'index.html'));
});
index.html is an Angular application. I have the first level of routes working fine using Angular's HTML5 routes eg. "http://lh:3000/staff" or "http://lh:3000"
but if I add another level or route parameters, e.g. "http://lh:3000/staff/" or "http://lh:3000/staff/test" Express seems to ignore express.static and instead uses the get wildcard to turn all my files into index.html so my page breaks.
Thanks for your help answerers
In secondary routes, it was loading assets referenced in index.html, relative to the secondary route. My temp solution is to add:
app.use('/files/',express.static(path.join(__dirname + "/public")));
but I realise now it is better to change my solution.

are staff/test, static assets sitting inside your assets folder?
If they are static assets, there must be a file in the path staff/test inside your assets folder.
if they are not static assets and they are dynamic content, consider using express.router,make a router for staff and add it as,
var staff = express.Router();
staff.use('/staff', staff)
//this is route handler for /staff/test
staff.post('/test', function(req, res, next){
res.json(some-json-content)
})

try this:
app.get('*/*', function(req, res){
res.sendFile(path.resolve(templatesPath + 'index.html'));
});

Related

Plesk/React/Express - Starting point is (React) index.html. But I can't do a backend request

BIG EDIT SINCE I DID SOME MORE RESEARCH
I'm trying to deploy my first Nodejs/React App on a Cloud-Server using Plesk.
That's what I tried first:
I created an .httaccess file with the following contents.
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
Problem with this was, that I didn't have access to express app.js anymore, since react's index.html file handles everything. So the alternative is to route accordingly out of app.js from express. I have found the following approach and trie to implement it.
Approch:
/api/app.js
app.use('/result', resultRouter);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
My implementation:
var createError = require('http-errors');
var express = require('express');
const cors = require('cors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
require('dotenv').config();
var helmet = require('helmet');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var resultRouter = require('./routes/result');
var app = express();
app.use(helmet());
app.use(cors());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
next();
});
//Set up mongoose connection
var mongoose = require('mongoose');
var mongoDB = 'MYMONGODBURL';
mongoose.connect(mongoDB, { useNewUrlParser: true, useUnifiedTopology: true });
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use('/result', resultRouter);
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
});
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
While I am sure this is the correct solution in general, I fail to implement it. The app.js file lays in the api folder. Before uploading it to Plesk, I insert the build folder from react inside. So app.js and the build folder are on the same level of the directory.
After the upload via git, I have both the document root as well as the application root set to configurator/api. Configurator is just an empty folder I set up beforehand.
If I set document root to configurator/api/build, the starting page of my react index.html file gets displayed. But routing to any other react component doesnt work.
What do I do wrong here? Also I have enabled "browsing for a domain", but still get a 504 Gateway Timeout Error.
I hope someone can point me in the right direction or has some input as to where I should look next.
Thanks in advance
Found a solution to my problem.
So I know my question is on a real beginner level and in hindsight, I didn't give enough info for someone to help me out here. But I found a solution, that hopefully helps other beginners that are stuck at this point. So I write to share what I learned in the process and also to reflect on it.
2 ways to get react/express running on Plesk
Basically there are two ways to get react/express running on Plesk. Both solutions are viable, but tackle different prerequesites.
Solution 1: You want to render a static site via react, that doesnt perform any backend-requests to nodejs.
In this case, you run Çıpm run build in your react-folder and put the newly created build folder inside your express folder. Inside the build folder, create a .htaccess file with the following content:
Options -MultiViews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.html [QSA,L]
I think doing it this way, you don't even need nodejs. But I haven't tried it out. Anyway. Upload the folder (let's call it api) to Plesk via git and set document root to api/build, while application root is just api.
Solution 2: You want to render static react, but you perform backend-requests to express for some business logic or database-requests at any point.
As in solution 1, create the build folder and move it into the api folder. The build folder is everything you need from React for your app to work at the end. In the api folder, create a new Javascript file on the root level. Name it server.js. Inside put the following code:
const app = require('./app');
const http = require('http');
http.createServer(app).listen(process.env.PORT);
As I understood, this starts your server in the first place and without you'll get a 504 Timeout-Error.
Second, you need to tell nodejs, that it redirects to react's index.html file, whenever you hit a route, that is not defined in express. For this, open your app.js file. Go right under the last of your express routes and insert the following:
app.use('/result', resultRouter);
// ^ above is the last route of my express app.
// below tells your server to redirect all other routes to index.html from React
app.use(express.static(path.join(__dirname, 'build')));
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
});
app.get('*', function (req, res) {
res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
});
After modifying and uploading to Plesk, set both your document root and your application root from the Plesk-Nodejs application to api (or whatever your root-folder is). Then, set the application starting file to server.js. Everything should work now.
Troubleshooting
Here are some obstacles I had on the way and you might face also.
Content Security Policy Error: After setting everything up successfully with solution 2, I got a Content Security Policy Error. This was because of the middleware Helmet I use in my express-app. Comment helmet out to test if it's the problem. If it is, there are ways to setup Helmet correctly. You don't need to disable it really.
CORS Error: My backend request failed with the same origin policy being hurt. This was because the axios request from react to express still referred to localhost. Replace all axios request urls in React with the right url from your production-domain.

How does express serve index.html for React App and how do I modify it?

I'm an express noob here and building a React App with server using express and client using create-react-app.
What I want to do
I want to update the title and meta tag in the index.html.
So browser requests url -> Server gets request and adds the title and tag to the index.html -> return it to the browser.
Listed my code here
...
app.use(bodyParser.json())
app.use(aMiddleware)
app.use("/api/foo", bar)
app.use(express.static('client/build'));
if (process.env.NODE_ENV === 'production') {
const path = require('path');
app.get('/*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client', 'build', 'index.html'))
})
}
Questions
Code is functioning, but I don't know how to replace the title/tag in the index.html
How do I update/replace index for environment that is not prod?
Fo prod environment, I use path.resolve(__dirname, '../client', 'build', 'index.html'), then where is index.html for dev environment? I see there is an index.html in public folder, is it the one that got rendered for dev environment?
I tried to add this code before app.use(express.static(...))
app.get('/', function(req, res) => {
// maybe replace string in the index.html (though I don't know where is it
// then res.send(...)?
})
but this never got triggered. Why?
Stuck on this for a while, tried many things, any help would be great.
You can use react-helmet for this... Or switch to Nextjs which is server side.
https://www.npmjs.com/package/react-helmet

Lighthouse reading my index instead of robots.txt

I'd like to use the chrome audits tool on my React app, but it keeps saying that my robots.txt file is invalid. The thing is, the file seems to be perfectly fine, only it doesn't read robots.txt, but rather my index.html file, therefore resulting in this error :
Both files are in my /public folder, so why would it read the index file ?
if your using Node and Express in order to run your React App project in production.
and if your server file code looks like below snippet, this means your are serving same file for all requests(routes). During lighthouse auditing, browser makes a get request to http://localhost:5000/robots.txt and in return it get served with index.html.
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
To solve this problem ,you can add route for robots.txt file, before above code snippet as shown below.
app.get('/robots.txt', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'robots.txt'));
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

AngularJS: File upload

I am a newbie of angularjs. My version is 1.6.4 using with nodeJS. I am following this tutorial for file upload leon/angular-upload. I did all the steps written here to make it work which are.
1) bower install --save angular-upload
2) Add module dependency "lr.upload" which i did
angular.module(__appName, ["ngMap", "multipleSelect", "lr.upload"]);
3) Add this code snippet in html page, so i did in my fileupload.html.
<div class="btn btn-primary btn-upload"
upload-button
url="/upload"
on-success="onSuccess(response)"
on-error="onError(response)">Upload</div>
4) And finally add script to html page.
<script src='bower_components/angular-upload/angular-upload.min.js'></script>
But i am still getting error:
POST http://localhost:3000/upload 404 (Not Found)
My server.js is code is:
var express = require("express");
var app = express();
app.use(express.static(__dirname + '/app'));
app.use('/bower_components', express.static(__dirname + '/bower_components'));
app.get("/", function(req, res){
res.redirect("app/index.html");
});
app.listen(3000, function(){
console.log("Tellworld web app listening on port 3000");
})
Here is Server.JS files that you should follow
https://github.com/leon/angular-upload/blob/master/example/server.js
You have to add :
app.post('/upload', function(req, res) { /*do your post processing here */ })
Your server will also need it route that receive request from the angular's route directive. In clear english you will need to declare a route pointing to upload in your server which may look like the code below
app.post('/upload', function(req, res){
console.log(req.body); //this is just to show the request body on your console
});

AngularJS routing in ExpressJS

I have an AngularJS app on a NodeJS server with ExpressJS. Now I am serving an Angular app as static files:
app.use(express.static(__dirname + '/app'));
But in navigation, I have a # sign:
http://127.0.0.1:8080/#/queryes
To solve this problem, I put this code in the Angular Router:
$locationProvider.html5Mode(true);
But now I can't get partial views. How do I mix Angular Routes with ExpressJS?
In order to use AngularJS html5mode along with Express, you must serve "index.html" for all requests to leave all routing up to AngularJS. I had this same problem a while back.
So first, you declare all API endpoint routes, any static file directories (CSS, JS, partials, etc), and then serve index.html for all remaining requests. For example:
// serve all asset files from necessary directories
app.use("/js", express.static(__dirname + "/app/js"));
app.use("/img", express.static(__dirname + "/app/img"));
app.use("/css", express.static(__dirname + "/app/css"));
app.use("/partials", express.static(__dirname + "/app/partials"));
app.use("/templates", express.static(__dirname + "/app/templates"));
// any API endpoints
app.post('/api/v1/auth/login', routes.auth.login);
// serve index.html for all remaining routes, in order to leave routing up to angular
app.all("/*", function(req, res, next) {
res.sendfile("index.html", { root: __dirname + "/app" });
});

Resources