How to watch and reload an ExpressJS app with pm2 - node.js

I'm developing an ExpressJS app.
I use pm2 to load it:
myapp$ pm2 start bin/www
This works fine, except that adding the --watch flag doesn't seem to work; every time I change the JS source I need to explicitly restart it for my changes to take effect:
myapp$ pm2 restart www
What am I doing wrong? I've tried the --watch flag with a non-ExpressJS app and it worked as expected.

See this solution in Stack Overflow
The problem is relative to the path where pm2 is watching, and if it is relative to the execution file or the actual root path of the project.

2021 Feb.
Things have changed a bit now. Gave a full example below from my project. Below works:
1 . Create config file. File: ecosystem.config.js
module.exports = {
apps: [
{
name: 'api',
script: './bin/www', // --------------- our node start script here like index.js
// ------------------------------------ watch options - begin
watch: ['../'],
watch_delay: 1000,
ignore_watch: ['node_modules'],
watch_options: {
followSymlinks: false,
},
// ------------------------------------ watch options - end
env: {
NODE_ENV: 'development',
PORT: 3001,
DEBUG: 'api:*',
MONGODB_URI:
'mongodb://localhost:27017/collection1?readPreference=primary&ssl=false',
},
env_production: {
NODE_ENV: 'production',
},
},
],
deploy: {
production: {
// user: "SSH_USERNAME",
// host: "SSH_HOSTMACHINE",
},
},
};
2 . Run server (dev/ prod)
pm2 start ecosystem.config.js
pm2 start ecosystem.config.js --env production
3 . More information :
https://pm2.keymetrics.io/docs/usage/watch-and-restart/

You need to specify the app location to the --watch option
myapp$ pm2 start bin/www --watch /your/location/to/app

I never managed to make default watch settings work in Ubuntu, however using polling via advanced watch options worked:
"watch": true,
"ignore_watch" : ["node_modules"],
"watch_options": {
"usePolling": true,
"interval": 1000
}
More info:
https://github.com/buunguyen/PM2/blob/master/ADVANCED_README.md#watch--restart
https://github.com/paulmillr/chokidar#api

Related

pm2 --watch is logging every 3 seconds irrespective of config file

We have the below config file for pm2:
module.exports = {
apps: [
{
script: 'index.js',
// ------------------------------------ watch options - begin
watch: ['/testing'],
watch_delay: 10000,
ignore_watch: ['node_modules', 'logs'],
watch_options: {
followSymlinks: false,
},
// ------------------------------------ watch options - end
error_file: 'logs/err.log',
out_file: 'logs/out.log',
log_file: 'logs/combined.log',
time: true,
},
],
deploy: {
production: {
user: 'SSH_USERNAME',
host: 'SSH_HOSTMACHINE',
ref: 'origin/master',
repo: 'GIT_REPOSITORY',
path: 'DESTINATION_PATH',
'pre-deploy-local': '',
'post-deploy':
'npm install && pm2 reload ecosystem.config.js --env production',
'pre-setup': '',
},
},
};
With this in place, and a single index.js file:
console.log(`testing`);
We get 'testing printed to the log file every 3 seconds;
2021-05-31T12:02:39: testing
2021-05-31T12:02:42: testing
2021-05-31T12:02:45: testing
2021-05-31T12:02:48: testing
2021-05-31T12:02:51: testing
2021-05-31T12:02:55: testing
There are no changes to the files, and this isn't the logs directory or files being monitored as they're excluded with ignore_watch: ['node_modules', 'logs'],.
Why isn't --watch only monitoring for file changes, and instead logging every 3 seconds?
PM2 is detecting that your app is exiting and restarting your app.
You can choose what PM2 should do when it detects when your app exists, such as --no-autorestart.
Example log output:
$ pm2 --watch --no-autorestart --ignore-watch=node_modules start index.js
$ pm2 logs -f
PM2 | App name:index id:0 online
0|index | testing
PM2 | App [index] with id [0] and pid [48907], exited with code [0] via signal [SIGINT]
# Modify index.js to log `testing 2`.
PM2 | Change detected on path index.js for app index - restarting
PM2 | App name:index id:0 online
0|index | testing 2
PM2 | App [index] with id [0] and pid [48910], exited with code [0] via signal [SIGINT]

Is it possible to use ignore_watch parameter by extension or wildcard in PM2?

I'm using pm2 for my node application. But whenever I travel between folders, or if I click to "stage changes" in VSCode, PM2 restarts the application.
I believe osX creating files like .DS_Store etc. and that triggers pm2 watch.
So I can add a wildcard to ignore that kind of file for every folder.
This is how my process.json looks like;
{
"apps": [{
"name": "server",
"script":"index.js",
"max_memory_restart": "1024M",
"node_args":"--max_old_space_size=2048",
"watch":true,
"ignore_watch":["ext/server/reports", "node_modules", ".git", ".vscode"],
"args":"dev",
"env_local": {
"NODE_ENV": "local"
},
...
}]
}
Yes, it is possible. Put a file named as ecosystem.config.js on the root of project.
module.exports = {
apps : [{
name: 'project_name',
script: 'index.js',
instances: 1,
autorestart: true,
restart_delay: 5000,
watch: true,
max_memory_restart: '512M',
ignore_watch: ['log-*.txt'],
error_file: 'log-error.txt',
out_file: 'log-output.txt'
}]
};
Here the parameter as ignore-watch will help you. e.g. In my case, I am ignoring the files as log-output.txt, log-error.txt.

Using Environment Variables with Vue.js

I've been reading the official docs and I'm unable to find anything on environment variables. Apparently there are some community projects that support environment variables but this might be overkill for me. So I was wondering if there's something simple out of the box that works natively when working on a project already created with Vue CLI.
For example, I can see that if I do the following the right environment prints out meaning this is already setup?
mounted() {
console.log(process.env.ROOT_API)
}
I'm a kinda new to env variables and Node.
FYI using Vue CLI version 3.0 beta.
Vue.js with Webpack
If you use vue cli with the Webpack template (default config), you can create and add your environment variables to a .env file.
The variables will automatically be accessible under process.env.variableName in your project. Loaded variables are also available to all vue-cli-service commands, plugins and dependencies.
You have a few options, this is from the Environment Variables and Modes documentation:
.env # loaded in all cases
.env.local # loaded in all cases, ignored by git
.env.[mode] # only loaded in specified mode
.env.[mode].local # only loaded in specified mode, ignored by git
Your .env file should look like this:
VUE_APP_MY_ENV_VARIABLE=value
VUE_APP_ANOTHER_VARIABLE=value
As noted in comment below:
If you are using Vue cli 3, only variables that start with VUE_APP_ will be loaded.
Don't forget to restart serve if it is currently running.
Vue.js with Vite
Vite exposes env variables that start with VITE_ on the special import.meta.env object.
Your .env should look like this:
VITE_API_ENDPOINT=value
VITE_API_KEY=value
These variables can be accessed in Vue.js components or JavaScript files under import.meta.env.VITE_API_ENDPOINT and import.meta.env.VITE_API_KEY.
Tip: Remember to restart your development server whenever you change or add a variable in the .env file if it's running.
For more info, please see the Vite documentation for env variables.
If you are using Vue cli 3, only variables that start with VUE_APP_ will be loaded.
In the root create a .env file with:
VUE_APP_ENV_VARIABLE=value
And, if it's running, you need to restart serve so that the new env vars can be loaded.
With this, you will be able to use process.env.VUE_APP_ENV_VARIABLE in your project (.js and .vue files).
Update
According to #ali6p, with Vue Cli 3, isn't necessary to install dotenv dependency.
Create two files in root folder (near by package.json) .env and .env.production
Add variables to theese files with prefix VUE_APP_ eg: VUE_APP_WHATEVERYOUWANT
serve uses .env and build uses .env.production
In your components (vue or js), use process.env.VUE_APP_WHATEVERYOUWANT to call value
Don't forget to restart serve if it is currently running
Clear browser cache
Be sure you are using vue-cli version 3 or above
For more information: https://cli.vuejs.org/guide/mode-and-env.html
In the root of your project create your environment files:
.env
.env.someEnvironment1
.env.SomeEnvironment2
To then load those configs, you would specify the environment via mode i.e.
npm run serve --mode development //default mode
npm run serve --mode someEnvironment1
In your env files you simply declare the config as key-value pairs, but if you're using vue 3, you must prefix with VUE_APP_:
In your .env:
VUE_APP_TITLE=This will get overwritten if more specific available
.env.someEnvironment1:
VUE_APP_TITLE=My App (someEnvironment1)
You can then use this in any of your components via:
myComponent.vue:
<template>
<div>
{{title}}
</div>
</template>
<script>
export default {
name: "MyComponent",
data() {
return {
title: process.env.VUE_APP_TITLE
};
}
};
</script>
Now if you ran the app without a mode it will show the 'This will get...' but if you specify a someEnvironment1 as your mode then you will get the title from there.
You can create configs that are 'hidden' from git by appending .local to your file: .env.someEnvironment1.local - very useful for when you have secrets.
Read the docs for more info.
A problem I was running into was that I was using the webpack-simple install for VueJS which didn't seem to include an Environment variable config folder. So I wasn't able to edit the env.test,development, and production.js config files. Creating them didn't help either.
Other answers weren't detailed enough for me, so I just "fiddled" with webpack.config.js. And the following worked just fine.
So to get Environment Variables to work, the webpack.config.js should have the following at the bottom:
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
Based on the above, in production, you would be able to get the NODE_ENV variable
mounted() {
console.log(process.env.NODE_ENV)
}
Now there may be better ways to do this, but if you want to use Environment Variables in Development you would do something like the following:
if (process.env.NODE_ENV === 'development') {
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"'
}
})
]);
}
Now if you want to add other variables with would be as simple as:
if (process.env.NODE_ENV === 'development') {
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
ENDPOINT: '"http://localhost:3000"',
FOO: "'BAR'"
}
})
]);
}
I should also note that you seem to need the "''" double quotes for some reason.
So, in Development, I can now access these Environment Variables:
mounted() {
console.log(process.env.ENDPOINT)
console.log(process.env.FOO)
}
Here is the whole webpack.config.js just for some context:
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.css$/,
use: [
'vue-style-loader',
'css-loader'
],
}, {
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
if (process.env.NODE_ENV === 'development') {
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"development"',
ENDPOINT: '"http://localhost:3000"',
FOO: "'BAR'"
}
})
]);
}
This is how I edited my vue.config.js so that I could expose NODE_ENV to the frontend (I'm using Vue-CLI):
vue.config.js
const webpack = require('webpack');
// options: https://github.com/vuejs/vue-cli/blob/dev/docs/config.md
module.exports = {
// default baseUrl of '/' won't resolve properly when app js is being served from non-root location
baseUrl: './',
outputDir: 'dist',
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
// allow access to process.env from within the vue app
'process.env': {
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
}
})
]
}
};
In vue-cli version 3:
There are the three options for .env files:
Either you can use .env or:
.env.test
.env.development
.env.production
You can use custom .env variables by using the prefix regex as /^/ instead of /^VUE_APP_/ in /node_modules/#vue/cli-service/lib/util/resolveClientEnv.js:prefixRE
This is certainly not recommended for the sake of developing an open source app in different modes like test, development, and production of .env files. Because every time you npm install .. , it will be overridden.
In addition to the previous answers, if you're looking to access VUE_APP_* env variables in your sass (either the sass section of a vue component or a scss file), then you can add the following to your vue.config.js (which you may need to create if you don't have one):
let sav = "";
for (let e in process.env) {
if (/VUE_APP_/i.test(e)) {
sav += `$${e}: "${process.env[e]}";`;
}
}
module.exports = {
css: {
loaderOptions: {
sass: {
data: sav,
},
},
},
}
The string sav seems to be prepended to every sass file that before processing, which is fine for variables. You could also import mixins at this stage to make them available for the sass section of each vue component.
You can then use these variables in your sass section of a vue file:
<style lang="scss">
.MyDiv {
margin: 1em 0 0 0;
background-image: url($VUE_APP_CDN+"/MyImg.png");
}
</style>
or in a .scss file:
.MyDiv {
margin: 1em 0 0 0;
background-image: url($VUE_APP_CDN+"/MyImg.png");
}
from https://www.matt-helps.com/post/expose-env-variables-vue-cli-sass/
Important (in Vue 4 and likely Vue 3+ as well!): I set VUE_APP_VAR but could NOT see it by console logging process and opening the env object. I could see it by logging or referencing process.env.VUE_APP_VAR. I'm not sure why this is but be aware that you have to access the variable directly!
For those using Vue CLI 3 and the webpack-simple install, Aaron's answer did work for me however I wasn't keen on adding my environment variables to my webpack.config.js as I wanted to commit it to GitHub. Instead I installed the dotenv-webpack plugin and this appears to load environment variables fine from a .env file at the root of the project without the need to prepend VUE_APP_ to the environment variables.
I am having same problem in vuecli#5. Trying to solve by reading official doc but can't get proper solution. After long time i got solution and it works fine.
Create .env file on root dir. touch .env
Set value on it i.e APP_NAME=name
vue.config.js file past it process.env.VUE_APP_VERSION = require('./package.json').version
Log to any method console.log(process.env.APP_NAME);
Running multiple builds with different .env files 🏭
In my app I wanted to have multiple production builds, one for a web app, and another for a browser extension.
In my experience, changing build modes can have side effects as other parts of the build process can rely on being in production for example, so here's another way to provide a custom env file (based on #GrayedFox's answer):
package.json
{
"scripts": {
"build": "vue-cli-service build",
"build:custom": "VUE_CLI_SERVICE_CONFIG_PATH=$PWD/vue.config.custom.js vue-cli-service build",
}
}
vue.config.custom.js
// install `dotenv` with `yarn add -D dotenv`
const webpack = require("webpack");
require("dotenv").config({ override: true, path: "./.env.custom" });
module.exports = {
plugins: [new webpack.EnvironmentPlugin({ ...process.env })],
};
Note 1: VUE_CLI_SERVICE_CONFIG_PATH swaps out the config from the default of vue.config.js, so any settings set in there will not apply for the custom build.
Note 2: this will load .env.production before .env.custom, so if you don't want any of the environment variables set in .env.production in your custom build, you'll want to set those to a blank string in .env.custom.
Note 3: If you don't set override: true then environment variables in .env.production will take precedence over .env.custom.
Note 4: If you are looking to have multiple different builds using vue-cli, the --skip-plugins option is very useful.
**just install this **
npm install -g #vue/cli
at your project
it is worked with me

How to prevent grunt-nodemon from restarting all apps

I'm running node on Windows 10. I have three node apps and I want to be able to start them all up with one handy grunt command. Furthermore, I want node to automatically restart if I modify any of the apps.
I'm using a combination of grunt-nodemon and grunt-concurrent for this. The node processes all start up fine.
The problem is that if I modify the code related to any of them they all restart, which takes a long time. How can I make it so that nodemon only restarts the app whose code I actually modified?
var loadGruntTasks = require('load-grunt-tasks')
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concurrent: {
runAll: {
tasks: ['nodemon:app1', 'nodemon:app2', 'nodemon:app3'],
options: {
logConcurrentOutput: true
}
}
},
nodemon: {
app1: {
script: './app1/app.js'
},
app2: {
script: './app2/app.js'
},
app3: {
script: './app3/app.js'
}
}
})
loadGruntTasks(grunt)
grunt.registerTask('default', ['concurrent:runAll'])
}
Update
If I use grunt-watch instead of grunt-nodemon, only the app whose code I modified will restart. The problem is that grunt-watch only knows to run node app.js which gives an error because the app is already running. Is there a way to make grunt-watch kill the node process and restart it?
I think the answer could be fairly simple. Nodemon has an ignore option. For each of your three applications nodemon grunt configurations you can configurate them to ignore the directories of the other applications. That way they only kick off their restart when their own files are changed and not those of other projects. Let me know how that goes. :) Specifics about setting up the ignore section of config can be found in both nodemons documentation and grunt-nodemons documentation.
Patrick Motard's answer made me think about what directory nodemon was running in and how it was observing the files for changes. It appears that since I started grunt inside the parent directory of all the node apps that each nodemon process was looking for changes in all of those directories. So I set the working directory of the nodemon processes to the corresponding directory for each app using the options.cwd setting. That seemed to fix it. Here is the working solution:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concurrent: {
runAll: {
tasks: ['nodemon:app1', 'nodemon:app2', 'nodemon:app3'],
options: {
logConcurrentOutput: true
}
}
},
nodemon: {
app1: {
script: 'app.js',
options: {
cwd: './app1'
}
},
app2: {
script: 'app.js',
options: {
cwd: './app2'
}
},
app3: {
script: 'app.js',
options: {
cwd: './app3'
}
}
}
})
loadGruntTasks(grunt)
grunt.registerTask('default', ['concurrent:runAll'])
}

How do I get the debugger to recognize the sourcemaps in webstorm 10 using the react starter kit

I created a sample react starter kit project in webstorm using webstorm's pre-defined project template and am trying to set breakpoints in debug mode.
I first built the project using npm run build then set the debug configuration to run build/server.js.
However it won't recognize any of the breakpoints in the original source files and seems to be ignoring the sourcemaps. How can I get it to recognize the sourcemaps and allow me to both set breakpoints in the source files as well as step into the source files.
There is this issue in the react starter kit repo: https://github.com/kriasoft/react-starter-kit/issues/121 but I couldn't see what the resolution was, and unlike the commenter, I couldn't even get it to step into the source files... it just stayed on the generated js files instead.
Well...
WebStorm 10 has no support for sourcemaps generated by Webpack. They are partially supported in WebStorm 11 for client-side applications (see http://blog.jetbrains.com/webstorm/2015/09/debugging-webpack-applications-in-webstorm/), but not supported for Node.js.
so, you can't debug server.js in WebStorm 11, but you can debug client side. To do this, try the following:
change appConfig in src/config.js as follows:
const appConfig = merge({}, config, {
entry: [
...(WATCH ? ['webpack-hot-middleware/client'] : []),
'./src/app.js',
],
output: {
path: path.join(__dirname, '../build/public'),
filename: 'app.js',
},
devtool: 'source-map',
module: {
loaders: [
WATCH ? {
...JS_LOADER,
query: {
// Wraps all React components into arbitrary transforms
// https://github.com/gaearon/babel-plugin-react-transform
plugins: ['react-transform'],
extra: {
'react-transform': {
transforms: [
{
transform: 'react-transform-hmr',
imports: ['react'],
locals: ['module'],
}, {
transform: 'react-transform-catch-errors',
imports: ['react', 'redbox-react'],
},
],
},
},
},
} : JS_LOADER,
...config.module.loaders,
{
test: /\.css$/,
loader: 'style-loader/useable!css-loader!postcss-loader',
},
],
},
});
set up the javascript debug run configuration:
URL: http://localhost:5000
Remote URLs: map project root folder to 'webpack:///path/to/react-starter-kit', like 'webpack:///C:/WebstormProjects/react-starter-kit'
map build/public to http://localhost:5000
This doesn't work perfectly, but works in general - breakpoints in src/routes.js, src/app.js are hit

Resources