Best way to set a Node/Express app to live or production mode - node.js

I'm currently in the process of making my node/express app into a production deployment, and as part of this, I need to make it run in a production friendly mode (e.g. fewer debugs to stdOut, write logs to different places, tell users less when errors occur etc.).
I'm struggling a bit with this, as whenever I set a variable of virtually any kind to invoke a 'production' mode, it doesn't take affect for the program as it runs.
When launched in dev mode, my code runs through Gulp, and runs this script:
#!/usr/bin/env node
var debug = require('debug')('ascema');
var app = require('../app');
app.set('port', process.env.PORT || 3000);
var server = app.listen(app.get('port'), function() {
debug('Express server listening on port ' + server.address().port);
});
Which, as you know, is just the generated launch script from the express generator.
In order to run it in live mode, I created an alternative startup for the server to use (I could hardly use gulp anyway) and live.js runs this:
#!/usr/bin/env node
var app = require('./app.js');
app.set('env', 'production');
console.log('running on Port 3000');
var server = app.listen(3000);
But, when I use app.get('env') anywhere in the app (e.g. in app.js or in it's various dependencies) it still returns 'development' and so none of my production tasks happen.
What am I doing wrong here?
Many thanks.

You have to set the NODE_ENV variable on the command line when you execute your NodeJS application.
For example:
NODE_ENV=production node app.js
Also, NODE_ENV is an environment variable so if set it in the environment on your server, you will not need to provide it every time you execute your application so node app.js will do.
You can set your environment variables in the /etc/environment file. Here are more details on that.

By using NPM you might to be used the follow scripts in the package.json:
"scripts": {
"start": "nodemon ./bin/www",
"dev_win": "set NODE_ENV=development && node ./bin/www >> /romba/log/api.log 2>> /romba/log/error.log",
"prod_win": "set NODE_ENV=production && node ./bin/www >> /romba/log/api.log 2>> /romba/log/error.log"
"prod_nix": "NODE_ENV=production node ./bin/www >> /romba/log/api.log 2>> /romba/log/_error.log"
},...
To start one of the script use the command:
npm run-script prod_win
In the JavaScript code I check the condition:
process.env.NODE_ENV.indexOf('production') > -1
or with Express.js
app.use((err, req, res, next) => {
req.app.get('env') === 'development' ? ...

Related

What is the best way to get a Vue app to correctly read the env variables that have been set on Heroku?

I have a vue app that is being hosted on Heroku. Now, because heroku doesn't do static apps, I wrote a simple express server to serve this app
//server.js
const express = require('express')
const serveStatic = require('serve-static')
const path = require('path')
app = express()
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
req.headers['x-forwarded-proto'] !== 'https'
? res.redirect(301, 'https://' + req.hostname + req.originalUrl)
: next()
} else {
next()
}
})
app.use(serveStatic(path.join(__dirname, 'dist')))
const port = process.env.PORT || 80
app.listen(port)
And hooked the build process of the app to Heroku existing workflow through package.json
.
.
.
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"postinstall": "if test \"$NODE_ENV\" = \"production\" ; then yarn run build ; fi",
"start": "node server.js"
},
.
.
.
This whole thing works and technically so do the environment variables. However, I see that some env variables that I set on heroku (in their dashboards) yesterday are still undefined when I access them in vue using process.env.VUE_APP_XXX.
In development I use .env and that works as intended. My idea here is that because there's the express server in the middle things don't work as I would have expected.
Is there any way to make this thing work smoothly or am I better off using a static site hosting a-la Netflify?
Using ENV variables in Vue CLI (or Webpack build app in general) is done using Webpack DefinePlugin which replaces the values like process.env.VUE_APP_XXX in your code at build time
So if you define (or change) your variables directly in Heroku dashboard instead of .env file committed to repo, you need to rebuild you app on Heroku to pick up the changes...
If you are using ENV to configure any secrets (API keys etc), don't !! These values will be included directly in JS bundle of your app and easily accessible to anyone...
You can use heroku cli to set your env variables - documentation
$ heroku config:set DATABASE_URI=database_uri_here
$ heroku config:set SESSION_SECRET=session_secret
... and so on for each variable,
OR
if you have the env variables set in your .env file then you can do this as well
heroku config:set $(cat .env | sed '/^$/d; /#[[:print:]]*$/d')

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.

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

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

Node.js how to set server environment PORT and NODE_ENV?

How do I set the server environment to a certain port?
For instance,
const app = require('express')()
const isProd = (process.env.NODE_ENV === 'production')
const port = process.env.PORT || 3000
I will always get false for isProd and 3000 for port
I don't see the usefulness of these two lines and I can just set them below manually:
app.get('/', function (req, res) {
const data = {message: 'Hello World!'}
return res.status(200).json(data);
})
app.listen(3000, function () {
console.log('listening on port 3030!')
})
Do I need some config file in my root?
Any ideas?
I am using Linux/ Ubuntu/ Kubuntu.
Try running your server using below command
set NODE_ENV=production && set PORT=7000 && node server.js
Or for Linux
NODE_ENV=production PORT=7000 node server.js
This will set environment and port for your Node server.
Depends a bit on where you're hosting (e.g. windows or *nix) and how you're running your app (as a Windows or Linux service, using pm2, using systemd, etc).
The simplest way is to just change the command line call you start your app with, eg (linux):
NODE_ENV=prod PORT=34567 node myapp.js
or Windows
set NODE_ENV=prod && set PORT=34567 && node myapp.js
If you're using systemd or pm2 (and you should be), then they each have config files that allow you to set those variables for the environment the server is running in.
Pm2 docs: http://pm2.keymetrics.io/docs/usage/application-declaration/
Locally, you can just set defaults in your environment through normal means (in *nix that means exporting them in your shell config)

Can't get NODE_ENV to stick on my MEAN stack on open shift

I have:
added export NODE_ENV=production to app-root/data/.bashprofile (and echo $NODE_ENV shows production)
Tried rhc set-env NODE_ENV=production -a myapp which generates Setting environment variable(s) ... Server returned an unexpected error code: 501
Created an .openshift/action_hooks directory in my local repo and added the line export NODE_ENV=production to both a start and a pre_start file, committed and pushed to openshift
Tried the .openshift/action_hooks/pre_start_* aswell
Declared a root variable (as tipsed by #JuJoDi below) in the beginning of my server.js file, now it looks lite this:
var express = require('express'),
routes = require('./routes'),
api = require('./routes/api'),
http = require('http'),
path = require('path'),
everyauth = require('everyauth'),
connect = require('connect'),
env = process.env.NODE_ENV ? process.env.NODE_ENV : "process.env.NODE_ENV is null";
I'm then printing out using this route:
app.get('/version', function(req,res){
res.json({
"app.get('env')" : app.get('env'),
"process.env.NODE_ENV" : process.env.NODE_ENV,
"Global env" : env
});
});
What happens is that app.get('env') always returns 'development' and process.env.NODE_ENV is not printed at all (null value)
Has anyone got any ideas why this isn't working?
UPDATE
I created a minimalistic node server, still can't get any env-variables to work on open shift:
var http = require('http');
var port = process.env.OPENSHIFT_NODEJS_PORT || 3000;
var ipaddress = process.env.OPENSHIFT_NODEJS_IP || process.env.OPENSHIFT_INTERNAL_IP || 'localhost';
var env = process.env.NODE_ENV
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('' + process.env.NODE_ENV + "|" + env);
}).listen(port, ipaddress);
I couldn't reproduce this issue. I think the 501 error response that you initially received is the cause of your problems.
You should be able to troubleshoot by running the following from within in your project source folder (add -a YOUR_APP_NAME to the end of each command to run elsewhere):
Check your current list of application tokens:
rhc env list
If you have something listed for NODE_ENV, then clear it:
rhc unset NODE_ENV
Set your NODE_ENV to "production":
rhc env set NODE_ENV="production"
Verify that the value has been set by reloading your server, by running rhc env list, or by connecting to your application over SSH to check the system environment directly:
rhc ssh
env | grep NODE_ENV
Running rhc help env provides a lot of good usage info as well. Depending on how they're written, some servers need to be reloaded in order to fetch the new content.
I think the easiest way to get up and running with MEANStack on OpenShift is to use Yeoman's Angular-fullstack generator. It should automatically configure your environment for you.
That closure does not have access to the process object. Above that try
var env = process.env.NODE_ENV;
then print it with
app.get('/version', function(req,res){
res.json({
"app.get('env')" : app.get('env'),
"process.env.NODE_ENV" : env
});
});
When you run the app, try this:
NODE_ENV=production npm start
Substitute npm start with whatever way you start your app (node server or whatever).
That's the way I change the environment, when I am working locally. Since I usually want it to be development.
Express.js will default app.get('env') to development if process.env.NODE_ENV isn't set.
Update:
Sorry, missed the OpenShift part :/
https://www.openshift.com/developers/openshift-environment-variables
Looks like the environment variables in OpenShift need to go in:
.openshift/action_hooks/build
Using export. Have you tried that?

Resources