I have been working all day on trying to deploy my MERN stack app to Heroku. So far it works perfectly fine if I run it locally. The app gets deployed on Heroku, but when I try to submit/get items from MongoDB Atlas, I get a 404 error.
I tried removing the Proxy in package.json, didn't work. I ran a build command and the file is in the correct place, still doesn't work. I'm really clueless about what could be going on..
Here is the code:
BACKEND:
index.js
const express = require('express');
const app = express();
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
app.use(cors());
app.use(express.static('build'));
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb' }));
const uri =
'mongodb+srv://db:passwordhere#cluster0.fgryd.mongodb.net/myFirstDatabase?retryWrites=true&w=majority';
mongoose.connect(uri);
const profileSchema = new mongoose.Schema({
..
..
..
});
const Profile = mongoose.model('Profile', profileSchema);
module.exports = mongoose.model('Profile', profileSchema);
profileSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
app.get('/api/profiles', (req, res) => {
Profile.find({}).then(profiles => {
res.json(profiles);
});
});
app.post('/api/profiles', (request, response) => {
const body = request.body;
if (!body) {
return response.status(400).json({
error: 'content missing',
});
}
const profile = new Profile({
...
...
...
});
profile.save().then(savedProfile => {
response.json(savedProfile);
});
});
const PORT = process.env.NODE_ENV || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
My Frontend:
App.js
function App() {
const [viewProfile, setViewProfile] = useState(false);
const [formState, setFormState] = useState(1);
const [prof, setProf] = useState([]);
const handleProfile = () => {
setViewProfile(!viewProfile);
};
const fetchData = async () => {
await axios.get('/api/profiles').then(res => setProf(res.data));
};
useEffect(() => {
fetchData();
}, []);
const addProfile = async data => {
let img = await ImgToBase64(data.profImg);
await axios.post('/api/profiles', {
...
...
...
});
fetchData();
alert(`success!`);
};
return (
<ChakraProvider>
...
...
...
</ChakraProvider>
);
}
Can I please get some help? I almost tried everything
Heroku offers you a node's container instead of VPS (Virtual Private Server).
Inside their servers, there are more than 10k apps running on a VM. They are mapping your app port with their subdomain. for that, you have to feed port configurations to your application.
In your code, there is process.env.NODE_ENV but in the Heroku environment, they are using process.env.PORT.
const PORT = process.env.PORT || 3001;
Also, you need to add a start script in your package.json.
start: "node index.js"
if your main file is not is the index.js, you can replace your script name with main script name
You can try these two things.
Change your PORT name.
https://devcenter.heroku.com/articles/getting-started-with-nodejs#push-local-changes
Your code:
const PORT = process.env.NODE_ENV || 3001;
After
const PORT = process.env.PORT || 3001;
Make sure you created Procfile with text "web: npm start" in the file.
https://devcenter.heroku.com/articles/getting-started-with-nodejs#define-a-procfile
Procfile
Related
I deployed a Node js server with Express Js and MongoDB on NameCheap cPanel. The problem is I have to refresh 2 or 3 times to get the data from the server.Otherwise its gives 404. Then it works fine but after some minutes it acts the same. I have to refresh 2 or 3 times.
`
const express = require("express");
const { MongoClient } = require("mongodb");
const cors = require("cors");
require("dotenv").config();
const app = express();
const port = process.env.PORT || 5000;
//Middleware
app.use(cors());
app.use(express.json());
//MongoDB linking
const uri = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}#xyz`;
const client = new MongoClient(uri);
async function run() {
try {
await client.connect();
//DB Folder and Subfolder
const database = client.db("XYZ");
const allUsers = database.collection("All-Users");
app.get("/allusers", async (req, res) => {
const get = allUsers.find({});
const allData = await get.toArray();
res.send(allData);
});
} finally {
//await client.close();
}
}
run().catch(console.dir);
app.get("/", (req, res) => {
res.send(" Server is running just fine");
});
app.listen(port, () => {
console.log(" server running on port :", port);
});
`
I dont have idea what the problem is. Did anyone faced the same issue, and solved the issue?
Thank you.
I'm following a tutorial at https://www.woolha.com/tutorials/node-js-google-cloud-pub-sub-basic-examples and having some difficulty..
I've the following code in server.js:-
const express = require('express');
const app = express();
const path = require('path');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
dotenv.config(); // Reads the .env file from the local folder.
// PubSub constant initialisation
const PubSub = require(`#google-cloud/pubsub`);
const pubsub = new PubSub();
const data = new Date().toString();
const dataBuffer = Buffer.from(data);
const topicName = 'sensehat-led-config';
app.use(bodyParser.urlencoded({ extended: true}));
// Tell the app to use the public folder.
app.use(express.static('public'));
app.get('/', (req,res) => {
res.send('Hello from App Engine!');
})
app.get('/submit', (req, res) => {
res.sendFile(path.join(__dirname, '/views/form.html'));
})
// Need to figure out how to get the css file to work in this. Can't be that hard.
app.get('/sensehat', (req, res) => {
res.sendFile(path.join(__dirname, '/views/sensehat.html'));
})
app.get('/sensehat-publish-message', (req, res) =>{
pubsub
.topic(topicName)
.publisher()
.publish(dataBuffer)
.then(messageId => {
console.log(`Message ${messageId} published`);
})
.catch(err => {
console.error('ERROR:', err);
});
})
app.post('/submit', (req, res) => {
console.log({
name: req.body.name,
message: req.body.message
});
res.send('Thanks for your message!');
})
// Listen to the App Engine-specified port, or 8080 otherwise
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log('Server listening on port ${PORT}...');
})
But when I run it I get a '500 Server Error', and looking at the Stackdriver logs I get the following error:-
TypeError: PubSub is not a constructor at Object.<anonymous>
I'm definitely a newbie at NodeJS and feeling my way around. After reading around I think the issue is coming from the
const PubSub = require(`#google-cloud/pubsub`);
const pubsub = new PubSub();
lines, but no idea how to rectify this.
You can try with latest versions of all libraries.
Dependencies in package.json
"dependencies": {
"#google-cloud/pubsub": "1.5.0",
"google-gax": "1.14.1",
"googleapis": "47.0.0"
}
Example code -
const {
PubSub
} = require('#google-cloud/pubsub');
const pubsub = new PubSub({
projectId: process.env.PROJECT_ID
});
module.exports = {
publishToTopic: function(topicName, data) {
return pubsub.topic(topicName).publish(Buffer.from(JSON.stringify(data)));
},
};
Calling file code
const PubSubPublish = require('path to your above file')
let publishResult = await PubSubPublish.publishToTopic(process.env.TOPIC_NAME, data)
Hope it helps!
You require the default export of #google-cloud/pubsub, but what look for is not in the default export.
Change the way you import PubSub to:
const {PubSub} = require(`#google-cloud/pubsub`);
Instead of:
const PubSub = require(`#google-cloud/pubsub`);
Hi in my express project, I have my index file where I require different files to startup my application. These require a database connection file, a file for logging stuff using winston and a file for configuring routes.
I use the require() statement within express to call these files, and when I run the application(using nodemon), I expect some messages to be logged to the terminal verifying that the files have been called, however no messages occur.
Here is my code:
index.js:
const express = require('express')
const app = express()
require('./startup/logging') ()
require('./startup/db') ()
require('./startup/routes') (app)
const port = process.env.PORT || 3000
app.listen(port, () => winston.info(`Listening on port: ${port}`))
db.js:
const mongoose = require('mongoose')
const winston = require('winston')
module.exports = function() {
mongoose.connect('mongodb://localhost/dwg', {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => winston.info('Connected to MongoDB...'))
.catch(err => console.error("Error"))
}
logging.js:
const winston = require('winston');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' }));
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'logfile.log' });
}
routes.js:
const express = require('express');
module.exports = function(app) {
app.use(express.json())
}
No database is created when running the application. I can confirm this by looking at mongodb compass. The message that is meant to be printed by app.listen() is also not printed to the console. Does anybody know the issue? Thank you.
The problem doing it this way is your app starts before it gets a chance to do rest of work like creating db connection etc. You should start the app only when these tasks are done. something like this
const express = require('express')
const app = express()
const logging = require('./startup/logging');
const db = require('./startup/db');
const routes = require('./startup/routes');
const port = process.env.PORT || 3000
app.listen(port, async () => {
await logging();
await db();
await routes();
// assuming you have winston here.
winston.info(`Listening on port: ${port}`)
})
Mongo part is defintely async so need await. Check if routes and logging needs await or not.
I am trying to conceal my connection string, so I installed env2 in my project. Then I made a config.env file that keeps my connection string like this:
export DB_URL='mongodb://user:userPassword#ds241968.mlab.com:41968/heroku_hc9xjmcl'
However when I use that variable as a connection string I cannot connect to Mlab I get the following error:
UnhandledPromiseRejectionWarning: MongoNetworkError: failed to connect to server [ds241968.mlab.com:41968] on first connect [MongoError: Authentication failed.]
But when I try to connect only with the string without using env2 I connect perfectly, so why does the ahuthentication fail when I use a env variable and how can I connect with one properly? Here is my server.js:
// Requiring the dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3009;
const itemRoutes = express.Router();
let Comment = require('./comment.model');
const env = require('env2')('../config.env');
console.log(process.env.DB_URL)
app.use(cors());
app.use(bodyParser.json());
const { DB_URL } = process.env;
mongoose.connect( DB_URL , { useNewUrlParser: true } )
const connection = mongoose.connection;
connection.once('open', function() {
console.log('Connection to MongoDB established succesfully!');
});
// Serve static assets
if(process.env.NODE_ENV === 'production') {
app.use(express.static('build'));
}
itemRoutes.route('/').get( async (req, res) => {
let collection = connection.collection("posts");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/comments').get( async (req, res) => {
let collection = connection.collection("comments");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/userComments')
.post((req, res) => {
res.setHeader('Content-Type', 'application/json');
let comment = new Comment(req.body);
comment.save()
.then(comment => {
res.status(200).json({comment})
})
.catch(err => {
res.status(400).send('failed')
})
});
app.use('/', itemRoutes);
app.use('/userComments', itemRoutes);
app.listen(PORT, function() {
console.log('Server is running on' + ' ' + PORT);
})
Looks like you are using Node and Heroku. In that case,
You should set Heroku Config Vars (you can do this either via CLI or your Heroku Dashboard)
Refer to the config var in your node application the same way you are referring to now.
Remove 'env2' related code as you won't need it for this purpose
For example, if you create Heroku config var called "MONGO_URI", refer to it as process.env.MONGO_URI in your node application.
Details can be found here: https://devcenter.heroku.com/articles/config-vars#managing-config-vars
Am new to jest, node and express, and am having a problem with testing my app.
The actual code seems to be working - it's just when passing the server instance to each of the test files (user.test.js and genres.test.js) and running jest, the port is being blocked. I assume it's because I'm creating duplicate instances of the server in each test file, without realising it.
Running jest with the flag --runInBand works, and so does only using one test file, but this doesn't help me understand exactly what's going on.
I've posted example code below, but I'm struggling to cut it down, however I do think most of the code is irrelevant, and it's just down to how I'm passing the server to each file.
Again, apologies for the length, but I think it should be very basic for anyone but me! Thanks.
index.js (.)
const express = require('express');
const app = express();
const genres = require('./routes/genres');
const users = require('./routes/users');
app.use(express.json());
app.use('/api/genres', genres);
app.use('/api/users', users);
const port = process.env.PORT || 3000;
const server = app.listen(port, () => console.log(`Listening on port ${port}...`));
console.log(typeof server);
// export server to be used in test file
module.exports = server;
genres.js (./routes)
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
res.send('post genre ok');
});
module.exports = router;
users.js (./routes)
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
res.send('post user ok');
});
module.exports = router;
genres.test.js (./tests)
const request = require('supertest');
let server;
describe('auth tests', () => {
const exec = async function(){
return await request(server)
.post('/api/genres');
};
beforeEach(() => {
server = require('../index');
});
afterEach(async () => {
await server.close();
});
describe('POST /', () => {
it('should return 200', async () => {
const res = await request(server).post('/api/genres');
expect(res.status).toBe(200);
});
});
});
user.test.js (./tests)
const request = require('supertest');
let server;
describe('user tests', () => {
const exec = async function(){
return await request(server)
.post('/api/user');
};
beforeEach(() => {
server = require('../index');
});
afterEach(async () => {
await server.close();
});
describe('POST /', () => {
it('should return 200', async () => {
const res = await request(server).post('/api/users');
expect(res.status).toBe(200);
});
});
});
Hopefully this (point 2) helps others with this question
It has worked for me, by splitting the app from the server. I'm not sure if this is the right approach, and I'm not 100% sure why it works with the app rather than the server, but all my tests are now passing.
index.js is now app.js:
const express = require('express');
const app = express();
const genres = require('./routes/genres');
const users = require('./routes/users');
app.use(express.json());
app.use('/api/genres', genres);
app.use('/api/users', users);
// export server to be used in test file
module.exports = app;
The server is separated into another file:
const app = require('./app');
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on port ${port}...`));
and then the test files import the app rather than the server. therefore each test doesn't create its own instance.
NB: I think - I don't really know how correct this is, but as mentioned, it works