To avoid same-domain AJAX issues, I want my node.js web server to forward all requests from URL /api/BLABLA to another server, for example other_domain.com:3000/BLABLA, and return to user the same thing that this remote server returned, transparently.
All other URLs (beside /api/*) are to be served directly, no proxying.
How do I achieve this with node.js + express.js? Can you give a simple code example?
(both the web server and the remote 3000 server are under my control, both running node.js with express.js)
So far I found this , but reading the documentation there didn't make me any wiser. I ended up with
var proxy = new httpProxy.RoutingProxy();
app.all("/api/*", function(req, res) { console.log("old request url " + req.url) req.url = '/' + req.url.split('/').slice(2).join('/'); // remove the '/api' part console.log("new request url " + req.url) proxy.proxyRequest(req, res, { host: "other_domain.com", port: 3000 });
});but nothing is returned to the original web server (or to the end user), so no luck.
210 Answers
request has been deprecated as of February 2020, I'll leave the answer below for historical reasons, but please consider moving to an alternative listed in this issue.
Archive
I did something similar but I used request instead:
var request = require('request');
app.get('/', function(req,res) { //modify the url in any way you want var newurl = ' request(newurl).pipe(res);
});I hope this helps, took me a while to realize that I could do this :)
11I found a shorter and very straightforward solution which works seamlessly, and with authentication as well, using express-http-proxy:
const url = require('url');
const proxy = require('express-http-proxy');
// New hostname+path as specified by question:
const apiProxy = proxy('other_domain.com:3000/BLABLA', { proxyReqPathResolver: req => url.parse(req.baseUrl).path
});And then simply:
app.use('/api/*', apiProxy);Note: as mentioned by @MaxPRafferty, use req.originalUrl in place of baseUrl to preserve the querystring:
forwardPath: req => url.parse(req.baseUrl).pathUpdate: As mentioned by Andrew (thank you!), there's a ready-made solution using the same principle:
npm i --save http-proxy-middlewareAnd then:
const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', {target: ');
app.use(apiProxy)Documentation: http-proxy-middleware on Github
I know I'm late to join this party, but I hope this helps someone.
8You want to use http.request to create a similar request to the remote API and return its response.
Something like this:
const http = require('http');
// or use import http from 'http';
/* your app config here */
app.post('/api/BLABLA', (oreq, ores) => { const options = { // host to forward to host: ' // port to forward to port: 80, // path to forward to path: '/api/BLABLA', // request method method: 'POST', // headers to send headers: oreq.headers, }; const creq = http .request(options, pres => { // set encoding pres.setEncoding('utf8'); // set http status code based on proxied response ores.writeHead(pres.statusCode); // wait for data pres.on('data', chunk => { ores.write(chunk); }); pres.on('close', () => { // closed, let's end client request as well ores.end(); }); pres.on('end', () => { // finished, let's finish client request as well ores.end(); }); }) .on('error', e => { // we got an error console.log(e.message); try { // attempt to set error message and http status ores.writeHead(500); ores.write(e.message); } catch (e) { // ignore } ores.end(); }); creq.end();
});Notice: I haven't really tried the above, so it might contain parse errors hopefully this will give you a hint as to how to get it to work.
6To extend trigoman's answer (full credits to him) to work with POST (could also make work with PUT etc):
app.use('/api', function(req, res) { var url = 'YOUR_API_BASE_URL'+ req.url; var r = null; if(req.method === 'POST') { r = request.post({uri: url, json: req.body}); } else { r = request(url); } req.pipe(r).pipe(res);
}); 4 I used the following setup to direct everything on /rest to my backend server (on port 8080), and all other requests to the frontend server (a webpack server on port 3001). It supports all HTTP-methods, doesn't lose any request meta-info and supports websockets (which I need for hot reloading)
var express = require('express');
var app = express();
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var backend = ' frontend = '
app.all("/rest/*", function(req, res) { apiProxy.web(req, res, {target: backend});
});
app.all("/*", function(req, res) { apiProxy.web(req, res, {target: frontend});
});
var server = require('http').createServer(app);
server.on('upgrade', function (req, socket, head) { apiProxy.ws(req, socket, head, {target: frontend});
});
server.listen(3000); 1 First install express and http-proxy-middleware
npm install express http-proxy-middleware --saveThen in your server.js
const express = require('express');
const proxy = require('http-proxy-middleware');
const app = express();
app.use(express.static('client'));
// Add middleware for http proxying
const apiProxy = proxy('/api', { target: ' });
app.use('/api', apiProxy);
// Render your site
const renderIndex = (req, res) => { res.sendFile(path.resolve(__dirname, 'client/index.html'));
}
app.get('/*', renderIndex);
app.listen(3000, () => { console.log('Listening on: );
});In this example we serve the site on port 3000, but when a request end with /api we redirect it to localhost:8080.
redirect to
0Ok, here's a ready-to-copy-paste answer using the require('request') npm module and an environment variable *instead of an hardcoded proxy):
coffeescript
app.use (req, res, next) -> r = false method = req.method.toLowerCase().replace(/delete/, 'del') switch method when 'get', 'post', 'del', 'put' r = request[method]( uri: process.env.PROXY_URL + req.url json: req.body) else return res.send('invalid method') req.pipe(r).pipe resjavascript:
app.use(function(req, res, next) { var method, r; method = req.method.toLowerCase().replace(/delete/,"del"); switch (method) { case "get": case "post": case "del": case "put": r = request[method]({ uri: process.env.PROXY_URL + req.url, json: req.body }); break; default: return res.send("invalid method"); } return req.pipe(r).pipe(res);
}); 1 I found a shorter solution that does exactly what I want
After installing http-proxy
npm install http-proxy --saveUse it like below in your server/index/app.js
var proxyServer = require('http-route-proxy');
app.use('/api/BLABLA/', proxyServer.connect({ to: 'other_domain.com:3000/BLABLA', https: true, route: ['/']
}));I really have spent days looking everywhere to avoid this issue, tried plenty of solutions and none of them worked but this one.
Hope it is going to help someone else too :)
0I don't have have an express sample, but one with plain http-proxy package. A very strip down version of the proxy I used for my blog.
In short, all nodejs http proxy packages work at the http protocol level, not tcp(socket) level. This is also true for express and all express middleware. None of them can do transparent proxy, nor NAT, which means keeping incoming traffic source IP in the packet sent to backend web server.
However, web server can pickup original IP from http x-forwarded headers and add it into the log.
The xfwd: true in proxyOption enable x-forward header feature for http-proxy.
const url = require('url');
const proxy = require('http-proxy');
proxyConfig = { httpPort: 8888, proxyOptions: { target: { host: 'example.com', port: 80 }, xfwd: true // <--- This is what you are looking for. }
};
function startProxy() { proxy .createServer(proxyConfig.proxyOptions) .listen(proxyConfig.httpPort, '0.0.0.0');
}
startProxy();Reference for X-Forwarded Header:
Full version of my proxy:
0I think you should use cors npm
const app = express();
const cors = require('cors');
var corsOptions = { origin: ' optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions));