Simple way to configure NodeJS environment - node.js

I am really just getting into cloud stuff and have been creating Node JS apps as a hobby for a long time. I am using MongoDB as my database. I have now pushed my app on Heroku and am happy that it runs, but I've only realized some things like there is no local storage and no database without addons. So now I have a free Atlas (MongoDB) hosting so my Heroku app can use that as its database.
My app starts with a simple npm start which does the build stuff (like grunt) and then runs the node app. But how can I change the environment to be production vs deploy so that if I did something like npm start -prod it would use the cloud hosted database, but npm start -dev would use my PC's MongoDB instance?

This what I do -
Loading environment variables from an .env file and passing them to the Node.js runtime would be the ideal case.
const loadEnvironment = async function () {
fs.readFile('.env', 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
return (data);
});
}; //loadEnvironment
For instance, file name .env which contains 'prod' or 'dev'. Handle db connections based on the env obtain from this file.

Related

How to set development database connection and production in same project - Node JS + MongoDB + Mongoose

I am building some application with NODEJS and Mongodb.
I push my commits to github (development) and to Heroku (Production).
It is frustrating to have to change every time I need to make my push the database connection:
const mongoose = require('mongoose');
// process.env.MONGODB_URI => for PRODUCTION HEROKU
// `mongodb://localhost/myProject` => for Local Development + GitHub Repo
mongoose.connect(**process.env.MONGODB_URI**, {family:4,useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify:false, useCreateIndex:true })
.then(ok => {
console.log(`Connected to Mongo! Database name: "${ok.connections[0].name}"`)
})
.catch(err => {
console.error('Error connecting to mongo', err)
});
I would like to make some kind of function to set connections when I push commits, for each situation.
For example, this logic:
mongoose.connect(if process.env.MONGODB_URI works, get it. If not, get this one: `mongodb://localhost/myProject`)
Or even better:
if request is in http://localhost:3000/ => get this connection `mongodb://localhost/myProject
if not get this one => process.env.MONGODB_URI
I don't really want a solution, just I would like to know if that is possible, and first step to reach it.
Thanks.
Take a look at dotenv
https://www.npmjs.com/package/dotenv
Be sure to include the .env file in your .gitignore.
you should not commit the .env file. It is common for developers to list the environment variables necessary to run the program in a README or similar internal documentation.
Some developers create and maintain a .sample-env file in the source code repository. This sample file would list all the environment variables used by the app, for example:
HOST=
PORT=
A developer would then clone the repository, copy the .sample-env file into a new .env file and fill in the values.
If your app is running on a physical machine or a virtual machine (for example, Digital Ocean droplets, Amazon EC2 and Azure Virtual Machines), then you can create a .env while logged into the server and it would run just as it is done on your local machine.

Accessing user uploaded images in a deployed MERN app

I am a relatively new developer and I have made a personal blog app, where a user can create a post and upload an image to use as the thumbnail for that post.
So far the backend and frontend work brilliantly and I am able to get the image, store it locally in a folder on my machine, store the file path in MongoDB and then access it and display it in the UI accordingly.
Now that I'm looking to finally deploy my application I have to figure out a way to upload images to an online cloud storage or something, where I can access them from my frontend as well.
Any suggestions on a good service of this kind and if possible, something free, suitable for my small project? Or any suggestions on an alternative way of dealing with this situation?
Any advice will be greatly appreciated!
Thanks in advance!
NOTE: I plan on deploying my app with Heroku, so if you've ever dealt with this issue directly using Heroku, please share your experience.
Yes, I have several apps that do just this! Sign up for a free MongoDB Atlas account and then you can store the data on their servers and point your Express app to the connection URL. Basically, they will give you a URL like this:
mongodb+srv://your-cluster-name:a232dfjoi39034r#atlas-free-cluster-czaoo.mongodb.net/blog-app?retryWrites=true
Which you can then store in a .env file like this:
MONGODB_URL=mongodb+srv://your-cluster-name:a232dfjoi39034r#atlas-free-cluster-czaoo.mongodb.net/blog-app?retryWrites=true
And access from your app like so:
mongoose.connect(process.env.MONGODB_URL, connectionOptions)
.catch((err) => {
console.log('Error on initial DB connection: ', err);
});
You'll need to load the .env files in your app using an npm packages such as dotenv on the development side. For heroku, you can use the heroku-cli
and set any environment variables for your app like so:
heroku config:set MONGODB_URL=mongodb+srv://your-cluster-name:a232dfjoi39034r#atlas-free-cluster-czaoo.mongodb.net/blog-app?retryWrites=true
Note, the development MongoDB URL could be a connection string to a local instance, such as:
MONGODB_URL=mongodb://localhost:27017/my-blog-app
And the one for heroku can be the MongoDB Atlas cluster.
There are a few other config things to do for Node apps, like having a 'start' script in package.json, which for Express apps created with express-generator looks like:
"scripts": {
"start": "node ./bin/www"
But for your case may be different. It should point to whatever file is the entry point for your server.
You'll also need to set the PORT from process.env.PORT, which heroku will set on their end, and on your end you can just default to 3000:
const PORT = process.env.PORT || 3000;
app.listen(PORT);
If you have any questions, feel free to PM me or just ask here and I can elaborate further. It can be intimidating the first few times, but it's actually really easy to deploy stuff to heroku this way once you get the hang of it! You can also check out the heroku docs for deploying Node apps.

Unable to access environment variables in google cloud (node js express app)

I am a beginner trying to learn web development. I have built small node js app and deployed in heroku. I am trying to do the same in GCP and learn the platform.
Code used is # https://github.com/unnikrishnan-r/firstgcpdeployment
The issue that I am facing is Unable to access env variables in the app
My index.js
exports.envVar = (req, res) => {
// Sends 'bar' as response
console.log("bbbccc");
console.log(process.env.JAWSDB_URL);
res.status(200).send(process.env.JAWSDB_URL);
return process.env.JAWSDB_URL;
};
.env.yaml is
JAWSDB_URL: testtest456
I then used below to deploy the function to gcloud
gcloud functions deploy envVar --env-vars-file .env.yaml --runtime nodejs8 --trigger-http;
Deployment was successful and was tested using console and
https://us-central1-howtodeployingcp.cloudfunctions.net/envVar
During deploy this happened
Can some one help me here?
It seems this line is problematic:
var dbUrl = envVar.envVar();
You're calling the function without passing in req, res as it's expecting. Perhaps you forgot to comment out this line.

Firebase Node.js admin SDK timeouts when trying to access Realtime DB

Using the Node.js admin SDK with Firebase Functions I get a timeout whenever I try to access the Realtime Database. This occurs only when testing a function locally (firebase serve --only functions,hosting) and when the default app is initialized using the functions.config().firebase.
This is a new behavior that started just a couple a days ago. However, if I try to initialize the default app with the serviceAccount.json file everything works as expected.
I'm using firebase-admin version 4.2.1 and firebase-functions version 0.5.9.
I wrote a straight forward http triggered function that fails due to timeout:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.database();
exports.testDbConnection = functions.https.onRequest((req, res) => {
return admin.database().ref().once('value')
.then(function(snapshot) {
res.json(snapshot);
}).catch(function(error) {
res.json(error);
});
});
from the documentation
Always end an HTTP function with send(), redirect(), or end(). Otherwise, your function might to continue to run and be forcibly terminated by the system
see https://firebase.google.com/docs/functions/http-events#terminate_http_functions
This might depend on the firebase-tools version that you are using, but looks familiar to this Github issue
The solution for it is to either upgrade to the latest version of the CLI or use the workaround solution:
Go to https://cloud.google.com/console/iam-admin/serviceaccounts
Click “Create service account”, give it a name (e.g. emulator), give it the Project>Owner role.Check “Furnish a new private key”, pick “JSON”.
Save the file somewhere on your computer
Run export GOOGLE_APPLICATION_CREDENTIALS="absolute/path/to/file.json"
Run firebase serve --only functions

Running a Node.js server and database on Azure

As a preface I am new to web development and have never published a site before.
I have built a website which runs fine locally and I want to publish it to the web using Azure.
The site uses a node.js server which I wrote myself (No express) which is connected to an SQLite3 database using the sqlite3 node module.
All I want to do is publish this site, I've tried using Azure to do so by using Azure command line tools to create a web app from the Git repo I have for the site.
I have a package.json file pointing to the server.js file which is the backend for the site, as well as serving files in the site it also returns data from an SQLite3 database I also have in the site folder. I also have the web.config file from this: https://github.com/projectkudu/kudu/wiki/Using-a-custom-web.config-for-Node-apps with the path to my server changed to match mine.
When I try to visit the site all I get is a blank screen, the application log gives this error: Error: SQLITE_CANTOPEN: unable to open database file
at Error (native)
So I'm guessing this means it has a problem having the DB built into the site in this way, if I comment out the database stuff it loads the site (minus the db content) just fine. When I try to test running the server in the azure console I get a "Bad Request" error, running on my own machine works fine.
My question is basically, how should I go about this goal of getting the site up given the challenges I've got? Is having an integrated db file completely the wrong approach or can I make it work? I've played around creating an azure DB but I cannot work out how to get the data from my db file into it. Are azure virtual machines the way to go, the advice I read was they're for more computationally intensive projects I'm only hosting a site?
I try to reproduce your issue on my side, and build a simple nodejs server with sqlite3 module on Azure Web Apps. But it works fine on my side, here are my test steps, you can try to follow my steps to fix your issue.
As to install sqlite3 requiring node-pre-gyp which is kind of Native Modules not supported via deployment task on Azure Web Apps. So we can install the sqlite3 module on local, and deploy the node_modules folder with the application togather to Azure.
As the nodejs runtime on Azure is in ia32 platform version. So we need to install with the command npm install sqlite3 --target_arch=ia32 on local.
Here is the code on my test:
var http = require("http");
var server = http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/html"});
response.write("<!DOCTYPE html>");
response.write("<html>");
response.write("<head>");
response.write("<title>Hello World Page</title>");
response.write("</head>");
response.write("<body>");
var sqlite3 = require('sqlite3').verbose();
var db = new sqlite3.Database('test.db');
var data = [];
db.serialize(function() {
db.run("CREATE TABLE IF NOT EXISTS lorem (info TEXT)");
var stmt = db.prepare("INSERT INTO lorem VALUES (?)");
for (var i = 0; i < 10; i++) {
stmt.run("Ipsum " + i);
}
stmt.finalize();
db.each("SELECT * FROM lorem", function(err, row) {
data.push(row);
}, function() {
response.write(JSON.stringify(data));
response.write("</body>");
response.write("</html>");
response.end();
});
});
db.close();
});
server.listen(process.env.PORT || 1337);
Any further concern, please feel free to let me know.

Resources