Search code examples
javascriptreactjsreact-async

Getting rid of localhost:3000 URLs for ReactAsync


Cross-posted to https://github.com/andreypopp/react-async/issues/34

When using react-async is is common to have code which looks like:

var UserPage = React.createClass({
  mixins: [ReactAsync.Mixin],

  statics: {
    getUserInfo: function(username, cb) {
      superagent.get(
        'localhost:3000/api/users/' + username,
        function(err, res) {
          cb(err, res ? res.body : null);
        });
    }
  },

  getInitialStateAsync: function(cb) {
    this.type.getUserInfo(this.props.username, cb);
  },

  ...

The problem with this is that it only runs correctly in a browser running on the server.

Using the obvious solution of making the URLs relative (e.g. '/api/users/' + username has a subtle issue.

It seems to work, when moving between pages, but does not work on a page reload or initial load. (You do not actually move between pages in a ReactJS app, it's just the URL that changes.)

The cause of this issue is that the server needs to call the AJAX API, during server-side rendering, but the server has no knowledge of the page origin as seen by browsers (http://www.example.com:3000).

Is there a way of telling this to the server-side renderer?

(I have already thought of a nasty work-around, where you use full URLs for both client and server, but this must be configured explicitly for each development, test and production server that runs the code.)


Solution

  • To start, use a simple plugin function for superagent. This rewrites absolute urls - but only on the server. This means that the browser will make a request to '/api' and the server will make it to 'localhost:port/api'.

    function fixUrlPlugin(port){
      return function fixUrl(req){
        if (process.browser) return req;
        if (req.url[0] === '/') {
            req.url = 'localhost:' + port + req.url
        }
        return req;
      };
    }
    

    Now somewhere in your code, you'll run this and save the output in a module.

    app.listen(8080);
    
    module.exports = fixUrlPlugin(8080);
    

    And then your code can be come:

    var urlPlugin = require('./path/to/that/module.js');
    
    // ...
    
      statics: {
        getUserInfo: function(username, cb) {
          superagent.get('/api/users/' + username)
            .use(urlPlugin)
            .end(function(err, res) {
              cb(err, res ? res.body : null);
            });
        }
      },