How to run Tabler in production - node.js

On my local machine I have been developing on a tabler clone. To run it, I do as the repo suggests and run npm run serve. This brings up nice dev tools like auto-compiling scss and livereloading after changes. Further, npm run dist creates a /dist folder with my sites contents. However, I want to run this on an EC2 instance, but am unsure how to run for a production environment. How would I do this?
A direct Tabler clone is live here on my server. As you can see it takes too long to serve up the basic index page. This is the issue I am trying to solve by running in production

My solution was to wrap the code in /dist with a new node/express program. I set up a basic express app under /prod. In app.js I have the following code.
const dist = path.join(__dirname, '/public/');
router.get('/', function(req, res) {
res.sendFile(path.join(dist, '/index.html'));
});
For production, first I call npm run dist
Then I call npm run prod which does the following: "prod": "del ./prod/public && move-cli --mkdirp ./dist/ ./prod/public"
Now the server is runnable using node prod/bin/www

Related

How to create Node.js Express app serving a Vue.js SPA?

I'm trying to set up a Node.js project that uses Express to provide a few backend APIs and serve a SPA built with Vue.js.
When I use the Vue cli to initialize a project, I get e.g. src/main.ts main file and commands npm run serve to run a dev server and watch for changes and npm run build to build a production release.
When I use the Express application generator to create a project, I get ./app.js main file and npm start to start the server and watch for changes.
How can I combine these into a single project, both served by the same Express server? Preferably so that a single command would watch + update changes to both server and client? I want to use Vue single file components and TypeScript, which (probably?) require a build step.
(I don't need dynamic server-side rendering of Vue templates, just the static SPA app provided. I prefer TypeScript, but JavaScript answers are fine as well.)
These will be different for your dev and prod environments...
For development look into concurrently - it basically allows you to create a single script in your package.json to start both the client and server concurrently and will watch for changes etc...
For production, you would need something like this in your app.js:
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
(The code above assumes that your directory structure has a client folder with a build folder after having run npm run build I'm more familiar with React than Vue... but the logic should be the same...)

Starting Angular application as a default of a NodeJS project

I have a NodeJS application and an Angular 6 as a frontend.
The project looks like:
-> Node Project
---> src
---> Client_App (Anuglar)
To run the application, I need to follow those commands and start the server and angular separately, like:
-> node start
-> cd src/Client_App
-> ng serve
I need to start the two application with one single command or to add my dist file of Angular to be run at the start of my NodeJS, which is using Jade right now.
I am still new to NodeJS and still don't know how to configure it.
Anybody can help? Thanks
Edited:
I have tried now to add the dist folder to my views folder and run it within the app.js
app.get('/', function (req, res, next) {
res.sendFile(path.join(__dirname + '/app_server/views/ngapp/index.html'));
});
But I am receiving the error, that my .js and .css folders are not found:
When you build your application with the CLI ng build --prod, you get a dist folder : this folder contains all of your application, bundled into different files (feel free to look at them).
To be able to create a .ZIP file with that, you will need two things :
this dist folder
an http server
You have the first one, but not the second one.
All you need is a very simple server. For instance, http-server on NPM can do that. By installing it as a dev dependency, you could create a command in your package.json file
"deploy-locally": "http-server ./dist"
And now run it with
npm run deploy-locally
Or even better,
"start": "http-server ./dist"
And run with
npm start
If you don't want to use a NPM package (or forced to use NodeJS), simply create a basic http server in a JS file and run it with your command line (sorry, can't help on that, not into nodeJS right now).
You can create a new route and pass in app.route as express.static as below,
var express = require('express');
var path = require('path');
var bodyParser = require('body-parser');
var app = express();
app.use(express.static(path.join(__dirname, 'dist')));
make sure, u have build version of angular application by running this command,
ng build --prod --build-optimizer
You would need express to install in this case. express has amazing ways to handle all this

How to deploy Front & Back node.js on the same Heroku app?

To be able to run my app, I need to run these 2 command in 2 different terminal :
cd app && npm install & npm start
cd api && npm install & npm start
I basically launch the front (app) and the back (api).
I want to deploy that on Heroku, but using the free plan.
Is there a way to do that? Do I need to create a package.json file on the root folder? If yes what should be inside?
Whatever I write, it seems to run only the front and never the back.
Many thanks!
You need to build the front-end and then serve it from the server end.
Whatever front-end framework you are using, make sure the build file is kept in the server static directory. Then make a route to load the build file (in most cases index.html).
Your steps might be
First, build the production-ready front-end (in most cases, it will generate the index.html in a distribution directory).
Put the distribution directory in your server public or static directory.
Make route in your server end and server the static file (mainly the index.html)
Now you are ready to go, put the node server to heroku.
For example, your final route might be
app.get("*", (req, res) => {
res.sendFile("static index.html file 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

Deploy the backend and frontend on the same Heroku app/dyno

At the root of my project, I have a frontend and backend folder. Both folders contain a package.json that list their dependencies. How do I tell Heroku to run npm install on both folders when deploying the application? It seems like Heroku expects to have a single package.json file by default. Do I have to do something with a Procfile? The Heroku documentation doesn't seem to tell much about my specific question.
Thanks for the help!
I just successfully completed this goal using static files created during a heroku postbuild step, as described in this blogpost. I have a React frontend (could be anything though) and Express API backend. Each process has its own port in dev, but deploying on Heroku uses just one total.
Put the working frontend in a subdirectory of root (such as /frontend).
Put the working backend in a subdirectory of root (such as /api -- the blogpost assumes the backend remains in the root directory -- either way is fine).
Proxy API requests from the frontend to the backend by adding this line to /frontend/package.json (replacing 5000 with your backend port):
"proxy": "http://localhost:5000",
Add the following to api/app.js (or api/index.js) in the backend (be sure the last part is AFTER you define the appropriate backend [or api] paths):
const path = require('path')
// Serve static files from the React frontend app
app.use(express.static(path.join(__dirname, '../frontend/build')))
// AFTER defining routes: Anything that doesn't match what's above, send back index.html; (the beginning slash ('/') in the string is important!)
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname + '/../frontend/build/index.html'))
})
Edit the root directory's /package.json file with something like the following (note that using the concurrently package allows an easy way to run the whole app locally with npm run dev, but only heroku-postbuild is required here):
"scripts": {
"frontend": "cd frontend && npm start",
"api": "cd api && nodemon app.js",
"dev": "concurrently --kill-others-on-fail \"npm run api\" \"npm run frontend\"",
"heroku-postbuild": "cd frontend && npm install && npm run build"
},
Make sure you install all backend package dependencies in the root directory, or you will get errors.
Make sure your /Procfile has something like web: node api/app.js
Seems that you can put a package.json file on the root of the project and use scripts to invoke npm i in both folder.
https://devcenter.heroku.com/articles/nodejs-support#customizing-the-build-process
Something like cd front && npm i && cd ../back && npm i
But i should say that if they are running on different ports, it may not work as it seems that only one web process per procfile is available.
this last point is to confirm.
You can define several entry points for your project in the Procfile :
web: cd front && npm i && npm start
server: cd backend && npm i && npm start
However, you have to upgrade to Hobby at least. It's 7$/dyno/month.

Resources