I'm hoping to better understand the difference between express's app.get()
and app.use()
.
I understand that app.use applies to all HTTP verbs.
I have also read that "app.use()
adds middleware rather than a route"
I'd like to understand why this fact causes this behaviour...
I have an express API server that needs to proxy a React development web server.
This means that all routes that are not API routes have to be proxied.
When I proxy the routes like this, it works:
var proxy = require('express-http-proxy');
module.exports = function set_react_catchall_routes(app) {
/* Final route to send anything else to react server. */
app.get('*', proxy('localhost:3000'));
app.post('*', proxy('localhost:3000'));
}
But when I do this it does not work:
app.use('*', proxy('localhost:3000'));
Specifically, the "index" page is proxied and served up, with content like this:
<body>
<div id="root"></div>
<script type="text/javascript" src="/static/js/bundle.js"></script>
</body>
and the client requests the javascript react bundle, but then "nothing happens".
I'm reasonably sure that there aren't "other" HTTP requests involved when it does work (other than GET and POST) because none are logged.
So what would be the difference?
Try putting this logging at the top, it should help to clarify what's going on:
app.use(function(req, res, next) {
// req.path will be '/static/js/bundle.js'
console.log('path1: ' + req.path);
next();
});
app.use('*', function(req, res, next) {
// req.path will be '/'
console.log('path2: ' + req.path);
next();
});
app.all('*', function(req, res, next) {
// req.path will be '/static/js/bundle.js'
console.log('path3: ' + req.path);
next();
});
When you use app.use
it will strip off the matching section of req.path
. If you don't specify a path (logging section 1) it won't strip anything off. Similarly section 3 is using app.all
(app.get
, etc. all work the same way) which doesn't change req.path
either. It's section 2 that's the kicker.
To understand why this happens consider this example:
var router = express.Router();
router.get('/profile', ...);
app.use('/user', router);
When a request comes in for /user/profile
the app.use
will strip off the /user
part of the path. As far as router
is concerned the path is just /profile
.
To quote the docs, http://expressjs.com/en/4x/api.html#req.path
When called from a middleware, the mount point is not included in req.path.
The path for a call to app.use
is a bit like a 'starts with' and anything that matches is thrown away. For *
that matches everything so it throws away everything.
If you take a quick dive into the source code for express-http-proxy
you'll see that it uses req.path
to determine the path of the proxy request. If you just use app.use
without a path it should work fine.
There are some other request properties that are similarly relevant to understanding app.use
:
req.url
is similar to req.path
but with the query string included. Just like req.path
it will have the section matching the mountpath
removed by app.use
. Note that the Express Request
inherits the url
property from Node's http.IncomingMessage
so it isn't explicitly listed in the Express documentation.req.originalUrl
starts off the same as req.url
but it will not be changed by app.use
.req.baseUrl
is used to store the section of the path that has been removed by app.use
.See the documentation for req.originalUrl
for more details on all three of these properties.