Thursday, March 23, 2017

Proxy with express.js

Leave a Comment

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 https://github.com/nodejitsu/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.

9 Answers

Answers 1

You want to use http.request to create a similar request to the remote API and return its response.

Something like this:

var http = require('http');  /* your app config here */  app.post('/api/BLABLA', function(req, res) {    var 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: req.headers   };    var creq = http.request(options, function(cres) {      // set encoding     cres.setEncoding('utf8');      // wait for data     cres.on('data', function(chunk){       res.write(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.end();     });    }).on('error', function(e) {     // we got an error, return 500 error to client and log error     console.log(e.message);     res.writeHead(500);     res.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.

Answers 2

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 :)

Answers 3

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); }); 

Answers 4

I found a shorter and very straightforward solution which works seamlessly, and with authentication as well, using express-http-proxy:

var proxy = require('express-http-proxy');  // New hostname+path as specified by question: var apiProxy = proxy('other_domain.com:3000/BLABLA', {     forwardPath: function (req, res) {         return require('url').parse(req.baseUrl).path;     } }); 

And then simply:

app.use("/api/*", apiProxy); 

I know I'm late to join this party, but I hope this helps someone.

Answers 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); 

Answers 6

Ok, 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 res 

javascript:

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); }); 

Answers 7

I've created a extremely simple module that does exactly this: https://github.com/koppelaar/auth-proxy

Answers 8

First install express and http-proxy-middleware

npm install express http-proxy-middleware --save 

Then 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: 'http://localhost:8080' }); 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: http://localhost:3000'); }); 

In this example we serve the site on port 3000, but when a request end with /api we redirect it to localhost:8080.

http://localhost:3000/api/login redirect to http://localhost:8080/api/login

Answers 9

I found a shorter solution that does exactly what I want https://github.com/nodejitsu/node-http-proxy/

After installing http-proxy

npm install http-proxy --save 

Use 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 :)

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment