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 :)
0 comments:
Post a Comment