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
isfalse
, you have aapp.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