Proxy with express.js
Solution 1
You 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: 'www.google.com',
// 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.
Solution 2
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 = 'http://google.com/';
request(newurl).pipe(res);
});
I hope this helps, took me a while to realize that I could do this :)
Solution 3
I 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).path
Update: As mentioned by Andrew (thank you!), there's a ready-made solution using the same principle:
npm i --save http-proxy-middleware
And then:
const proxy = require('http-proxy-middleware')
var apiProxy = proxy('/api', {target: 'http://www.example.org/api'});
app.use(apiProxy)
Documentation: http-proxy-middleware on Github
I know I'm late to join this party, but I hope this helps someone.
Solution 4
To 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);
});
Solution 5
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 = 'http://localhost:8080',
frontend = 'http://localhost:3001';
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);
user124114
Updated on April 06, 2021Comments
-
user124114 about 3 years
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 exampleother_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 https://github.com/http-party/node-http-proxy , 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.
-
Saule about 8 yearsthe way you do it is working for me, without any modifications
-
VyvIT about 7 yearsAlthough a bit too late to answer, but was facing similar issue and resolved it by removing body parser so that request body is not being parsed before proxying it further.
-
-
user124114 about 12 yearsYeah, some modifications were necessary, but I like this better than introducing an extra new "Proxy" module dependency. A bit verbose, but at least I know exactly what's going on. Cheers.
-
Alex Turpin over 10 yearsThanks, much simpler than using Node.js' HTTP request
-
Stephan Hoyer about 10 yearsEven simpler, if you also pipe the request: stackoverflow.com/questions/7559862/…
-
Henrik Peinar about 10 yearsNice and clean solution. I posted an answer to make it work with POST request also (otherwise it doesn't forward your post body to the API). If you edit your answer I'd be happy to remove mine.
-
Mariano Desanze almost 10 yearsCouldn't make it work with PUT. But works great for GET and POST. Thank you!!
-
davnicwil over 9 years@Protron for PUT requests just use something like
if(req.method === 'PUT'){ r = request.put({uri: url, json: req.body}); }
-
Tamlyn over 9 yearsAlso see this answer for improved error handling.
-
keinabel almost 9 yearswhenever I try to do similar routing (or the exact same) I end up with the following: stream.js:94 throw er; // Unhandled stream error in pipe. ^ Error: getaddrinfo ENOTFOUND google.com at errnoException (dns.js:44:10) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:94:26) any ideas?
-
setec almost 9 yearsIt seems like you need to do res.writeHead before data chink is written, otherwise you'll get error (headers cant be written after body).
-
Paul over 8 yearsRather than a case statement all of which do the same thing except use a different request function) you could sanitize first (e.g. an if statement that calls your default if the method isn't in the list of approved methods), and then just do r = request[method](/* the rest */);
-
Carlos Rymer over 8 yearsIf you need to pass through headers as part of a PUT or POST request, make sure to delete the content-length header so request can calculate it. Otherwise, the receiving server could truncate the data, which will lead to an error.
-
Vinoth Kumar over 8 yearsThe req.url doesn't have the full url, so updated the answer to use req.baseUrl instead of req.url
-
MaxPRafferty over 8 yearsI also like to use req.originalUrl in place of baseUrl to preserve querystrings, but this may not always be the desired behavior.
-
Selfish over 8 years@MaxPRafferty - vaid comment. Worth noting. Thanks.
-
Michal Tsadok over 7 years@user124114 - please put the full solution that you have used
-
User about 7 yearsCan you pass the IP address through this somehow?
-
Andrew over 6 yearsThis is the best solution. I'm using http-proxy-middleware, but it's the same concept. Don't spin your own proxy solution when there are great ones out there already.
-
Juicy almost 6 yearsDoesn't seem to work anymore with
request
. Throws awrite after end
error. -
nish1013 almost 6 yearsWhat does
pipe()
do here? does it return the complete response get from the remote service? -
sec0ndHand over 5 yearsThis is the only one that also deals with web-sockets.
-
Shnd almost 5 yearsseems that you'll have problem setting headers this way.
Cannot render headers after they are sent to the client
-
Christopher Krah over 4 yearsreplace it like this:
var preparedWrite = ''; // wait for data cres.on('data', function(chunk){ //res.write(chunk); preparedWrite = preparedWrite+chunk; }); cres.on('close', function(){ // closed, let's end client request as well res.writeHead(cres.statusCode); res.end(); }); cres.on('end', function(){ // finished, let's finish client request as well res.writeHead(cres.statusCode); res.write(preparedWrite); res.end(); });
-
mekwall over 4 yearsI've updated the answer to es6 syntax and fixed the writeHead issue
-
Mustafa Hussain over 4 yearsthere's another package that is more simple to use npm install express-proxy-server --save var proxy = require('express-proxy-server'); app.use('/proxy', proxy('example.org/api'));
-
drmrbrewer about 4 years@Jonathan @trigoman now that
request
has been deprecated (see notice at github.com/request/request), what is the alternative? -
Dimitri Kopriwa about 4 years
http-proxy-middleware/dist/index.js' does not provide an export named 'default
-
valik about 4 years@Henrik Peinar, will this help when i do a login post request and expect to redirect from web.com/api/login to web.com/
-
Wildhammer almost 4 yearsThis resolved my issue. The only difference was the version of http-proxy-middleware that I installed did things slightly different which was easy to figure out following documentations.
-
tanner burton almost 4 yearsThis helped me but I had to change forwardPath to proxyReqPathResolver. It looks like they deprecated forwardPath.
-
FiNeX almost 3 yearsHi, what if I need to alter a string on the HTML body before piping out to the client?
-
ArpitM over 2 yearsThis answer helped me with a non-deprecated solution using
node-fetch
.