React axios calls to Express API in Heroku deployment - node.js

I have a React todo app I built using create-react-app and I built a simple express server to query mongoDB to get all the appointment objects. It works just as expected when I am running it on my machine. The front end spins up on localhost:3000 and the server on localhost:3001. I use axios to make a get request to localhost:3000/api/appointments to load all the appointments into the App.js state. I uploaded it to Heroku and I got a CORS error on the request. After that, I tried to just use the route 'api/appointments' in the request and every permutation of that I can come up with which all respond with 404 errors.
Where does the node.env variable spin up the server on Heroku? And how do I call it fromm a React app with axios?
Same question in a different context if it helps:
When I run the app on my machine and access it with Postman, I can GET localhost:3001/api/appointmentsand it returns an array of JSON objects from the database just as I would expect. When I deploy to Heroku GET https://appointment-ledger-map.herokuapp.com/api/appointments returns all the markup for index.html. I assume this means that the api server is up and running because it responds but why is it not responding with the array of JSON objects as expected?
// server.js
var express = require('express');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var Appointment = require('./model/appointments');
//and create our instances
var app = express();
var router = express.Router();
//set our port to either a predetermined port number if you have set
//it up, or 3001
var nodeEnv = process.env.NODE_ENV || 'development';
var port = process.env.PORT || 3001;
var host = process.env.HOST || '0.0.0.0';
//db config
mongoose.connect('mongodb://josh11:josh11#ds133162.mlab.com:33162/heroku_tl016m5d');
//now we should configure the API to use bodyParser and look for
//JSON data in the request body
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//To prevent errors from Cross Origin Resource Sharing, we will set
//our headers to allow CORS with middleware like so:
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
//and remove cacheing so we get the most recent appointments
res.setHeader('Cache-Control', 'no-cache');
next();
});
//now we can set the route path & initialize the API
router.get('/', function(req, res) {
res.send({ message: 'API Initialized!'});
console.log('Api initialized');
});
//Use our router configuration when we call /api
app.use('/api', router);
//starts the server and listens for requests
app.listen(port, host, function() {
console.log(`api running on port ${port}`);
});
//adding the /appointments route to our /api router
router.route('/api/appointments')
//retrieve all appointments from the database
.get(function(req, res) {
//looks at our Appointment Schema
Appointment.find(function(err, appointments) {
if (err)
res.send(err);
//responds with a json object of our database appointments.
res.send(appointments)
});
console.log(appointments);
})
//post new appointment to the database
.post(function(req, res) {
var appointment = new Appointment();
//body parser lets us use the req.body
appointment.appointmentTitle = req.body.appointmentTitle;
appointment.appointmentDate = req.body.appointmentDate;
appointment.appointmentTime = req.body.appointmentTime;
appointment.appointmentDescription = req.body.appointmentDescription;
appointment.appointmentDestination = req.body.appointmentDestination;
appointment.appointmentOrigin = req.body.appointmentOrigin;
appointment.travelMode = req.body.travelMode;
appointment.save(function(err) {
if (err)
res.send(err);
res.send({ message: 'Appointment successfully added!' });
});
});
// App.js
loadAppointments() {
axios.get('/api/appointments')
.then(res => {
this.setState({
appointments: res.data,
filteredAppointments: res.data
});
})
}

npm install cors --save
then
var cors = require('cors');
finally
mongoose.connect('mongodb://josh11:josh11#ds133162.mlab.com:33162/heroku_tl016m5d');
//now we should configure the API to use bodyParser and look for
//JSON data in the request body
app.use(cors()); **//Must be before BodyParser**
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
//To prevent errors from Cross Origin Resource Sharing, we will set
//our headers to allow CORS with middleware like so:
Re-deploy it and voila :)
Hope it helped you

Related

getting CORS error when I try to fetch my api

When I try to fetch my API from my react app, it shows me this error message when my laptop is connected through a LAN connection.
But when my laptop is connected through a mobile hotspot, it doesn't show me any error.
And when I run my API in localhost:5000, it shows me this error message on the terminal.
Could not connect to any servers in your MongoDB Atlas cluster. One common reason is that you're trying to access the database from an IP that isn't whitelisted. Make sure your current IP address is on your Atlas cluster's IP whitelist: https://docs.atlas.mongodb.com/security-whitelist/
I already give access to my IP address and it's working fine when I connected through a mobile hotspot.
Why is it happening and how to fix this?
I am able to fetch other APIs but not mine.
API link: https://invoicesapi.herokuapp.com/api/invoices
Github Repo: https://github.com/rishipurwar1/invoice_api
API app.js code:
const path = require('path');
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;
const connectDB = require('./config/db');
// routes
const invoices = require('./routes/api/invoices');
// Init Middleware
app.use(express.json({ extended: false }));
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', "*");
res.setHeader('Access-Control-Allow-Methods', "POST,GET,OPTIONS");
res.setHeader('Access-Control-Allow-Headers', "Content-Type, Authorization");
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
// Connect Database
connectDB();
app.get('/api', (req, res) => {
res.render('index')
});
app.use('/api', invoices);
app.listen(port, () => {
console.log('Server is started');
})
You should consider using the cors package (https://www.npmjs.com/package/cors)
npm install cors
Add this to app.js file:
var cors = require('cors')
var corsOptions = {
origin: 'http://yoursite.com',
optionsSuccessStatus: 200
}
app.use(cors(corsOptions))

Angular image not served properly from nodejs backend

I want to serve a image from my nodejs backend and show it in frontend, my backend running at 8081 port and frontend at 8080. I can see images in http://localhost:8081/image.JPG but in frontend I am getting 404 as it is looking for http://localhost:8081/image.JPG.
var express = require('express');
var cors = require("cors");
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
global.__basedir = __dirname;
app.use(express.static(__dirname + '/resources/static/assets/uploads/'));
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
const db = require('./app/config/db.config');
app.use(cors({origin: 'http://localhost:8080'}));
// force: true will drop the table if it already exists
db.sequelize.sync({force: false}).then(() => {
console.log('Drop and Resync with { force: false }');
});
require('./app/routers/upload.router.js')(app);
// Create a Server
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("App listening at http://%s:%s", host, port)
})
i can see image when i query in browser as it is looking for 8081 port
frontend looking for port 8080 so getting below error--
someone please suggest me a solution. I am not getting any clue. Thank you.
An approach that you can take is to use proxy config provided by Angular like below:
{
"/api/assets": {
"target": "http://localhost:8081",
"secure": false
}
}
Now, from the frontend when you hit this URL: http://localhost:8080/api/assets/image.JPG it will proxy to http://localhost:8081/api/assets/image.JPG
your image tag will look like <img src="/api/assets/image.JPG">
More on Proxy config & how to configure: https://angular.io/guide/build#proxying-to-a-backend-server

Ionic/Angular not connecting properly to nodeJS (routes work perfectly in postman

I am able to run the GET & POST requests using POSTMAN, and it works. I cannot figure out where I'm going wrong in my app. My GET request to the same address works just fine.
Ex., In my service:
createOne(route, document) {
console.log(`create new ${route}`, document);
console.log(`http://localhost:8080/api/${route}`);
return this.http.post(`http://localhost:8080/api/${route}`, document);
}
This logs in the console as:
Unfortunately, it doesn't log a thing on the nodeJS server, so I'm sure I made a mistake somewhere in the server.js file - but can't figure out where!
POST request via postman to http://localhost:8080/api/appts with this for raw JSON body:
{
"title": "test from postman",
"description": "test description",
"startTime": "2020-08-04T17:40:45.521-04:00",
"endTime": "2020-08-04T20:10-04:00",
"allDay": false
}
My server.js file:
//1. require express and call it as a function
require('./api/data/db.js');
const cron = require('node-cron');
var express = require('express');
console.log(process.env.PORT);
var app = express();
var cors = require('cors');
var path = require('path');
var bodyParser = require('body-parser');
var routes = require('./api/routes');
var json2xls = require('json2xls');
//PORT SETUP
if (process.env.PORT) {
app.use(cors());
app.set('port', process.env.PORT || 8080);
} else {
app.set('port', 8080);
}
// MIDDLEWARE
app.use(function (req, res, next) {
console.log(req.method, req.url);
next();
});
app.use(json2xls.middleware);
// ROUTING
app.use(express.static(path.join(__dirname, 'app/www'))); //setup primary route request folder
app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));
app.use(bodyParser.urlencoded({ extended: true })); //only get strings and arrays from form when false
app.use(bodyParser.json()); // tell backend to understand json data
app.use('/api', routes);
// LOAD THE SINGLE PAGE FOR IONIC
app.get('*', function (req, res) {
res.sendFile('/app/www/index.html', { root: __dirname });
});
// START SERVER LISTENING
var server = app.listen(app.get('port'), function () {
var port = server.address().port; // return the port for the server listening
console.log('RayWood API running on port ' + port); //confirm app running
});
My File Tree is setup as follows:
Seeing how the nodeJS server doesn't log the URL or REQUEST, I think the problem is in my server.js file - but I still don't know what I did wrong. Please help!
You are not subscribing to Observable .If you are not familiar with Observable You can convert it to Promise please use below sample implemenation
createOne(route, document) {
console.log(`create new ${route}`, document);
console.log(`http://localhost:8080/api/${route}`);
return new Promise((resolve, reject) => {
this.http.post(`http://localhost:8080/api/${route}`,document).subscribe((resp)=>{
console.log(resp);//This is the response from Node API
resolve(resp);
},(err)=>{
reject(err)
});
});
}

Why does express middleware override my api's response?

I am new to nodejs/express and have followed a blog article "Build Node.js RESTful APIs in 10 Minutes". It provided detailed steps to create simple REST apis. After finishing every code mentioned in the article, the app have not been working properly, it would just return
{
"url": "/transactions not found"
}
I found that the culprit was in the last part of the article, which told me to add this line to server.js:
Having done all these, what happens if we entered a wrong route? say
you entered 'http://localhost:3000/task', It responds with a message
“Cannot GET /task”. Let’s add express middleware which could be used
to return more interactive messages.
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ' not found'})
});
It seems to hardcode a HTTP status code of 404 no matter what does my api returns. After removing this line, the app return meaningful response.
Here is my server.js:
var express = require('express'),
app = express(),
port = process.env.PORT || 3000,
mongoose = require('mongoose'),
Transaction = require('./api/models/transactionModel'), //created model loading here
bodyParser = require('body-parser');
// mongoose instance connection url connection
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/transactionDb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ' not found'})
});
var routes = require('./api/routes/transactionRoutes'); //importing route
routes(app); //register the route
app.listen(port);
console.log('Transaction List RESTful API server started on: ' + port);
And the controller:
'use strict';
var mongoose = require('mongoose'),
Transaction = mongoose.model('Transactions');
exports.list_all_transactions = function (req, res) {
Transaction.find({}, function (err, transaction) {
if (err)
res.send(err);
res.json(transaction);
});
};
exports.create_a_transaction = function (req, res) {
var new_transaction = new Transaction(req.body);
new_transaction.save(function (err, transaction) {
if (err)
res.send('Error creating a new transaction!');
res.json(transaction);
});
};
it wasn't override your response, just because it returned in the middle before touch your api. The flow of request is running from top to bottom, example in your code above:
[coming request] --> bodyParser --> bodyParser --> 404 handler (returned here) -x-> api (cannot reach).
Just use() 404 middleware to the bottom and everything works fine.
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/transactionDb');
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var routes = require('./api/routes/transactionRoutes'); //importing route
routes(app); //register the route
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ' not found'})
});
I suspect your
app.use(function(req, res) {
res.status(404).send({url: req.originalUrl + ' not found'})
});
should be lower: the server is finding that piece of code first, while the actual routes are loaded later.
app.use means that it always executes that code, so it handles every request by sending a 404 error and saying it can't find the URL.
Put that code under all of the other routes (right above app.listen(port)) and it should work!

Accessing internal API with React, Axios on Heroku

I am building a full stack React application that accesses its own back end API with Axios. In my local environment, the following works as expected, with the server responding with JSON data, which is then rendered properly.
axios.get('/api/questions/categories')
I deployed to Heroku, and the app is launching normally and MongoDB is connected. Now, when the same GET request is made, it is not reaching the back end. When I log the response from Axios to the console, it contains the actual HTML of the page, instead of the JSON object expected.
For further clarification, if I manually type 'http://localhost:8080/api/questions/categories' in the address bar, the expected JSON data is displayed. If I do the same with the app on Heroku, I see that a '#' is appended to the url and the page display does not change, no error messages. This leads me to think that react-router is involved, but I have not been able to figure out how/why.
My stack: Node, Express, Mongo, React
Not using Redux
Using Axios to call my own API
// Dependencies
var express = require('express');
var path = require('path');
var webpack = require('webpack');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('./webpack.config.js');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var morgan = require('morgan');
var inDev = process.env.NODE_ENV !== 'production';
var port = inDev ? 8080 : process.env.PORT;
var app = express();
// MIDDLEWARE
if (inDev){
var compiler = webpack(config);
var middleware = webpackMiddleware(compiler, {
publicPath: config.output.publicPath,
contentBase: 'app',
stats: {
colors: true,
hash: false,
timings: true,
chunks: false,
chunkModules: false,
modules: false
}
});
app.use(morgan('dev'));
app.use(middleware);
app.use(webpackHotMiddleware(compiler));
app.get('/', function response(req, res) {
res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
res.end();
});
} else {
app.use(express.static(__dirname + '/dist'));
app.get('*', function response(req, res) {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
}
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
//and remove caching so we get the most recent comments
res.setHeader('Cache-Control', 'no-cache');
next();
});
// DATABASE
var dbPath = inDev ? 'mongodb://localhost/quizMe' : 'mongodb://heroku_pmjl5579:c28cf07fpf05uus13ipjeur5s7#ds143000.mlab.com:43000/heroku_pmjl5579';
mongoose.connect(dbPath);
// ROUTING / API
// var indexRoute = require('./routes/index');
var questionsRoute = require('./routes/api/questions');
// app.use('/', indexRoute);
app.use('/api/questions', questionsRoute);
app.listen(port, function(){
console.log('Express server up on ' + port);
});
Thanks for any help!
Most single page applications route all requests to the root path and let the front end router take over. I suspect that is what is happening to your app.
Do you have any form of requests redirection logic in your back end code or any server configuration code?
What you can do is to whitelist some paths that you don't want front end routing to take over, such as those that start with /api. Pasting your server side config here will be helpful.
In your server config, when inDev is false, you have a app.get('*', ...) that catches all requests and responds with the static single page app. Hence API requests will also give the same response. You will need to restructure your routes to match /api before the wildcard *. Some examples can be found here

Resources