9

I've got a React app that via an API pulls data from a separate database.

When I run it locally, the app is one port and the API is on another port.

Since when I make AJAX calls in the app to the API, I need to include the URL where the API can connect.

It works if I hardcode the separate port (e.g., the app is on http://localhost:3000 and the API on http://localhost:3100, making the AJAX url call to the API http://localhost:3100/api/trusts).

However, since the app and API are on different ports, I can't make the AJAX url a relative path because it erroneously sends the AJAX call to http://localhost:3000/api/trusts and not http://localhost:3100/api/trusts.

How do I get them to run on the same port?

Thanks!

Here's my server.js:

var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var path = require('path');
var app = express();
var router = express.Router();
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

//set our port to either a predetermined port number if you have set it up, or 3001
var port = process.env.PORT || 5656;

//db config
var mongoDB = 'mongodb://XXX:XXX!@XXX.mlab.com:XXX/XXX';
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));

//body parsing
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// allow cross-browser
app.use(function(req, res, next) {
  res.setHeader('Access-Control-Allow-Origin', '*');
  next();
});

// handling static assets
app.use(express.static(path.join(__dirname, 'build')));

// api handling
var TrustsSchema = new Schema({
  id: String,
  name: String
});

var Trust = mongoose.model('Trust', TrustsSchema);

const trustRouter = express.Router();

trustRouter
    .get('/', (req,res) => {

      Trust.find(function(err, trusts) {
        if (err) {
          res.send(err);
        }
        res.json(trusts)
      });
    });

app.use('/api/trusts', trustRouter);


//now  we can set the route path & initialize the API
router.get('/', function(req, res) {
  res.json({ message: 'API Initialized!'});
});

app.get('/*', function (req, res) {
   res.sendFile(path.join(__dirname, 'build', 'index.html'));
 });

app.listen(port, function() {
  console.log(`api running on port ${port}`);
});

Below is the AJAX call I'm trying to make that doesn't work because the relative path is appended to the app's port (i.e., http://localhost:3000/) and not the API's port (i.e., http://localhost:3100/):

axios.get("/api/trusts")
  .then(res => {
    this.setState({trusts: res.data});
  })
  .catch(console.error);
Tholle
  • 108,070
  • 19
  • 198
  • 189
Adam White
  • 1,096
  • 3
  • 14
  • 28
  • You can't run both of them on the same port. Depending on your setup you can use a proxy to redirect API requests to your backend server. With that you don't need to hardcode the URL but use /api/foo to make a request. – devserkan Jul 16 '18 at 17:51
  • 3
    if you're using webpack-dev-server, you can use the builtin proxy to send requests to `/api/...` to a different address transparently https://webpack.js.org/configuration/dev-server/#devserver-proxy – Tyler Sebastian Jul 16 '18 at 17:53
  • I've tried setting the proxy and it doesn't solve the problem I'm having. I've included the AJAX call above to give some more context as to what I'm trying to do. – Adam White Jul 16 '18 at 18:01
  • 1
    You definitely want the proxy. It'll allow you to forward all requests to `/api/trusts` to a specific port like `http://localhost:3100/api/trusts` – wpercy Jul 16 '18 at 18:03
  • You are using a static handler. Can you serve the React app from a static directory, in which case it will know the right way to access the dynamic request as it'll be from the same host:port. How are you hosting the react app? – xrd Jul 16 '18 at 18:20

1 Answers1

12

To tell the development server to proxy any unknown requests to your API server in development, add a proxy field to your package.json, for example:

"proxy": "http://localhost:4000",

This way, when you fetch('/api/todos') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/todos as a fallback. The development server will only attempt to send requests without text/html in its Accept header to the proxy.

"Keep in mind that proxy only has effect in development (with npm start), and it is up to you to ensure that URLs like /api/todos point to the right thing in production."

Note: this feature is available with react-scripts@0.2.3 and higher.

More details here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development

wdm
  • 7,121
  • 1
  • 27
  • 29
  • I'm so sorry, but I still can't get this to work. I've included the proxy line in package.json, but when the AJAX call is made, the relative path is appended to the app's port and the API's port listed as a proxy. – Adam White Jul 16 '18 at 18:44
  • Is your proxy set to `http://localhost:3100`? With this configuration the `Request URL` should still show :3000 but the proxy will pass the request to :3100. Also try restarting your dev server if you haven't already. – wdm Jul 16 '18 at 19:20
  • Thanks, wdm! I can now get it to work locally, but then when I deploy it to Heroku, I receive an Invalid Host error. I started another thread for help on that: https://stackoverflow.com/questions/51386191/invalid-host-error-when-deploying-a-create-react-app-on-heroku (This has not been as easy as I hoped.) – Adam White Jul 17 '18 at 16:47
  • 3
    Wait, development only? Without an answer for production? I'm sorry, but this isn't a very good answer. It'll leave people confused when they build for production. – dylanh724 Mar 26 '21 at 10:05
  • 1
    @dylanh724 the question specifically states that the environment is local. In production you can setup a proxy in your web server's configuration. If you need more details related to your specific production needs you can ask another question. – wdm Mar 29 '21 at 20:50