AngularJs route reaload fails in nodeJs - node.js

I managed to get an example app working following several examples, but theres a problem i cant seem to solve. In the angular app routes work ok like this:
- http://localhost:8888/#/login works
but when i reload the url i get the Cannot GET /login error from nodeJs.
i tried doing this:
app.get('/*', function (req, response) {
response.write(__dirname + '/../Client/index.html');
response.end();
});
and this
app.get('/login', function (req, response) {
console.log('*****************************************');
response.sendfile(path.resolve(__dirname + '/../Client/'));
});
but none work, i always get the same error and in the first one it just writes the url.
I think the problem is that while in the app angular knows the routing and handle the reloads because the file is served with
app.use('/', express.static(__dirname + '/../Client/'));
but when the url doesnt have the angular hashtag nodejs tries to look for the url but fails. is there a way i fix this? maybe redirecting to the correct url or something, i searched for hours trying stuff but none worked, thanks for taking your time.

The error is generated from the fact that Angular will rewrite the window.location at runtime while you are using your application starting from the root localhost/ and routing you to localhost/login (without calling the server with a GET).
So, when you refresh, the browser didn't know that the url was written by javascript, so he calls the node server that reject the request.
I other word: you need to start your navigation always from the root.. but this is unusable.
So I figure out how to develop a single project with Angular (4 in my case) for the front-end and nodejs-express for the back-and.
I've tested this solution for redirect with fragment from different source like auth0 and the use of JSON Web Token.
This let me to deploy this application easily to a single heroku instance.
NodeJs Configuration
The first middlewere have to be the path where the Angular's app is build
app.use(express.static(__dirname + './dist'));
Then I have configured express-jwt (the regular expression is for all the url that not starts with "/api")
app.use(expressJwt({secret: appConfig.SecurityJwt}).unless({
path: [
'/api/v1/login',
/^(?!\/api).*/gm
]
}));
Then the business logic of front end:
app.post('/api/v1/login', ...
app.get('/api/v1/myOtherServices' ....
And in the very end of routing configuration, even after the custom error handler:
app.use(appHandleError);
app.get('*', function (req, res) {
res.sendFile('index.html', {root: './dist'});
});
app.listen(4200);
Angular routing
I did not do anything particular here:
const routes: Routes = [
{path: 'home', component: LoginComponent},
{path: 'authenticated', component: ActivatePageComponent},
{path: '', redirectTo: '/home', pathMatch: 'full'}
];
#NgModule({
imports: [RouterModule.forRoot(routes, {useHash: false, enableTracing: false})],
exports: [RouterModule]
})
export class AppRoutingModule {
}
Then in the package.json I have made a custom script:
"scripts": {
"ng": "ng",
"start": "node server.js",
"build": "ng b --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"postinstall": "ng b --prod",
"buildAndStart": "ng build && npm start",
"i18n": "ng-xi18n --i18nFormat=xlf"
},
That I can launch with the command npm run buildAndStart so the angular project is builded and the server node is up and running.
My angular app will be listening to http://localhost:4200/ and all the rest service at the /api/* path as well.
So, for example, url like:
http://localhost:4200/authenticated#access_token=123
http://localhost:4200/authenticated?access_token=123
will be evaluated by Angular, and Angular can call all the /api/v1/myOtherServices without any problem, even the refresh/reload will not generate any error this time.

Related

Expanding mobile application into web app on heroku but it only displays a blank page?

I get this may seem like a duplicate question, but none of the prior solutions have worked. I have been attempting to fix this for the past few days and I'm at my wits end.
I currently have a working mobile application whose backend lives on heroku. I am attempting to extend this into a web application, so I deployed create-react-app in addition to the already existing backend to my heroku application. The CRA code I pushed successfully deploys, but I only see a blank page when entering my URL.
Other than the blank page, the only thing I can see is the title page, and upon inspection of the page, no errors are displayed. My index.html code also displays in the console. No errors are displayed in the heroku logs either.
app.js (relevants snippet)
const app = express();
app.use(cors())
app.use((req, res, next) => {
res.setHeader(
"Access-Control-Allow-Origin",
"*"
);
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, DELETE, OPTIONS"
);
next();
});
app.use(express.static(path.join(__dirname,'public')))
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname, 'frontend_web','public', 'index.html'))
})
app.use(bodyParser.json())
I have tried refactoring the above code in many different ways. From using path.join instead of path.resolve, to adding app.use(express.static(path.join(__dirname,'build'))) to the code, but nothing seems to work.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import reportWebVitals from './reportWebVitals';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint.
reportWebVitals();
both my index html and js files remain unchanged from create-react-app, and still belong to their default folders. html lives in public and the js in src. Some of the backend files live in the root directory. Others like models, routes, and utils, have their own folders.
package.json in root directory
"scripts": {
"start": "node app.js",
"dev": "cross-env NODE_ENV=dev node app.js",
"test": "cross-env NODE_ENV=test jest --verbose --runInBand",
"heroku-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix frontend_web &&
npm run build --prefix frontend_web"
}
package.json in frontend web
"scripts": {
"start": "cross-env NODE_PATH=src react-scripts start",
"build": "cross-env NODE_PATH=src react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
Procfile
web: node app.js
I even added this file below which was suggested in an article I saw. I'm unsure of how it actually works. I did it as a final resort, as I have no idea why the page isn't rendering.
static.json
{
"root": "build/",
"clean_urls": false,
"routes": {
"/**": "index.html"
}
}
Any help or guidance will be greatly appreciated!
As a sidenote, adding * after / in:
app.get('/', function (req, res) {
res.sendFile(path.resolve(__dirname, 'frontend_web','public', 'index.html'))
})
results in my mobile application crashing. From my understanding, it is an important regex, as it allows for a dynamic build across multiple files. Since it breaks my mobile page though, I am omitting it for now.

How to connect to MongoDB server and npm start at the same time for MERN app

I am making a MERN app and I notice that every time I want it to work, I have to first connect to the MongoDB server by "node server.js".
Then, I have to do "npm start" to run the react app which is connected to the server I just connected to.
Is there a way to do both of these actions using one command?
If you are using this for production, then you could use nodemon, which is a good tool that hot reloads your server and front end.
"scripts": {
"dev": "SET THIS=development & nodemon server.js",
},
Also, make sure you're server JS has a catch all that points to your app, such as:
server.get('*', (req, res) => {
return handle(req, res)
})
backend
- - server.js //with mongodb connection, and api routes.
//This is what you are going to use for your API calls from the front end. i.e http://backend.com/userapi/
frontend
- - server.js that contains the catch all statment. Also would add custom routes in here i.e
server.get('/posts/:id', (req, res) => {
return app.render(req, res, '/article', { id: req.params.id })
})

How to deploy Angular Universal app to Node.js production server?

I have an Angular 8 application with Universal that I want to deploy to a shared web host production server. I checked with the web host in advance and they told me that hosting an angular universal web app is possible on their shared web hosting. However, whatever I do, I can't get the website to work. When I go to the website I keep seeing the message: "This site can't be reached"
Things I have done so far:
Build project with npm run build:ssr which created a dist folder with a browser and server folder and a server.js file
Move the dist folder to the server inside the public_html folder.
Then accessed the server through SSH and did these:
install Node.js and npm
npm install
npm install pm2 -g
pm2 start dist/server.js
pm2 starts without problems.
These are some of the project files. If any are missing please ask and I'll add them to the question.
Part of the package.json with the scripts:
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"compile:server": "webpack --config webpack.server.config.js --progress --colors",
"serve:ssr": "node dist/server",
"build:ssr": "npm run build:client-and-server-bundles && npm run compile:server",
"build:client-and-server-bundles": "ng build --prod && ng run ProjectName:server:production --bundleDependencies all"
},
server.js from the build (only the express part, since it's 25000+ lines):
const app = express__WEBPACK_IMPORTED_MODULE_1__();
const PORT = process.env.PORT || 4000;
const DIST_FOLDER = Object(path__WEBPACK_IMPORTED_MODULE_2__["join"])(process.cwd(), 'dist/browser');
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP, ngExpressEngine, provideModuleMap } = __webpack_require__(144);
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine('html', ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [
provideModuleMap(LAZY_MODULE_MAP)
]
}));
app.set('view engine', 'html');
app.set('views', DIST_FOLDER);
// Example Express Rest API endpoints
// app.get('/api/**', (req, res) => { });
// Serve static files from /browser
app.get('*.*', express__WEBPACK_IMPORTED_MODULE_1__["static"](DIST_FOLDER, {
maxAge: '1y'
}));
// All regular routes use the Universal engine
app.get('*', (req, res) => {
res.render('index', { req });
});
// Start up the Node server
app.listen(PORT, () => {
console.log(`Node Express server listening on http://localhost:${PORT}`);
});
According to several answers on SO or elsewhere you "simply" copy paste the dist folder to the server, run pm2 and your website supposedly works. I feel like there's a lot missing to get it working though.
Does someone know how to properly deploy an Angular Universal website to a production server?
I ended up using a tool in my DirectAdmin console called 'NodeJS Selector', because PM2 kept causing problems for me. It's a tool to install your application, do npm install and start the app. So basically what you'd do using SSH, but in a fancy UI. The setup looks like this:
With my folder structure looking like this:
domains
- appname.com
- public_html
- browser (=> this set as the document root in Apache, because index is located here)
- index.html
- .htaccess
- other files...
- server
- server.js
- package.json
I had my web host do 2 things, because I'm not a root user on the server. They set the documentroot to the browser folder in Apache's httpd.conf file. In the same file they also added proxy settings to port 4000 because that's where my app is running on. So in the httpd.conf file will be something like this:
DocumentRoot "/domains/appname.com/public_html/browser"
<Proxy *>
Order allow,deny
Allow from all
</Proxy>
ProxyPreserveHost On
ProxyRequests Off
ProxyPass / https://localhost:4000/
ProxyPassReverse / https://localhost:4000/
Next in the NodeJS selector you set the root to where the startup file is located (in my case server.js). The package.json needs to be in de public_html folder as well so you can do npm install in the Selector.
To start the app you click 'Run JS script' and choose the serve:ssr option and it runs the node server command.
The Angular docs describe how you have to rewrite some rules on the server. Apache is installed on my web server so I added an .htaccess file with the rewrite rules from the docs. However this app is served by Node and Node can serve the actual paths. So in my case I didn't have to add the .htaccess file with rewrite rules. On the contrary, when I added the .htaccess file it caused the Universal side of my app not to fully render. See this question for more info.
In server.ts file change
const DIST_FOLDER = join(process.cwd(), 'dist/browser');
to
const DIST_FOLDER = join(process.cwd(), 'browser');
Now build the application by npm run build:ssr and copy everything inside of dist folder to your public folder (on host). Remember, instead of pm2 start dist/server.js, you should run pm2 start server.js.

Why Angular 2 doesn't load default app-root component?

I'm trying to start Angular app generated via angular CLI, but it seems like the default app-root component doesn't load. Need to say, that I'm using proxy for connection between angular app and express server, and I'm running two scripts at the same time: node bin/www for express/node.js start and ng serve --proxy-config proxy.config.json for starting Angular and creating proxy connection, it looks like this (the part of package.json):
"scripts": {
"start": "concurrently --kill-others \"node bin/www\" \"ng serve --proxy-config proxy.config.json\""
}
The index page loads fine, but it seems that app-root component (the default component, which was created from angular CLI ng new) doesn't loading:
Here is my node.js/express uses and a route:
var express = require('express');
var router = express.Router();
var app = express();
var path = require('path');
app.use(express.static('./src/client/'));
app.use(express.static('./'));
app.use(express.static('./tmp'));
app.use('/*', express.static(path.resolve('src/client/index.html')));
router.get('*', function(req, res) {
res.sendFile(path.resolve('src/client/index.html'));
});
module.exports = router;
And the structure of my project (if needed):
What did I miss? Why the default app-root component doesn't loading? (need to say, when I run ng serve, it starts the angular homepage as needed and the component is OK, so I think the problem is somewhere in express).
Thanks in advance
You should serve the contents of the dist/ folder after calling ng build --prod (the --prod is important, as the default is --dev). So, it would be something like this:
"scripts": {
"start": "ng build --prod && node bin/www"
}
And, more or less adapting your express script:
app.use(express.static('./dist'));
app.use('/*', express.static(path.resolve('dist/index.html')));
router.get('*', function(req, res) {
res.sendFile(path.resolve('dist/index.html'));
});

Angular2 routing in heroku

I have an angular2 application in heroku, and I'm having trouble with the router. In localhost everything works like a charm, but when I deploy to heroku and try to access by any route that is not index i got 404 error, if I go index, then navigate trough page the routing occurs normally, unless I reload the page, then i get another 404, here's the piece of my package.json used by heroku
"heroku-prebuild": "npm install http-server -g",
"heroku-postbuild": "ng build --target=production --environment=prod && rsync -a dist/* .",
"start": "http-server dist/",
Do I need to setup any express rewriting to be used in my Procfile?
It seems to be a problem from the server , angular knows routing but your server doesnot know all these paths. The simple solution is to redirect all the paths to your main index.html . Like this,
app.get('*', function (req, res) {
res.sendfile('./dist/index.html'); // load our index.html file
});
This will not give any 404 error , all your paths will be redirected to main path i.e index.html and angular routing will run the same as it was in your local host .

Resources