How to do Node.js path aliases? - node.js

I am using Webpack in order to build my Node.js backend project.
In my Webpack config I have aliases, like this:
alias: {
'#': resolvePath('src'),
'#app': resolvePath('src/app'),
},
When I build the app for production, those aliases are working, since Webpack converts them to the their real paths on the created bundle.
However I ran nodemon in order to develop the API. And nodemon is not related to the webpack bundle or the devserver.
Therefore this is getting error (obviously):
let myService = require('#services/my-service');
I want to ask how to run those aliases using Webpack config OR allow kind of aliases using the nodemon server before a bundle was created - in dev mod.

Related

process.env.NODE_ENV always 'development' when building nestjs app with nrwl nx

My NX application's npm run build:server calls ng build api-server that triggers the #nrwl/node:build builder.
It builds the NestJS application as main.js. Things work except I wanted process.env.NODE_ENV to be evaluated at runtime but I think it was resolved at build time (via Webpack).
Currently, the value is always set to 'development'.
I am new to Nrwl's NX. Any solution this?
In NestJs/Nodejs app in Nx.Dev workspace process.env.NODE_ENV is replaced during compilation from typescript to javascript very "smart way" to "development" string constant (everything like NODE_ENV is replaced). I don't know why. But only way how can I get real NODE_ENV in runtime is this code:
//process.env.NODE_ENV
process.env['NODE' + '_ENV']
The reason you're seeing development is because you're building the app in development mode - it's not best practice to evaluate at runtime because then the builder can't do fancy things to make the build production ready. If you want production, you need to build the app in production mode by adding the --prod flag (just like how you need to build Angular in production mode).
If you need to serve the app in production mode (instead of build) the default config doesn't provide you with a prod mode for serve. You'll need to add the configuration to your angular.json.
So this code:
"serve": {
"builder": "#nrwl/node:execute",
"options": {
"buildTarget": "api-server:build"
}
},
Would become
"serve": {
"builder": "#nrwl/node:execute",
"options": {
"buildTarget": "api-server:build"
},
"configurations": {
"production": {
"buildTarget": "api-server:build:production"
}
}
},
and then you can run
ng serve --project=api-server --prod
Indeed the nx builder will replace the expression process.env.NODE_ENV in our source code with the current value of the env var (or the nx-mode).
What happens is this:
the build command executes the nx builder which creates a configuration for web-pack
this configuration instructions for the webpack-define plugin to replace the text process.env.NODE_ENV during compilation with the actual value of the env-var (or the nx-mode):
see nx-code getClientEnvironment()
Since the webpack-define plugin will look for the text process.env.NODE_ENV, it's easy to use a workaround as explained in this answer:
process.env['NODE'+'_ENV']
Warning
When you need to apply this workaround to make your app work, then something is wrong. Since you have compiled your app in production mode, it does not make sense to pass another value for NODE_ENV when you start the (production) app.
The webpack Production page contains some helpful info.
We also had this case, and the issue was, that we relied on the NODE_ENV variable to load different database configs for dev, prod, test, etc.
The solution for our case was to simply use separate env-vars for the database config (e.g. DB_NAME, DB_PORT, ..), so that we can use different db-configs at runtime with any build-variants: dev, prod, test, etc.
I recently faced the same problem using Express instead of Nest.
What we did to overcome this was adding some file replacements when compiling for any of our environments (development, production, staging, staging-dev). This is done in the angular.json file the same way the environment files are replaced for the Angular app .
Another approach that worked for us, was loading the environment variables only once, and retrieve them from that origin. As our app relies on Express for it's backend the used the Express env variable as:
import express from 'express';
const _app = express();
const _env = _app.get('env');
console.log(_env); // shows the right environment value set on NODE_ENV
To come to this conclusion we checked Express code for the env variable and it does use process.env.NODE_ENV internally.
Hope it helps. Best regards.
We had the same issue, we eventually used the cross-env package in our package.json:
"prodBuild": "cross-env NODE_ENV=production nx run api-server:build:production",
"prodServe": "cross-env NODE_ENV=production nx run api-server:serve:production"
process.env is indeed only available at run-time. What is probably happening is that you are not setting this value when running your application. Can I ask how you are running it?
As a trivial example
# The following will read the environment variables that are defined in your shell (run `printenv` to see what those are)
> node main.js
# this will have your variable set
> NODE_ENV=production node main.js
Of course you want to have it actually set in your environment when deploying the app rather then passing it in this way, but if you're doing it locally you can do it like this.

How Do I Build For A UAT Environment Using React?

According to the React docs you can have development, test and production envs.
The value of NODE_ENV is set automatically to development (when using npm start), test (when using npm test) or production (when using npm build). Thus, from the point of view of create-react-app, there are only three environments.
I need to change root rest api urls based on how I am deployed.
e.g.
development: baseURL = 'http://localhost:3004';
test: baseURL = 'http://localhost:8080';
uat: baseURL = 'http://uat.api.azure.com:8080';
production: baseURL = 'http://my.cool.api.com';
How do I configure a UAT environment for react if it only caters for dev, test and prod?
What would my javascript, package.json and build commands look like to switch these values automatically?
Like John Ruddell wrote in the comments, we should still use NODE_ENV=production in a staging environment to keep it as close as prod as possible. But that doesn't help with our problem here.
The reason why NODE_ENV can't be used reliably is that most Node modules use NODE_ENV to adjust and optimize with sane defaults, like Express, React, Next, etc. Next even completely changes its features depending on the commonly used values development, test and production.
So the solution is to create our own variable, and how to do that depends on the project we're working on.
Additional environments with Create React App (CRA)
The documentation says:
Note: You must create custom environment variables beginning with REACT_APP_. Any other variables except NODE_ENV will be ignored to avoid accidentally exposing a private key on the machine that could have the same name.
It was discussed in an issue where Ian Schmitz says:
Instead you can create your own variable like REACT_APP_SERVER_URL which can have default values in dev and prod through the .env file if you'd like, then simply set that environment variable when building your app for staging like REACT_APP_SERVER_URL=... npm run build.
A common package that I use is cross-env so that anyone can run our npm scripts on any platform.
"scripts": {
"build:uat": "cross-env REACT_APP_SERVER_URL='http://uat.api.azure.com:8080' npm run build"
Any other JS project
If we're not bound to CRA, or have ejected, we can easily configure any number of environment configurations we'd like in a similar fashion.
Personally, I like dotenv-extended which offers validation for required variables and default values.
Similarly, in the package.json file:
"scripts": {
"build:uat": "cross-env APP_ENV=UAT npm run build"
Then, in an entry point node script (one of the first script loaded, e.g. required in a babel config):
const dotEnv = require('dotenv-extended');
// Import environment values from a .env.* file
const envFile = dotEnv.load({
path: `.env.${process.env.APP_ENV || 'local'}`,
defaults: 'build/env/.env.defaults',
schema: 'build/env/.env.schema',
errorOnMissing: true,
silent: false,
});
Then, as an example, a babel configuration file could use these like this:
const env = require('./build/env');
module.exports = {
plugins: [
['transform-define', env],
],
};
Runtime configuration
John Ruddell also mentioned that one can detect at runtime the domain the app is running off of.
function getApiUrl() {
const { href } = window.location;
// UAT
if (href.indexOf('https://my-uat-env.example.com') !== -1) {
return 'http://uat.api.azure.com:8080';
}
// PROD
if (href.indexOf('https://example.com') !== -1) {
return 'http://my.cool.api.com';
}
// Defaults to local
return 'http://localhost:3004';
}
This is quick and simple, works without changing the build/CI/CD pipeline at all. Though it has some downsides:
All the configuration is "leaked" in the final build,
It won't benefit from dead-code removal at minification time when using something like babel-plugin-transform-define or Webpack's DefinePlugin resulting in a slightly bigger file size.
Won't be available at compile time.
Trickier if using Server-Side Rendering (though not impossible)
To have multiple environments in a React.js application you can use this plugin
env-cmd from NPM
And after that Create the three files as per your need.
For example if you want to setup dev, stag and prod environments you can write your commands like this.
"start:dev": "env-cmd -f dev.env npm start", // dev env
"build:beta": "env-cmd -f stag.env npm run build", // beta env
"build": "react-scripts build", // prod env using .env file

How to run a node app after production build by WebPack when output is index.html?

***Updated for further clarification:
Thanks much for taking the time to reply. Running 'npm run build' (I'm using the Vuetify/Webpack template) created an index.html and a directory named 'static' with the respective html, css, and js (in 'dist' directory). The question is - how can I run this in production using node or pm2 (for example, 'pm2 start myapp.js')?
Doing some further research and testing, the 'npm run build' built as static. It was my lack of understanding how vue-cli was to build for production (via WebPack) (vue-cli builds static meant to be served via web server). Doing further testing, I can load and bring up my app on a couple local Linux servers (the static content running on one servers, while MongoDB and API running on another server (API running with pm2)). Also, I was able to bring it up on AWS S3 (as static). I think I have my answer now. Thanks much for your time.
***From Original Post:
How to run the following node app in production? Webpack build produced an index.html (I assume as it's using html-webpack-plugin). I can view the site if I run under a web server (e.g., nginx), but I want to run my final app using pm2, or running node process.
I am using the Vuetify WebPack template (I'm using Vue.js), when I run the npm run build for production (in package.json: “build”: “node build/build.js”) the output is as follows:
$ ls
index.html static/
and
$ ls static/js/
app.f7301c212bc5fefd6563.js manifest.2ae2e69a05c33dfc65f8.js vendor.1aae36aa0dc798d4d36d.js
app.f7301c212bc5fefd6563.js.map manifest.2ae2e69a05c33dfc65f8.js.map vendor.1aae36aa0dc798d4d36d.js.map
So, how do I run this via node (npm start) or pm2 (pm2 start) (can't use index.html or static/js files).
When I run the webpack build I was expecting a .js output (for example ‘build.js’) to run via node or pm2 (npm start that runs ‘node build.js’ or pm2 start build.js). I suppose this will work for serverless, but was looking to run via pm2 (with nginx as proxy) as the app will have other factors (e.g., MongoDB) that won’t allow running in serverless (e.g., AWS Lambda).
Would this be a matter of just re-configuring WebPack to output to a .js file (for example, build.js), or will the static files (served via a webserver, nginx/Apache/http-server) allow for the same functionality? That is, will there be any factors that would limit my app by running as static verses via ‘node build.js’ or ‘pm2 start build.js’, (for example, will my API connects to the back-end still work (back-end being node/JS))?

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

is it possible to have multiple modules defined in tsconfig?

Ok, so I'm making a angular2 app with a node server. I've followed the 5min tutorial so I'm using "system" as the module system. However I am using a simple node express server instead of the way they have in the tutorial. I am writing this in typescript as well, but it is also compiled to "system" style of importing modules. And when running the server I get System is not defined.
How do I fix so System is defined when starting the server?
Is it not recommended to use "system" as the compiler option "module" in the tsconfig-file, when writing a node server?
Can I compile the server code with eg "commonjs" and the angular front end with "system"?
How do I do the configuration of module loading (the small script they have in the index.html) if I would not use "system" in the front end either?
I would recommend to split front-end and back-end applications into different projects and make them communicate through a REST service. I mean the server application will provide the service and the front application will consume it using AJAX.
This way you will be sure that there won't be conflict between the tools you use. For example the use or not of SystemJS. You'll be free to use SystemJS in the Angular2 application as described in the 5min tutorial of the angular.io website and commonjs for the Node / Express application.
The following article describes how to do that:
Angular2 TypeScript Gulp and ExpressJS - http://blog.edenmsg.com/angular2-typescript-gulp-and-expressjs/
As #Thierry Templier suggested, you need to split your code into /client and /server to be more maintainable and structured, if you didn't do that yet.
You can create two tsconfig.json files, put one into /server and other into /client folder.
/server
tsconfig.json ( with module: commonjs )
/client
tsconfig.json ( with module: systemjs )
When you are invoking tsc from shell, you use --project command:
tsc -p ./server <-- /server/tsconfig.json is loaded
tsc -p ./client <-- /client/tsconfig.json is loaded

Resources