These codes work perfectly locally and there is data saved to MongoDB and I could pull data from it. However when it is deployed to Heroku, the app hangs at the '.save()' portion. Needless, nothing is saved to MongoDB.
Am I doing anything wrong? Could it be the .env portion?
(Works on repl.it and glitch too but not locally from Visual Studio Code)
Full codes at github if anyone wants to take a peek
urlshortener.html
<form action="api/shorturl/new" method="POST">
<label for="url_input">URL to be shortened</label>
<input id="url_input" type="text" name="url" value="https://www.freecodecamp.org">
<input type="submit" value="POST URL">
</form>
package.json
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.0",
"dotenv": "^8.2.0",
"ejs": "^2.5.6",
"express": "^4.15.2",
"heroku": "^7.43.2",
"mongodb": "^3.6.2",
"mongoose": "^5.10.7",
"shortid": "^2.2.15"
server.js
// init project
require('dotenv').config();
var express = require('express');
var mongodb = require('mongodb');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var shortid = require('shortid');
var app = express();
var port = process.env.PORT || 3000
let uri = process.env.MONGODB_URI;
mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
var cors = require('cors');
app.use(cors({ optionsSuccessStatus: 200 })); // some legacy browsers choke on 204
// http://expressjs.com/en/starter/static-files.html
app.use(express.static('public'));
// http://expressjs.com/en/starter/basic-routing.html
app.get("/", function (req, res) {
res.sendFile(__dirname + '/views/index.html');
});
app.get("/urlshortener", function (req, res) {
res.sendFile(__dirname + '/views/urlshortener.html');
});
// testing API endpoint...
app.get("/api/hello", function (req, res) {
res.json({ greeting: 'hello API' });
});
// URL Shortener Microservice
// define the schema and build a model to store saved urls
let ShortUrl = mongoose.model('HerokuUrl', new mongoose.Schema({
original_url: String,
short_url: String,
suffix: String
}));
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post("/api/shorturl/new", (req, res) => {
let client_requested_url = req.body.url; // from input box
let suffix = shortid.generate(); // automatically generated
// this works
// res.json({
// 1: client_requested_url,
// 2: suffix
// })
let newUrl = new ShortUrl({
original_url: client_requested_url,
// short_url: client_requested_url + "/api/shorturl/" + suffix,
short_url: __dirname + "/api/shorturl/" + suffix,
suffix: suffix // suffix: suffix
})
// this works
// res.json({
// 'info': newUrl
// })
// APP HANGS at this save
newUrl.save((err, doc) => {
if (err) return console.error(err);
res.json({
original_url: newUrl.original_url,
short_url: newUrl.short_url,
suffix: newUrl.suffix // suffix: suffix
});
});
});
app.get("/api/shorturl/:suffix", (req, res) => {
let urlSuffix = req.params.suffix;
ShortUrl.findOne({ suffix: urlSuffix }).then(foundUrl => {
res.redirect(foundUrl.original_url);
});
})
// listen for requests
var listener = app.listen(port, function () {
console.log('Your app is listening on port ' + listener.address().port);
});
I got it working...
The app hangs at .save() so it has something got to do with the database.
It works locally but not when I push to heroku, so it has something got to do with the environment variable.
I typed heroku logs --tail and logged the environment variable which got undefined and this:
UnhandledPromiseRejectionWarning: 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.
So I snooped around Stackoverflow and added the database uri as a config variable in Heroku.
It worked!!!!
Thanks everyone.
Try using mongo db atlas for network storage
Related
I'm probably doing something silly, and putting myself in a situation that I don't need to be in. But my ultimate question is why do I have to have an empty folder in my server in order for my GET requests to work?
Before I start with an example, everything is under a subfolder: http://www.example.org/subfolder1
Here is Server code:
const express = require('express');
const datastore = require('nedb');
const url = require("url");
var path = require('path');
const app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server,
{
path: '/subfolder1/socket.io',
cors:
{
origin: '*',
methods: ["GET", "POST"],
credentials: true
}
});
const port = 3000;
io.on('connection', socket => {
console.log("Socket connected: " + socket.id);
});
app.get("/subfolder1/getSettings", (req, res, next) => {
res.json({ configuration: conf });
});
app.use(express.json());
app.get("/subfolder1/", function (req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
app.use(express.static(path.join(__dirname, 'public')));
server.listen(port, '0.0.0.0', function(error){
if(error) {
console.log('Server failed to listen: ', error)
} else{
console.log('Server is listening on port: ' + port)
}
});
client code:
const response = await fetch("/subfolder1/getSettings");
const settings = await response.json();
Now, in this example I'm calling getSettings (http://www.example.org/subfolder1/getSettings):
app.get("/subfolder1/getSettings", (req, res, next) => {
res.json({ configuration: conf });
});
No matter what I do, I will forever get a 404 error message unless I create an empty folder called "getSettings" in my folder structure on my server. Once I create the empty folder, the call works! So for now, I've just making empty folders on my server for my get calls. Well now look what happens when the URL is something more complicated, such as the following:
http://www.example.org/subfolder1/team=6/queuedPlayers (Where team can be any integer)
Now I'm stuck and my workaround is broken. Any ideas?
My app.js entire codes:
var express = require("express");
var app = express();
var port = 3000;
app.listen(port, () => {
console.log("Server listening on port " + port);
});
var mongoose = require("mongoose");
mongoose.Promise = global.Promise;
mongoose.connect("mongodb://localhost:27017/node-demo", { useNewUrlParser: true, useUnifiedTopology: true });
app.use("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
var nameSchema = new mongoose.Schema({
firstName: String,
lastName: String
});
var User = mongoose.model("User", nameSchema);
app.post("/addname", (req, res) => {
});
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/addname", (req, res) => {
app.post("/addname", (req, res) => {
var myData = new User(req.body.firstName, req.body.lastName);
myData.save()
.then(item => {
res.send("item saved to database");
})
.catch(err => {
res.status(400).send("unable to save to database");
});
})
});
This is my index.html file
<!DOCTYPE html>
<html>
<head>
<title>Intro to Node and MongoDB
</title>
</head>
<body>
<h1>Into to Node and MongoDB</h1>
<form method="post" action="/addname">
<label>Enter Your Name</label><br>
<input type="text" name="firstName" placeholder="Enter first name..." required>
<input type="text" name="lastName" placeholder="Enter last name..." required>
<input type="submit" value="Add Name">
</form>
</body>
<html>
After writing my code I open my mongod server than start mongo in other terminal then I run my app.js it worked properly it create server localhost:3000 then I went to my host I add my name in form and then submit, but it doesn't add to my database
Why this is not working?
My form data doesn't stored in my mongodb database. Please help me!
I think the body-parser library is deprecated on express 4 and later versions.
please use
app.use(express.json())
instead of body=parser.
not need to call any npm packages for parsing data from the form.
Here is the issue:
var myData = new User(req.body.firstName, req.body.lastName);
you have to pass an object to new User
var myData = new User( { firstName:req.body.firstName, lastName:req.body.lastName } );
i think i know what is wrong.
change app.use("/"... to app.get("/"...
bacause use handles all requests including POST. so when you send any request it sends the html file again and kills any other process.
app.get("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
I think you are not passing data correctly in the user model.
app.post("/addname", (req, res) => {
// make sure your request object is getting the same properties
var myData = new User(req.body.firstName, req.body.lastName);
myData.save()
.then(item => {
res.send("item saved to database");
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
As you updated the code for form, lastName is the fieldName. You should make the changes in the schema object too.
Also, Please check the request headers that comes in if it is of Accept:multipart/form-data then the bodyParser won't work instead you should use multer.
Some useful body parsers that you might need to consider if needed.
form-data: multer
x-www-form-urlencoded: express.urlencoded()
raw: express.raw()
json: express.json()
text: express.text()
Updated app.js file:
var express = require("express");
var app = express();
var port = 3000;
app.listen(port, () => {
console.log("Server listening on port " + port);
});
var mongoose = require("mongoose");
mongoose.Promise = global.Promise;
mongoose.connect("mongodb://localhost:27017/node-demo", { useNewUrlParser: true, useUnifiedTopology: true });
app.use("/", (req, res) => {
res.sendFile(__dirname + "/index.html");
});
var nameSchema = new mongoose.Schema({
firstName: String,
lastName: String
});
var User = mongoose.model("User", nameSchema);
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post("/addname", (req, res) => {
var myData = new User(req.body.firstName, req.body.lastName);
myData.save()
.then(item => {
res.send("item saved to database");
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
Note: you have added two extra routes with same route handler, I have just removed it. Update this file in your project and run it.
I am trying to connect my node app with MongoDB. The code seems to execute as I get the output
Server is running on port 5000
MongoDB database connection established successfully
on the terminal
But when I try to post get from insomnia it takes about two minutes before I get the error
Error: Server returned nothing (no headers, no data)
const express = require('express');
//const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
const uri = process.env.ATLAS_URI;
mongoose.connect( uri, {useUnifiedTopology: true, useNewUrlParser: true}, () => { console.log("MongoDB database conection established successfully")}).catch(err => console.log(err));
const exercisesRouter = require('./routes/exercises');
const usersRouter = require('./routes/users');
app.use('/exercises', exercisesRouter);
app.use('/users', usersRouter);
//emitter.setMaxListeners();
app.listen(port, () => {
console.log('Server is running on port : ' + port);
});
I am following a tutorial and these are the other files I have
exercise.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const exerciseSchema = new Schema({
username: {type: String,required: true},
description: {type: String,required: true},
duration: {type: Number,required: true},
date: {type: Date,required: true},
},
{
timestamps: true,
});
const Exercise = mongoose.model('Exercise', exerciseSchema);
module.exports = Exercise;
exercises.js
const router = require('express').Router();
let Exercise = require('../models/exercise.model');
router.route('/').get((req, res) => {
Exercise.find()
.then(exercises => res.json(exercises))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/add').post((req, res) => {
const username = req.body.username;
const description = req.body.description;
const duration = Number(req.body.duration);
const date = Date.parse(req.body.date);
const newExercise = new Exercise({
username,
description,
duration,
date,
});
newExercise.save()
.then(() => res.json('Exercise added!'))
.catch(err => res.status(400).json('Error: ' + err));
});
module.exports = router;
users.js
const router = require('express').Router();
let User = require('../models/user.model');
router.route('/').get((req,res) => {
User.find()
.then(users => res.json(users))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/add').post((req,res) => {
const username = req.body.username;
const newUser = new User({username});
newUser.save()
.then(() => res.join('User added!'))
.catch(err => res.status(400).json('Error: ' + err));
})
module.exports = router;
user.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlenght: 3
},
},{
timestamps: true,
});
const User = mongoose.model('User', userSchema);
module.exports = User;
I also get Cannot GET / when I go to http://localhost:5000 on my browser and in the inspector errors it says Refused to load the image 'http://localhost:5000/favicon.ico' because it violates the following Content Security Policy directive: "default-src 'none'". Note that 'img-src' was not explicitly set, so 'default-src' is used as a fallback. This may be a lot but I am trying to learn how to connect backend with front end and would appreciate it if someone can guide me. Thank you in advanced.
The problem seems to be CORS related. This means that the express API you have created, will not accept calls from other domains and expects the interaction to come from the same application. As you are are using insomnia, which is a separate desktop app or something, express will block access to routes. This is the default behavior.
You need to grab the CORS middleware and set up your routes as needed. Just follow the docs.
A quick test to allow all access:
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.get('/users', (req, res) => {...
...
Ther is an other method to add CORS
app.use(function (req, res, next) {
//Enabling CORS
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type,
Accept, x-client-key, x-client-token, x-client-secret, Authorization");
next();
});
I'm also going through the same tutorial: https://www.youtube.com/watch?v=7CqJlxBYj-M
The problem is with app.use(express.json());
Comment that out and it'll work fine. But then you won't be able to parse JSON data when you're doing post requests. I'm not sure why express.json() doesn't work.
But either way you can use Body-Parser to solve that problem. First user npm i body-parser to install it to your package manager in your backend folder. Then copy the below code to your index.js
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())
add this code and you'll be fine. This answer gives a detailed explanation of express.json & body-parser.
express.json() is a method inbuilt in express to recognize the incoming Request Object as a JSON Object.
I recommend using body-parser (it is an NPM package) to do the same thing. It is developed by the same peeps who built express and is designed to work with express. body-parser used to be part of express.
Decided to learn some web service development and rain into a problem. I cant seem to find a way how to use ExpressJS Router() to pass POST data to the controller files that i have. When i read contents of the request that was rerouted from product.route.js to the product.controllerjs i get
TypeError: Cannot read property 'name' of undefined
File structure as follows:
nodeTutorial/
app.js
controllers/
product.controller.js
models/
product.model.js
routes/
product.route.js
Node.js Dependencies
"dependencies": {
"body-parser": "^1.18.3",
"express": "^4.16.4",
"mongoose": "^5.4.1"
}
After hours of debugging i was able to get the request data without using any of the Express routing and just simply retrieve the data within the app.js by doing
app.post('/hello', function(req, res){
console.log("name: " + req.body.name + "; price: " + req.body.price);
res.json({requestBody: req.body})
});
This is good, but i would like to stay a little more organized and not put everything into a single file.
app.js code
//app.js
const express = require('express');
const bodyParser = require('body-parser');
const product = require('./routes/product.route'); // Imports routes for the products
const app = express();
// Set up mongoose connection
//........
app.use(express.json());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use('/products', product);
//Method that worked for me here (explained above)
let port = 1234;
app.listen(port, () => {
console.log('Server is up and running on port number ' + port);
});
product.route.js
const express = require('express');
const router = express.Router();
const product_controller =
require('../controllers/product.controller');
router.post('/create', product_controller.product_create);
module.exports = router;
product.controller.js
const Product = require('../models/product.model');
//Below is where i need to retrieve data from req
//As of now it prints 'req: undefined'
//And TypeError: Cannot read property 'name' of undefined
exports.product_create = function (req, res) {
console.log("req: "+ req.params.name);//doesnt work
//Also tried all of the below and all return undefied or an Empty Object
console.log("req.params.name: "+req.params.name);
console.log("req.params: "+ req.params);
console.log("req: "+ req.param("name"));
console.log("req.query.name: "+ req.query.name);
console.log("req.query: "+ req.query);
console.log("req.body.name: "+ req.body.name);
console.log("req.body: "+ req.body);
console.log("TEST");
let product = new Product(
{
name: req.body.name,
price: req.body.price
}
};
product.model.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ProductSchema = new Schema({
name: {type: String, required: true, max: 100},
price: {type: Number, required: true},
});
module.exports = mongoose.model('Product', ProductSchema);
When following post request is sent with name and price as body parameters i need to print them out and assign to Product Object
POST /products/create HTTP/1.1
Host: localhost:1234
Content-Type: application/x-www-form-urlencoded
cache-control: no-cache
name=apple%2Cprice=15
Sorry in advance if this is a simple solution, i am new to this whole web service thing.
So you want to handle product related routes in product.controller.
In your app.js, app.use('/products',product.controller)
In product.controller,
const express=require('express')
const router=express.Router()
router.get('/create',(req,res)=>{//your code})
Im just starting out with MEAN application, and im stuck while adding data into the database. So please help me to find the solution for this.
This is the root file, entry point in the application
//Importing modules
var express=require('express');
var mongoose=require('mongoose');
var bodyparser=require('body-parser');
var cors=require('cors');
var path=require('path');
var app=express();
const route=require('./routes/route');
//Connect to mongoDb
mongoose.connect('mongodb://localhost:27017/contactlist');
//on connection
mongoose.connection.on('connected',()=>{
console.log("Successfully established a connection to mongodb Database ")
});
//on error
mongoose.connection.on('error',(err)=>{
if(err){
console.log("Failed to established a connection "+err);
}
});
const port=3000;
//For routing
app.use('/api',route);
//Adding middleware -cors
app.use(cors());
//body-parser
app.use(bodyparser.json());
//Static Files
app.use(express.static(path.join(__dirname,'public')));
//port no
app.get('/',(req,res)=>{
res.send('Foobar');
});
app.listen(port,()=>{
console.log("Server started listening to port "+port);
})
And this my route file,
const express = require('express');
const router = express.Router();
// fetching the schema
const Contact = require('../Models/contacts');
//Retriving the contacts
router.get('/contacts', (req,res,next)=>{
Contact.find(function(err,contacts){
// Sending to client in json format
res.json(contacts);
});
});
// Adding Contacts
router.post('/contact', (req,res,next)=>{
let newContact = new Contact({
first_name : req.body.first_name,
last_name : req.body.last_name,
phone : req.body.phone
});
newContact.save((err,contact)=>{
if(err){
res.json({msg: "Failed to add contact."});
}else{
res.json({msg:"Contact added sucessfully"});
}
});
});
//Deleteing contact
router.delete('/contact/id',(req,res,next)=>{
Contact.remove({_id:req.params.id},function(err,result){
if(err){
res.json(err);
}else{
res.json(result);
}
})
});
module.exports=router;
Now, I'm trying to add a few records in DB (Mongo DB) using postman, but it's throwing an error saying "TypeError: Cannot read property 'first_name' of undefined, at router.post (C:\Mean App\ContactList\routes\route.js:16:25)"
In postman, for header, I'm using "Content-type: application/json" and in raw body, I'm adding JSON data like this,
{
"first_name" : "Siddhesh",
"last_name" : "Mishra",
"phone" : "9594106324"
}
And here is my code where I'm creating schema
const mongoose=require('mongoose');
const ContactSchema = mongoose.Schema({
first_name:{
type:String,
required:true
},
last_name:{
type:String,
required:true
},
phone:{
type:String,
required:true
}
});
const Contact=module.exports=mongoose.model('Contact',ContactSchema);
thank you.
You will have to move body-parser above the routes use and should work
//body-parser
app.use(bodyparser.json());
//For routing
app.use('/api',route);
From Express API v4.x
app.use([path,] callback [, callback...])
...
Middleware functions are executed sequentially, therefore the order of
middleware inclusion is important.
body-parser needs to go before the routes:
//Importing modules
var express = require('express');
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var cors = require('cors');
var path = require('path');
var app = express();
const route = require('./routes/route');
//Connect to mongoDb
mongoose.connect('mongodb://localhost:27017/contactlist');
//on connection
mongoose.connection.on('connected', () => {
console.log("Successfully established a connection to mongodb Database ")
});
//on error
mongoose.connection.on('error', (err) => {
if (err) {
console.log("Failed to established a connection " + err);
}
});
const port = 3000;
//body-parser
app.use(bodyparser.json()); // here
//Adding middleware -cors
app.use(cors());
//Static Files
app.use(express.static(path.join(__dirname, 'public')));
// For routing
app.use('/api', route);
app.get('/', (req, res) => {
res.send('Foobar');
});
app.listen(port, () => {
console.log("Server started listening to port " + port);
})
To always be on the safe side your routes should always come last after all middleware.