I have recently deployed my node js app to Heroku and am having some trouble getting my app to work. My app loads but when i try to sign in it breaks. As far as I can tell is the environment variable are not loading correctly for some reason. The db variable is a string taken from my .ENV file and is the Monogo URI used to connect to mongo db. For some reason when heroku runs it process.env.MongoURI is coming undefined. Does anybody know why this might be happening?
The specific error I am getting is.
Error
MongooseError: The `uri` parameter to `openUri()` must be a string, got "undefined". Make sure the first parameter to `mongoose.connect()` or `mongoose.createConnection()` is a string.
App.js
/* eslint-disable no-unused-vars */
/* eslint-disable comma-dangle */
/* eslint-disable no-console */
/* eslint-disable arrow-parens */
const createError = require('http-errors');
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const mongoose = require('mongoose');
const flash = require('connect-flash');
const session = require('express-session');
const passport = require('passport');
const dotenv = require('dotenv');
dotenv.config();
const indexRouter = require('./server/index');
const port = process.env.PORT || 3000;
const app = express();
require('./server/config/passport')(passport);
// DB Config
const db = process.env.MongoURI;
mongoose.connect(db, { useNewUrlParser: true, useUnifiedTopology: true }) // I beleieve this is the line that is breaking the code.
.then(() => { console.log('MONGODB Connected'); })
.catch(err => console.log(err));
app.use(expressLayouts);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
app.use('/', indexRouter);
// catch 404 and forward to error handler
app.use((req, res, next) => {
next(createError(404));
});
// error handler
app.use((err, req, res, next) => {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(port, () => {
console.log(`Running on port ${port}`);
});
module.exports = app;
You probably should use Heroku's built in environment variable editor for this purpose.
First of all, make sure that your .env is added to your .gitignore file. When you deploy your project to heroku, it will automatically adapt to the variables on its new host.
Find more: https://devcenter.heroku.com/articles/node-best-practices#be-environmentally-aware
Related
im trying to connect my express application to mongoDB and it works fine when I host the express application locally however when I upload it to Azure App Service it crashes.
What I've tried so far -
-Replacing the connection string with an environmental variable
-Removing the connection code entirely to identify exactly where the error occurs
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const mongoose = require('mongoose')
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
const workoutRoutes = require('./routes/workouts')
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
//Connect to mongoose server
//------------------------------------------------- ERROR CAUSED BY THIS BLOCK OF CONNECTION CODE
mongoose.connect(process.env.MONGO_URI).then(() => {
console.log('Connected to the mongo databse database');
}).catch((err) => {
console.log(err)
})
//------------------------------------------------- ERROR CAUSED BY THIS BLOCK OF CONNECTION CODE
app.use('/', indexRouter);
app.use('/users', usersRouter);
//app.use('/api/workouts', workoutRoutes)
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
Application settings in azure
Okay, so I've managed to work out the issue. It's with the mongoose version. I was using v6 so I downgraded to v5.11.10 and it worked perfectly. I don't know the exact part of mongoose v6 that is causing the issue but I've found a workaround for now.
let createError = require("http-errors");
let express = require("express");
let path = require("path");
let cookieParser = require("cookie-parser");
let logger = require("morgan");
const session = require("express-session");
let fileStore = require("session-file-store")(session);
const oneDay = 1000 * 60 * 60 * 24;
// Routers
let indexRouter = require("./routes/index");
let usersRouter = require("./routes/users");
let loginPage = require("./routes/login");
let app = express();
// System settings
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.set("trust proxy", 1);
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use(
session({
secret: "SECRET_KEY_FOR_SESSION",
saveUninitialized: true,
resave: false,
cookie: { maxAge: oneDay, secure: !true },
store: new fileStore(),
})
);
app.use("/", indexRouter);
app.use("/login", loginPage);
app.use("/users", usersRouter);
app.get("/createSession", (req, res, next) => {
res.send("This is sessionCreate file");
let mysession = req.session.save;
mysession.data = "LOGIN";
res.end;
});
app.get("/getSession", (req, res, next) => {
let getsession = req.session;
res.send(`${getsession.data}`);
res.end;
});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;
getsession.data === undefined?
I uploaded a variable to the session but it was not detected?
All system settings are set correctly but what is the error? Session => The session is being saved to a storage folder but cannot be read.
session_saved_folder
Specifies the boolean value for the Secure Set-Cookie attribute. When truthy, the Secure attribute is set, otherwise it is not. By default, the Secure attribute is not set.
Note be careful when setting this to true, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection.
Please note that secure: true is a recommended option. However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies. If secure is set, and you access your site over HTTP, the cookie will not be set. If you have your node.js behind a proxy and are using secure: true, you need to set "trust proxy" in express:
So I am using Express File Upload and when I attempt to send a post request to it, it returns the error from the error-handler that is set up, which essentially it couldn't find any files when I console.log(req.files) it returns undefined, which is why the error is being sent back, but I don't know how to fix the problem.
Index.js
router.post('/upload-avatar', async (req, res) => {
try {
console.log(req.files)
if(!req.files) {
res.send({
status: false,
message: 'No file uploaded'
});
} else {
//Use the name of the input field (i.e. "avatar") to retrieve the uploaded file
let avatar = req.files.avatar;
//Use the mv() method to place the file in upload directory (i.e. "uploads")
avatar.mv('./uploads/' + avatar.name);
//send response
res.send({
status: true,
message: 'File is uploaded',
data: {
name: avatar.name,
mimetype: avatar.mimetype,
size: avatar.size
}
});
}
} catch (err) {
res.status(500).send(err);
}
});
App.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const fileUpload = require('express-fileupload');
const cors = require('cors');
const bodyParser = require('body-parser');
const _ = require('lodash');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
app.use(fileUpload({
createParentPath: true
}));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I am using an azure server, and at the moment I am using postman to get it working first!
Thanks In Advance!
Middlewares should be ordered appropriately. Your file upload middleware was placed below your index router. So when a request hits the server Express would run your indexRouter’s handler before the upload middleware and unless your handler calls next(), the file upload middleware would not process your request. And you cannot call next() since it would mean “I’m done with my part, hand this request (req) to the next middleware/handler”.
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const fileUpload = require('express-fileupload');
const cors = require('cors');
const bodyParser = require('body-parser');
const _ = require('lodash');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(fileUpload({
createParentPath: true
}));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I'm trying to run a simple query from an express route:
var router = require('express-promise-router')()
const { Pool } = require('pg')
const pool = new Pool({
user: 'user',
password: 'password',
host: 'host',
port: 1234,
database: 'db'
})
router.get('/', async (req, res) => {
console.log('OK')
try {
const { rows } = await pool.query('Select VERSION()')
console.log(rows)
}
catch(e) {
console.log(e)
}
console.log('DONE')
})
module.exports = router
'OK' Prints after sending the request but rows, e, or 'DONE' never print. I'm following the async/await method directly from https://node-postgres.com/guides/async-express.
I've also came across a thread for koa-router where people were having issues with async await calls because of some middle-ware they added that wasn't synchronous
https://github.com/ZijianHe/koa-router/issues/358.
I'm not sure what middle-ware would cause this but here's my app.js that initializes all middle-ware:
var createError = require('http-errors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require("cors");
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var dataRouter = require("./routes/data");
var uploadRouter = require("./routes/upload")
var fundingRouter = require('./routes/chartData/fundingOverview')
var testRouter = require('./routes/test')
var authRouter = require('./routes/auth')
var session = require('express-session')
var MongoStore = require('connect-mongo')(session)
var passport = require('passport')
const config = require('config')
const mongo = config.get('mongo')
const mongoose = require('mongoose')
mongoose.connect(mongo, {
useUnifiedTopology: true,
useNewUrlParser: true,
useFindAndModify: false
}).then(res => {
console.log('connected')
}).catch(err => {
console.log(err)
})
var express = require('express');
const mountRoutes = require('./routes')
var app = express();
const bodyParser = require('body-parser')
app.use(bodyParser.json())
mountRoutes(app)
app.use(cors())
var sessionMiddleWare = session({
secret: 'top session secret',
store: new MongoStore({ mongooseConnection: mongoose.connection }),
resave: true,
saveUninitialized: true,
unset: 'destroy',
cookie: {
httpOnly: false,
maxAge: 1000 * 3600 * 24,
secure: false, // this need to be false if https is not used. Otherwise, cookie will not be sent.
}
})
app.use(sessionMiddleWare)
// Run production React server on Node server
if(process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
// End Run production React Server on Node Server
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// app.use('/upload', uploadRouter)
// app.use('/', indexRouter);
// app.use('/users', usersRouter);
// app.use('/data', dataRouter)
// app.use('/funding', fundingRouter)
// app.use('/login', usersRouter)
// app.use('/auth', authRouter)
// catch 404 and forward to error handler
// app.use(function(req, res, next) {
// next(createError(404));
// });
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I'm mounting the routes directly after body parser. That's the only middle-ware that's called before the routes and is required in order for me to get data into the back end.
I'm able to execute that simple query by putting it into a script file and running 'node test.js' (I.E without the router) and it works fine so I know it's not a problem with node-postgre.
I know this is a problem with the call stack not being totally synchronous but I'm confused as to what's not at this point. I even made the axios call on the front-end async/await with no luck (I don't think it was necessary though).
Any guidance would be help a lot.
EDIT:
I created a fresh express skeleton and hooked my front-end to make a call to a route on the new express server with the same code, it worked. It led me to discover the call wasn't being completed because I was running the server with Nodemon. When I start the server using 'yarn start' the async calls get processed correctly. The question now is what in nodemon makes async router calls not work?
You need to finish the request/response cycle in your middleware.
So in your code:
router.get('/', async (req, res) => {
console.log('OK')
try {
const { rows } = await pool.query('Select VERSION()')
console.log(rows)
res.status(200).json(rows)
}
catch(e) {
console.log(e)
res.status(500).json(e)
}
console.log('DONE')
})
I have added body-parser to my application app.js file. I have a routes folder and a controllers folder which handles my request.
Initially, I did not have body-parser added to my application. When I added body-parser and console logged req.body I got an empty object. When I console logged req.body.email, req.body.password, and req.body.displayName values from postman were read as undefined.
app.js
let createError = require('http-errors');
let express = require('express');
let path = require('path');
let cookieParser = require('cookie-parser');
let logger = require('morgan');
let bodyParser = require('body-parser');
let assert = require('assert');
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config()
}
let usersRouter = require('./routes/user');
let app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use('/user', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
// MongoDB Connection
const db = {};
const MongoClient = require('mongodb').MongoClient;
MongoClient.connect(process.env.MONGODB_CONNECT_URL, (err, client) => {
// Connection works dont worry about it
});
module.exports = app;
routes/user.js
const express = require('express');
const router = express.Router();
const user = require('../controllers/user');
router.post('/', user.createUser);
router.delete('/:id', user.deleteUser);
router.get('/:id', user.loginUser);
module.exports = router;
controllers/user.js
const bcrypt = require('bcryptjs');
const Joi = require('joi');
const ObjectId = require('mongodb').ObjectID;
exports.createUser = async (req, res, next) => {
console.log('Request body: ', req.body);
const email = req.body.email;
const password = req.body.password;
const displayName = req.body.displayName;
console.log('Email: ', email);
console.log('Password: ', password);
console.log('Display name: ', displayName);
};
Please make sure that you are adding content-type header in postman content-type : application/json also in body tab select raw and beside raw select json from drop-down list.
Check this
https://i.stack.imgur.com/ZDhcl.png
You are probably trying to send form-data with Postman which sends a multipar body, body parser cannot handle multipart bodies. For handling multipart bodies you have to use a different module, I normally use multer.
With multer installed you just have to include it and it as middleware (under you body-parser for instance) using none() since in this case you want to handle text-only multipart body (More information about this in multer docs
let multer = require('multer');
app.use(multer().none());
Besides that I wanted to mention you are including two body parsers in your code, the express body parser
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
and an external body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
Pick one, you don't need both, the best option for me would be to keep the one that comes with express, this way you don't have to install any more external packages.