Search code examples
reactjsmongodbherokumerndotenv

Hiding my MongoURI variable in .env breaks my app on Heroku


I need help with this.

My original setup exposed my mongoURI.

I had a root folder called config that had a file called default.json

default.json

{
  "mongoURI": "mongodb+srv://name:[email protected]/test?retryWrites=true&w=majority",
  "jwtSecret": "secretKey"
}

I used that mongoURI variable in /server.js

server.js

const mongoose = require('mongoose');
const express = require('express');
const app = express();
const config = require('config');          // <<~~ to access /config dir
const cors = require('cors');
const path = require('path');


// Middleware
app.use(express.json());


// DB Config
// You can get all your config/ values with config.get('')
const db = config.get('mongoURI');                                // <<~~ access mongoURI var


// Connect to MongoDB
mongoose
    .connect(process.env.URI || db, {                        // <<~~ use mongoURI var
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useCreateIndex: true
    })
    .then(() => console.log("Connected to MongoDB.."))
    .catch(err => console.log(err));


// Available Routes
const tournaments = require('./routes/api/tournaments');
const users = require('./routes/api/users');
// Use Routes
app.use(cors());
app.use('/tournaments', tournaments);
app.use('/users', users);


// For Heroku deployment
if(process.env.NODE_ENV === 'production') {
    app.use(express.static(path.join(__dirname, 'client', 'build')));

    app.get('*', (req, res) => {
        res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'))
    });
};


// Run Server
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port: ${port}`));

I had that all pushed up to the repo at first, but my app is finished so I'm tying up loose ends so I want to hide that var.

First Attempt

  • I put the /config directory in .gitignore
  • ran git rm -rf --cached .
  • ran git add .

I did that since the file I was trying to gitignore had already been published to the repo, so I had to do this to clear that and re-push the entire app to github, this time actually hiding /config

RESULT

That worked on localhost, and the file was correctly hidden from GitHub.

BUT this actually broke my deployed Heroku version of the app. The app broke.

Second Attempt

  • Reverted that change
  • Installed dotenv library
  • Created a file .env and set my variables:

.env

MONGO_URI="mongodb+srv://name:[email protected]/test?retryWrites=true&w=majority"
JWT_SECRET="secretKey"
  • Replaced the call to /config in server.js to utilize this new ENV setup:

server.js

const mongoose = require('mongoose');
const express = require('express');
const app = express();
// const config = require('config');               // <<~~removed this
const cors = require('cors');
const path = require('path');
const dotenv = require('dotenv');                  // <<~~new line


// Middleware
app.use(express.json());


// DB Config
// You can get all your config/ values with config.get('')
// const db = config.get('mongoURI');                            // <<~~removed this
dotenv.config();                                            <<~~new line
const url = process.env.MONGO_URI;                           <<~~new line


// Connect to MongoDB
mongoose
    .connect(url, {                         <<~~tried this way
    .connect(process.env.URI || url, {      <<~~and this way
        useNewUrlParser: true,
        useUnifiedTopology: true,
        useCreateIndex: true
    })
    .then(() => console.log("Connected to MongoDB.."))
    .catch(err => console.log(err));


// Available Routes
const tournaments = require('./routes/api/tournaments');
const users = require('./routes/api/users');
// Use Routes
app.use(cors());
app.use('/tournaments', tournaments);
app.use('/users', users);


// For Heroku deployment
if(process.env.NODE_ENV === 'production') {
    app.use(express.static(path.join(__dirname, 'client', 'build')));

    app.get('*', (req, res) => {
        res.sendFile(path.join(__dirname, 'client', 'build', 'index.html'))
    });
};


// Run Server
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port: ${port}`));

RESULT

Works fine on localhost,

Heroku app starts without breaking,

BUT the app is not accessing data from mongodb.

I'm not sure what I did wrong. Sorry for lengthy post. Help! And thanks


Solution

  • It is a good practise to use .env only for local development and add it to .gitignore to avoid pushing to the source repository.

    Define the same local env variables as Config Vars: this can be done via the Heroku CLI or in the Heroku Dashboard (Web UI). Once deployed your application can access the env variables as you do locally (i.e. access DATABASE_URL config var with process.env.DATABASE_URL).

    This is also handy to keep separate your local dev from your Heroku configuration, as normally they would have different settings (dev vs prod).