How can I define dev & production ENV using express & mysql2? - node.js

I found some other similar answers to this, but I was not confident in my ability to comprehend and get it right, so I'm hoping to get a little validation on my approach if it's right, seeing as what i've cobbled together is from a tutorial. I've also included the requires, as I'm not actually calling express in this file (db.js) but it is used in other places:
(PS. I am deploying to Heroku, and using JawsDB as my production DB)
require("dotenv").config();
const mysql = require("mysql2");
//const { DatabaseError } = require('pg');
const pool = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
});
and this is what i'm thinking of doing:
app.configure('production', function(){
app.locals.URLs = {
const pool = mysql.createPool({
host: process.env.DB_HOST_LOCAL,
user: process.env.DB_USER_LOCAL,
database: process.env.DB_NAME_LOCAL,
password: process.env.DB_PASSWORD_LOCAL,
}
});
app.configure('development', function(){
app.locals.URLs = {
const pool = mysql.createPool({
host: process.env.DB_HOST_JAWS,
user: process.env.DB_USER_JAWS,
database: process.env.DB_NAME_JAWS,
password: process.env.DB_PASSWORD_JAWS,
}
});
is this right, and will I need to require app = require('express')?

If you want to inject anything into express (router, middleware, configuration, template engine, etc) then you're gonna need "app". With locals you are creating a "URLs" property inside your app instance.
This is not a big deal but you could instead module.exports = pool; as an independent js module and use it regardless of Express.
About your env, be cautious no to leak production passwords into development nor a developer's local configuration into production. That said you don't need a if.. else for your configuration. I also spot you don't have an env configuration for tests...
Here in the docs the prefered way to load your vars is by preloading them.
node -r dotenv/config your_script.js
You can also customize your .env file if you enjoy .env.local .env.production .env.test approach
node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
This also allows you to have multiple settings without worry of conflicts / unintended overrides or leftovers.
You could also
inject individual variables via cross-env
inject production variables via PM2

Related

Cant access variables from .env file in node.js application

I am trying to dockerize a strapi app with mongodb atlas database. The issue I am facing is that database file in /config is not reading the variable from .env file.
.env file
HOST=0.0.0.0
PORT=1337
DATABASE_HOST=xyz.mongodb.net
DATABASE_USERNAME=abc-admin
DATABASE_PASSWORD=12345xyz
ADMIN_JWT_SECRET=abcd1234
Database connection code
const {
DATABASE_HOST,
DATABASE_USERNAME,
DATABASE_PASSWORD
} = process.env;
module.exports = ({ env }) =>
({
defaultConnection: 'default',
connections: {
default: {
connector: 'mongoose',
settings: {
host: env('DATABASE_HOST', process.env.DATABASE_HOST),
srv: env.bool('DATABASE_SRV', true),
port: env.int('DATABASE_PORT', 27017),
database: env('DATABASE_NAME', 'xyz-dev'),
username: env('DATABASE_USERNAME', process.env.DATABASE_USERNAME),
password: env('DATABASE_PASSWORD', process.env.DATABASE_PASSWORD)
},
options: {
authenticationDatabase: env('AUTHENTICATION_DATABASE', null),
ssl: env.bool('DATABASE_SSL', true),
},
},
},
});
I have tried with process.env and without it in the above file. But when I run the image after build it shows below error
error Error connecting to the Mongo database. URI does not have
hostname, domain name and tld
Any idea what I am doing wrong here? Thanks
One option is to use dotenv you need to import dotenv and run dotenv.config() before you can start using env variables
so change to
import dotenv from "dotenv";
dotenv.config()
// your code which user process.env
other option is to define all those env variable on your OS level. On unix you can add to ~/.bashrc file
Here's a bit more elaborate answer to your question (after reading your comments). Creating .env file means you just created it. It doesn't get automatically loaded. It's a typical way to use on unix machines, but has no relation to Node whatsoever.
What you need to do is somehow parse the content of that file (which is purely text), convert it to key-value pairs and pass it to node. There are many packages, and one that Amit showed is dotenv. It does all the work for you, and at the end, you get your variables injected inside process.env.
The simplest way would be to install this package (from npm) and use it as described. But if you cannot modify the code in any way, then you can simply parse the content of the file with a script, and then start the node server. Here's an example (taken from npm scripts: read .env file):
"scripts": {
"example": "some-lib --argument --domain $(grep DOMAIN .env | cut -d '=' -f2)"
}
The drawback here is that it doesn't work across various operating systems and that using a specific library for that is way more tested than your manual scripts.

Hide password by ignoring file in .gitignore, but unable to deploy to Heroku?

I was trying to deploy my Node JS application to Heroku. Heroku was connected to my Github account and deployed through Github. In my Node JS application, I created a SQL pool file awsPool.js using the following code:
const mysql = require('mysql');
const awsPool = mysql.createPool({
connectionLimit: 10,
host: "myDb.abcdefg.eu-west-2.rds.amazonaws.com",
user: "myUsername",
password: "myPassword",
port: 3306,
database: 'myDb',
debug: false
});
module.exports = awsPool;
And imported it in my Express application. The pool contains credentials such as my username and password, so I set them as ignored in .gitignore. However, when trying to deploy the application to Heroku, Heroku gave me this error:
Error: Cannot find module './awsPool'
I understand this is likely due to awsPool.js being not tracked in my Github, but how can I properly hide my credentials and deploy to Heroku?
You can use ponto .env files that will define your credentials. So when you are going to deploy your application on Heroku you will only need to define your environment variables

in nodejs app how to use dotenv and config?

I am new to development, i understand that dotenv create environment variables that we don't want to expose in our code , while config create similar variables to be used by app for configuration however I am little confused about use case. could you explain it a little or point me to further resource for better understanding. and is there any other similar packages that create environment variables and how are they used ?
The way I do it is using both dotenv and config packages.
You'd create a .env file (which you'll add to .gitignore)
For example
client_id='1234'
client_secret='XXXXX'
Then you create a folder called config at the root of your project, and inside it a file default.js
In this file you'll add first
require('dotenv').config();
and then you can export js friendly variables
require('dotenv').config();
export const client = {
clientId: process.env.client_id,
clientSecret: process.env.client_secret
}
Finally you can use these in your index.ts for example
import config from 'config';
console.log(config.get('client'));
// { clientId: '1234', clientSecret: 'XXXXX'}
You can use dotenv npm package for this use case
create a .env file in your project something similar to:
DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3
then you can configure your file:
const dotenv = require('dotenv');
dotenv.config({ path: path.resolve(__dirname, './config.env') })
then you can use them like:
db.connect({
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS
})

How to fix "Error: The server does not support SSL connections" when trying to access database in localhost?

I am following Heroku's node.js tutorial to provision a Postgres database.
After creating a simple table and connecting to localhost:5000/db, I get an error saying "Error: The server does not support SSL connections".
I've been searching for solutions for hours but can't seem to fix it. Your help will be greatly appreciated. Thank you!
Here I provide a workaround for the question, and not the title of this post. A better answer to the post overall would probably address configuring SSL for the local machine.
I arrived here trying to resolve the problem of finishing that Heroku tutorial mentioned in the question and setting up the Postgres database to work locally as well as remotely.
const pool = new Pool({
connectionString: process.env.DATABASE_URL || 'postgresql://postgres:<your admin password>#localhost:5432/<your db name>',
ssl: process.env.DATABASE_URL ? true : false
})
My idea is to use SSL on the app I deploy but dodge SSL altogether on the local machine. By simply skipping SSL config on the local machine I am able to concentrate my efforts on developing a working app that still uses Heroku's built in SSL.
I use the Heroku environment variables to detect their environment versus my own and I select values accordingly in the code sample above. For me this has worked both locally and remotely.
This is the new form Heroku is working today in 2021, they made small corrections in the connection.
const pool = (() => {
if (process.env.NODE_ENV !== 'production') {
return new Pool({
connectionString: process.env.DATABASE_URL,
ssl: false
});
} else {
return new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
}
});
} })();
They changed a bit, using this way you can keep local and heroku available.
I had the same issue for setting up my local environment after the tutorial. Sticking to the command heroku local to start my server fixed it for me. This command is detailed in the Run the app locally section of the tutorial.
In my case I added a Express server and a client side using React + Webpack. My package.json scripts for local development look like:
"dev": "run-p dev:*",
"dev:server": "heroku local -f Procfile.dev",
"dev:webpack": "webpack --watch --config webpack.dev.js --mode development",
run-p is just from npm-run-all to run all scripts starting with dev at the same time.
The server uses the heroku local command and a specific Procfile for local development (use -f flag to do this), where I start a server locally with nodemon to watch for changes (see the Procfile.dev below).
The webpack script builds and watches for changes on the client side with React.
Procfile.dev for local development:
web: nodemon index.js
Procfile for production:
web: node --optimize_for_size --max_old_space_size=920 --gc_interval=100 index.js
Then the same code for connecting to the DB from the Provision a database section works for me:
const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: {
rejectUnauthorized: false
}
});
I came here not looking for a localhost solution (I wanted to connect to my db which is online), however, this turned up as the first google result, so I'll add a solution for not localhost issues here:
Check out the .env file to duplicate the Environment variables you setup in your heroku environment; see here: https://devcenter.heroku.com/articles/heroku-local#set-up-your-local-environment-variables
const pool = new Pool({
connectionString: process.env.DATABASE_URL || 'postgresql://postgres:#localhost:5432/',
ssl: process.env.DATABASE_URL ? true : false
})
In your code, you add this snippet with the credentials and connection string details, here process.env.DATABASE_URL comes from environment file, if it is there as it will enable ssl mode, else in local without ssl it works. Make sure, you will mention env variable only for other than local. So on both environment it works.
This helped me, when I was using connectionUrl.
postgres://password:postgres#localhost:5432/database?sslmode=disable
You might have noticed I have added ?sslmode=disable at the end of
connection url.
For more information about sslmode check this out.
To fix this I had to edit the database.js file.
Path to file:
<path-to-your-strapi-project>/config/database.js
if you find something with ssl that is set to true set it to false. In my case, I used Mysql it looked like this:
ssl: env.bool('DATABASE_SSL', true)
Change it to:
ssl: env.bool('DATABASE_SSL', false)
Best solution that I found that worked only:
const client = new Client({
connectionString: productiondbLink || localdblink,
ssl: process.env.DATABASE_URL ? { rejectUnauthorized: false } : false
});
client.connect();

Should I use a NodeJS global to make config properties available to my application?

My question is relatively simple:
In NodeJS you have the ability to set globals using
GLOBAL.myConfigObject = {...}
My question for the developer community is whether or not this is best practice. If not, what would be a better way to relay config variables (such as api-url or Port or ip address) to the entire application.
Instead of setting a global for the config, use a global for the environment you're launching in. Setup a config directory that has each of your environments:
config/local.js
module.exports = {
port: 3000,
ipAddress: "127.0.0.1"
}
config/production.js
module.exports = {
port: 443,
ipAddress: "8.8.8.8"
}
in your main file where you spin up your server:
server.js
var config = require("./config/" + process.env.NODE_ENV)
// use your config however you need it
spinning up your server from the command line, you can then do:
NODE_ENV=local node server.js
This way, you have a single global variable indicating your environment, but you can use whatever configuration variables you need for that environment.
most common way for these config such as port , and api_keys , is enviorment variable.
bash
$ set PORT=8080
you can access environment variable like that
node.js
node> process.env.PORT
In my project case, it's very useful to set a connection config. I use it for all of my server and if I have to change my database, I can switch pretty easily

Resources