How to set up dev and API server from create-react-app? - node.js

I've started a new app with create-react-app, and ejected from that. I made a small express server as follows:
const express = require('express');
const app = express();
if(process.env.NODE_ENV === 'production') {
app.use(express.static('build'));
}
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server started at: http://localhost:${port}/`);
});
In package.json I've added a line, "proxy": http://localhost:3000", as well as switching the commands:
"scripts": {
"run": "npm-run-all -p watch-css start-js",
"start": "node server.js",
},
run used to be start.
However now of course when I run npm start and go to localhost:3000, I get Cannot GET /. I need this server to receive and return local API calls I'll be making from my app, but I also want it to run a hot-reloading dev server just like the old npm start (now npm run) command did. How do I do this?

Some time ago I made a fork of the create-react-app repository adding webpack watch option because of this same reason. It might help you.
Just to add more info, I really invested time looking on how to get webpackdevserver to build the "bundle.js", and found that it is not possible because it loads the bundle into memory but doesn't persist it, so the file is never created. The only way available is the webpack watch option but, I don't understand why the create-react-app team can't add it to the repo, it's a really requested feature, and there are more forks than mine that solves this issue. So, you have three options:
Use the proxy server in package.json (if it works)
Make your own fork and add the watch option, or use an existing one
Don't use create-react-app

Related

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.

express server starting react client

Until now, I have been using create-react-app for my projects, with the express-server and the react client each in their own folders.
However, I am now trying to avoid create-react-app in order to really understand how everything work under the hood. I am reading an Hacker Noon article that explains how to setup react with typescript and webpack. In this article they also have the express server at the root of the client which compiles everything itself:
const path = require('path'),
express = require('express'),
webpack = require('webpack'),
webpackConfig = require('./webpack.config.js'),
app = express(),
port = process.env.PORT || 3000;
app.listen(port, () => { console.log(`App is listening on port ${port}`) });
app.get('/', (req, res) => {
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
});
let compiler = webpack(webpackConfig);
app.use(require('webpack-dev-middleware')(compiler, {
noInfo: true, publicPath: webpackConfig.output.publicPath, stats: { colors: true }
}));
app.use(require('webpack-hot-middleware')(compiler));
app.use(express.static(path.resolve(__dirname, 'dist')));
In the end, the start command looks like it:
"start": "npm run build && node server.js"
So I assume the client and the server start on the same port.
Why would you do such a thing? Are there any pros and cons?
It is true that this will allow your development to happen using the same server as express and that web pack will continuously update your dist/index.html file with whatever updates you make to your file. There's not too much of a disadvantage to this as this is just for development. But typically on prod you'll have a single built file that you will serve. And it will not web pack-dev-middleware to be running. Once you've built your server. For the purposes of production it might be possible that you'll only need static assets. But typically, even the server which serves mostly client files will potentially need a server if you want to do server side rendering and/or code splitting.
The command: "npm run build && node server.js" will run the bash/cmd commands into the terminal. npm run build is one step because of the use of && it will if that command succeeds, run the next command which is node server.js which is a strange command I would probably run node ./ (and put the server as index.js) or at least just write node server.
What I'd prefer to see in your package.json:
"start": "yarn build && node ./"
That would be possible if you mv server.js index.js (and npm i -g yarn).
Another thing to note, and look into is what the build step does.
Further Explanation:
The command runs the build step so check what your "build": key runs in your package.json.
This command will probably not exit with the code 1 (any exit code of a terminal process that is above 0 will result in an error and will not pass the &&).
Presumably, the build process described in the package.json will take all the javascript and CSS files and put them into the index.html file which will then be sent to the client side whenever someone access the '/' path.
After that succeeds, it will start the server that you put the code to above.
res.sendFile(path.resolve(__dirname, 'dist', 'index.html'));
will happen if anybody comes across the '/' path.

Setting up Angular Universal App for development

I have created a project with Angular-CLI. (using command: ng new my-angular-universal).
Then I carefully followed all the instructions from https://github.com/angular/angular-cli/wiki/stories-universal-rendering
It builds for --prod and works fine. But there are no instructions on how I can set up a --dev build and have it served with --watch flag.
I tried removing --prod flags from npm "scripts", and it doesn't even run in dev mode. It builds fine but when I open it in browser this is what I see (directly printed to response):
TypeError: Cannot read property 'moduleType' of undefined
at C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:7069:134
at ZoneDelegate.invoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:105076:26)
at Object.onInvoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:6328:33)
at ZoneDelegate.invoke (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:105075:32)
at Zone.run (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:104826:43)
at NgZone.run (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:6145:69)
at PlatformRef.bootstrapModuleFactory (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:7068:23)
at Object.renderModuleFactory (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:52132:39)
at View.engine (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:104656:23)
at View.render (C:\Users\Mikser\documents\git\my-angular-universal\dist\server.js:130741:8)
the versions of npm packages that I use are currently the latest:
#angular/* - #5.2.*
#angular/cli #1.7.3
except for ts-loader, had to downgrade it because it wasn't working:
ts-loader #3.5.0
So if anyone has any info on how to make this work, it would be very appreciated! Or maybe you know some project templates with Angular Universal App configured for both --dev and --prod builds and ability to --watch?
For development, run npm run start which triggers ng serve. The current setup has hot module reloading so it will watch for your changes and update your dev view. I used the same instructions and got it working here https://github.com/ariellephan/angular5-universal-template
In short, for development, run npm run start and look at http://localhost:4200.
For production, run npm run build:ssr and npm run serve:ssrand look at http://localhost:4000
As contributors have pointed out, it might not be the most efficient and fastest way to develop, but nevertheless I did not want to accept workarounds. Besides, hosting front and back on separate servers brings up CORS issues, and I never planned my app to run on separate hosts, I wanted it all on the same host together with API methods.
The problem with --dev build was this:
when building with the following command:
ng build --app 1 --output-hashing=false (note that there is no --prod flag)
AppServerModuleNgFactory turned out missing in the ./dist-server/main.bundle
I imagine that this relates to the ahead of time(--aot) compilation which is the default behavior if you are building for --prod. So the instructions from https://github.com/angular/angular-cli/wiki/stories-universal-rendering included instructions to configure express server for production build only. And since there is no need for server to be able to dynamically render html templates the working --dev build command would be:
ng build --app 1 --output-hashing=false --aot
and this gets rid of the TypeError: Cannot read property 'moduleType' of undefined
Now to watch this whole mess:
run these in separate command windows:
ng build --watch
ng build --app 1 --output-hashing=false --aot --watch
webpack --config webpack.server.config.js --progress --colors --watch
And for the server to restart on change, you have to install nodemon package and run it like this:
nodemon --inspect dist/server (--inspect if you wish to debug server with chrome)
Some other important stuff:
Angular/CLI has a command to generate necessary scaffolding for a universal app:
ng generate universal
and it generates a fixed version of main.ts that avoids client angular bootstrap issue:
document.addEventListener('DOMContentLoaded', () => {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.log(err));
});
a problem that I stumbled upon once I implemented TransferState
There are basically two parts - the server and the UI. While developing the UI, I simply use ng serve. That means when I make changes in my code in the IDE, the browser refreshes automatically. And, here the server part is not used.
I do prod build and run the server only for final testing to see if everything works as expected (No error due to any 3PP library DOM manipulation or AOT related issues, etc.)
Here, I have created a skeleton structure of an Angular Universal project. As I extensively use Vagrant and Docker in my projects, I run the server in a Docker container within the Vagrant guest system. And for development of the UI, I don't run the server. Simply, the ng serve is used.
If you look into my structure in the above Github link, you'll find the details as to how to run it for development and production in the Readme file.
The web server handler server.ts uses the server bundle
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');
That's why the server bundle needs to be compiled before you can compile the server.ts file.
So having a watch system would mean
watching/recompiling the client bundle
watching/recompiling the server bundle
recompiling the server.ts once the server bundle is created
All of them take some time (especially if you do it with aot)
I'd recommend, like Saptarshi Basu mentionned, to develop as best as you can with ng serve and check with angular universal every so often.
Otherwise, it should be possible do achieve what you want with some kind of tasks (grunt/gulp/...) which triggers sequentially ng build ... and recompilation of server.ts file.
It is a bit messy no doubt, as we preferably wish for one command to rule them all.
I came up with a somewhat OK solution where my output will be:
dist/browser
dist/ng-server
Using the executable npm-run-all package (I find it working a lot better on windows machines than concurrently does) I run the three watch tasks: browser, ng-server and nodeJS. Watching node has a pre-task defined that simply runs a small utility/helper/file that watches for the existence of a dist/ng-server folder and terminate itself once found.
For all of this to work (based on the universal-starter repo as of november 2018) there's a couple of modifications to package.json required. Primarily, to support the --watch flag on ng run commands we need to update the compiler-cli (if memory serves), ng update --all should take care of that, giving you the latest angular/cli version in the process (assuming you have a recent cli version installed globally).
package.json
ng update --all
angular 6+
angular/cli 7+
yarn add/npm install the following
chokidar
npm-run-all
(runs our tasks in parallel with the -p flag. -p kills all processes, -l gives each running task a specific color and name in the console)
ts-node (runs nodejs in it's ts-format)
nodemon // for restarting ts-node
add something similar to my util/await-file.js (after some consideration I added my own file-watcher code below even though it wasn't exactly written with the intentions to be put up on display...)
modify your package.json scripts like below
modify your angular.json to match your folder names, following my examples, mainly the "server"'s outputPath should be changed from dist/server to dist/ng-server.
package.json scripts
"dev": "npm-run-all -p -r -l watch:ng-server watch:browser watch:node",
"watch:browser": "ng build --prod --progress --watch --delete-output-path",
"watch:ng-server": "ng run ng-universal-demo:server --watch --delete-output-path",
"watch:node": "yarn run watch:file-exist && yarn run ts-node",
"ts-node": "nodemon --exec ts-node server.ts -e ts,js",
"watch:file-exist": "node utils/await-file.js",
util/await-file.js
const chokidar = require('chokidar');
const fs = require('fs');
const path = require('path');
const DIR_NAME = 'ng-server';
const DIST_PATH = './dist';
// creates dist folder if it doesn't exist - prior to adding it to the watcher.
if (!fs.existsSync(DIST_PATH)) {
fs.mkdirSync(DIST_PATH);
}
const watcher = chokidar.watch('file, dir', {
ignored: '*.map',
persistent: true,
awaitWriteFinish: {
stabilityThreshold: 5000,
pollInterval: 100
}
});
const FOLDER_PATH = path.join(process.cwd(), 'dist');
watcher.add(FOLDER_PATH);
console.log(`file-watcher running, waiting for ${DIST_PATH}/${DIR_NAME}`);
function fileFound() {
console.log(`${DIR_NAME} folder found - closing`);
watcher.close();
process.exit();
}
watcher
.on('add', function (filePath) {
const matchWith = path.join('dist', DIR_NAME);
const paths = filePath.split(path.sep);
const fileName = paths[paths.length - 1];
if ((filePath.indexOf(matchWith) >= 0)
&& fileName.indexOf('.js') > fileName.length - 4) {
fileFound();
}
})
.on('error', error => console.log(`Watcher error: ${error}`));
"npm run start" and using "http://localhost:4200" works for me. Even with Angular 10

npm wait for port/command before executing next (concurrently)

So in my npm package I currently have this script:
"start": "concurrently --kill-others \"npm run dev\" \"electron .\""
The problem is that since the server wouldn't be up yet when electron runs its command, it shows up as blank. This is resolved by reloading the app once the server starts.
So I was wondering if there was a way to wait for the server to start by detecting the port or some other method so that I don't have to do the reload myself.
Here's how I'm setting up the url (trying to implement Vue into it).
let format = live ?
url.format({
pathname: path.join(__dirname, 'dist/index.html'),
protocol: 'file:',
slashes: true
})
:
'http://localhost:8080'
// Specify entry point to default entry point of vue.js
win.loadURL(format);
The key here is to get your dev server up immediately and then implement auto reload or hot module replacement.
Have you looked at electron-webpack? Then your start script would look like:
"dev": "electron-webpack dev"
And your main.js:
const url = process.env.NODE_ENV !== 'production'
? `http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}`
: `file://${__dirname}/index.html`
window.loadURL(url)
I'll recommend you to checkout electron-webpack-quick-start for an example of implementation.
You can use readyator if you want to wait for ports (or URLs) to be ready before starting your script.
Installation: npm i readyator
Waiting for port 80 to be ready before starting Electron with readyator:
readyator 80 "electron ."
It also comes with a handy Node.js API:
import {runWhenReady} from 'readyator';
await runWhenReady([80], 'electron .');

How do I make Openshift to use Express 4, instead of its installed Express 3?

I developed my Nodejs Express app locally using Express 4 and it works as expected on my computer. I then git the whole app up to Openshift. When I try to run it Openshift returns"503 Service Unavailable". If I ssh into my base Node cartridge and do "express -V" it returns version 3.2.5. I get the same version 3.2.5 if I go into my app folder at app-root/repo and run "express -V".
So clearly my Express 4 which was included in the git upload in my app's node_modules is not being used. What is the solution to use Express 4 as required by my app?
Ideas are- remove Openshift's version of Express 3, force Openshift to use my Express 4 in my app area, upgrade Openshift's Express 3 to Express 4. I cannot figure out how to do any of those and I have researched this.
Here's how to troubleshoot:
ssh into your cartridge
cd into the app-root/repo directory
run grep version ./node_modules/express/package.json
you should see a version based on your package.json dependency
verify your package.json has a scripts section containing a start command that just runs your app with something like node ./server.js (server.js being whatever file you coded your main app start script in). You don't need the express command line program to launch an express server. It's for setting up new project boilerplate and other ancillary tasks.
To see the version of express running within your app, you can add this code to your server.js (or equivalent) file: console.log(require("express/package").version);
Look at this project to know how to integrate openshift with express4
Its a simple example .
https://github.com/master-atul/openshift-express4
try this
rhc ssh
cd app-root/repo
npm start
also edit the ./bin/www
var port = normalizePort(process.env.OPENSHIFT_NODEJS_PORT || '8080');
var ip = process.env.OPENSHIFT_NODEJS_IP;
if (typeof ip === "undefined") {
// Log errors on OpenShift but continue w/ 127.0.0.1 - this
// allows us to run/test the app locally.
console.warn('No OPENSHIFT_NODEJS_IP var, using 127.0.0.1');
ip = "127.0.0.1";
};
//app.set('ip', port);
app.set('port', port);
var server = http.createServer(app);
server.listen(port, ip);
server.on('error', onError);
server.on('listening', onListening);
you can follow step:
copy all content bin/www and replace all content in file server.js:
Change some content at server.js:
from
`var port = normalizePort(process.env.PORT || '3000');`
to
var port = normalizePort(process.env.OPENSHIFT_NODEJS_PORT || '3000');
Add line:
var ip = process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1';
from
server.listen(port);
to
server.listen(port, ip);
Add more to package.json
from
"scripts": {
"start": "node bin/www"
},
to
"scripts": {
"start": "node server.js"
},
Add line:
"main": "server.js",
Use npm install --save module-name for npm install
create file .gitignore with content:
node_modules
on local run node server.js to start server with address localhost:3000
upload to openshift:
git add .
git commit -m "First update new server version"
git push
Browser: domain-appname.rhcloud.com

Resources