34

I've written a small proxy with nodejs, express and htt-proxy. It works well for serving local files but fails when it comes to proxy to external api:

var express = require('express'),
    app = express.createServer(),
    httpProxy = require('http-proxy');


app.use(express.bodyParser());
app.listen(process.env.PORT || 1235);

var proxy = new httpProxy.RoutingProxy();

app.get('/', function(req, res) {
    res.sendfile(__dirname + '/index.html');
});
app.get('/js/*', function(req, res) {
    res.sendfile(__dirname + req.url);
});
app.get('/css/*', function(req, res) {
    res.sendfile(__dirname + req.url);
});

app.all('/*', function(req, res) {
    req.url = 'v1/public/yql?q=show%20tables&format=json&callback=';
    proxy.proxyRequest(req, res, {
        host: 'query.yahooapis.com', //yahoo is just an example to verify its not the apis fault
        port: 8080
    });

});

The problem is that there is no response from the yahoo api, maybe there is an response but i dont came up in the browser.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297

5 Answers5

126

Even simpler with pipe and request-Package

var request = require('request');

app.use('/api', function(req, res) {
  var url = apiUrl + req.url;
  req.pipe(request(url)).pipe(res);
});

It pipes the whole request to the API and pipes the response back to the requestor. This also handles POST/PUT/DELETE and all other requests \o/

If you also care about query string you should pipe it as well

req.pipe(request({ qs:req.query, uri: url })).pipe(res);
Stephan Hoyer
  • 4,792
  • 2
  • 29
  • 26
  • any chance you could explain what happens in `req.pipe(request(url)).pipe(res);`? Why are two `pipe`s required? – Jonathan Livni Sep 30 '14 at 11:53
  • 4
    the `pipe` line hangs when the requests has json content-type - see [this question](http://stackoverflow.com/questions/26121830/proxy-json-requests-with-node-express) – Jonathan Livni Sep 30 '14 at 13:53
  • why not use `request(url).pipe(res);` as [suggested here](http://stackoverflow.com/a/16924410/348545) – Jonathan Livni Sep 30 '14 at 13:55
  • 1
    using only the upper `request(url).pipe(res);` will eventually do it, but you would loose any request specific information except the URL. My proposed way is first piping the whole request to the API and piping the response back to the requestor. That's why two pipes are happening. – Stephan Hoyer Oct 01 '14 at 04:27
  • Any way to pass query parameters elegantly as well? – Oleg Belousov Feb 17 '15 at 10:08
  • maybe `req.pipe(request({url:url, qs: {foo: 'bar'}})).pipe(res);` but I haven't checked – Stephan Hoyer Feb 18 '15 at 10:17
  • Yay, this saved me a lot of searching. Brilliant solution. – Matthew Vita Jan 02 '16 at 01:10
  • Also don't forget to pay attention to headers. Especially `host` header very important. – Alex Povar Jan 20 '16 at 15:41
  • This doesn't carry forward query string. – hazardous May 02 '16 at 16:20
  • 1
    If anybody else is facing this issue, the following code works with passing query string values correctly - `req.pipe(request({ qs:req.query, uri: db_url })).pipe(res);` – hazardous May 02 '16 at 16:33
  • 1
    @HazardouS Thanks for the tip. I updated the answer accordingly. – Stephan Hoyer Oct 26 '16 at 07:15
  • 3
    As indicated in @Jonathan 's link, if the content-type is JSON, add `json: true` to `request` parameters, so it becomes: `req.pipe(request({ qs:req.query, uri: url, json: true })).pipe(res);` – Antoine OL Feb 28 '18 at 17:23
  • 1
    I'm no expert using Node streams/pipes. Forgive my ignorance, but how would I manipulate the request prior to redirecting? In my use case, I would be appending API credentials. – J. Munson May 10 '18 at 03:25
  • It doesn't forward redirections. Somebody knows how to do it? – Lucas Willems Oct 11 '19 at 07:57
  • It does not handle preflight requests while making cross-origin requests. `Response to preflight request doesn't pass access control check: It does not have HTTP ok status.` – Avinash Jeeva Nov 29 '19 at 10:48
7

Maybe your code is different when you're testing, but I'm querying the same URL as in your code sample using the following:

http://query.yahooapis.com:8080/v1/public/yql?q=show%20tables&format=json&callback=

and I get nothing back. My guess is you want to change port to 80 (from 8080) -- it works when I change it like so:

http://query.yahooapis.com:80/v1/public/yql?q=show%20tables&format=json&callback=

So that means it should be:

proxy.proxyRequest(req, res, {
    host: 'query.yahooapis.com', //yahoo is just an example to verify its not the apis fault
    port: 80
});
hross
  • 3,633
  • 2
  • 27
  • 31
  • Ok, changing the port works for my project as well but unfortunately I've got a 404 from the server. Calling the same url in the browser it works. – Andreas Köberle Sep 27 '11 at 05:51
4

Maybe I use http-proxy in a wrong way. Using restler does what I want:

var express = require('express'),
    app = express.createServer(),
    restler = require('restler');


app.use(express.bodyParser());
app.listen( 1234);



app.get('/', function(req, res) {
    console.log(__dirname + '/index.html')
    res.sendfile(__dirname + '/index.html');
});
app.get('/js/*', function(req, res) {
    res.sendfile(__dirname + req.url);
});
app.get('/css/*', function(req, res) {
    res.sendfile(__dirname + req.url);
});


app.all('/*', function(req, res) {

    restler.get('http://myUrl.com:80/app_test.php/api' + req.url, {

        }).on('complete', function (data) {
                console.log(data)
               res.json(data)
            });

});
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
0

I ended up using http-proxy-middleware.

The code looks something like this:

var express = require("express");
var proxy = require("http-proxy-middleware");

const theProxy = proxy({
  target: "query.yahooapis.com",
  changeOrigin: true,
});

app.use("/", theProxy);
app.listen(process.env.PORT || 3002);
Ioanna
  • 1,311
  • 2
  • 23
  • 36
0

That's what I've been using for a while. Can handle both JSON and binary requests.

app.use('/api', (req, res, next) => {
    const redirectUrl = config.api_server + req.url.slice(1);
    const redirectedRequest = request({
        url: redirectUrl,
        method: req.method,
        body: req.readable ? undefined : req.body,
        json: req.readable ? false : true,
        qs: req.query,
        // Pass redirect back to the browser
        followRedirect: false
    });
    if (req.readable) {
        // Handles all the streamable data (e.g. image uploads)
        req.pipe(redirectedRequest).pipe(res);
    } else {
        // Handles everything else
        redirectedRequest.pipe(res);
    }
});
Eugene Kulabuhov
  • 2,349
  • 1
  • 26
  • 25