Setting the body of a http-proxy-middleware Proxied Request - node.js

I'm still new still newish to node, and having some troubling figuring out how to set a new response body with the http-proxy-middleware package.
That is, I have the following small program that proxies requests
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use('**', proxy({
target: 'http://alanstorm.com',
changeOrigin: true,
onProxyRes:function (proxyRes, req, res) {
//I want to do something here to change the response
console.log("Called");
console.log(proxyRes);
console.log(res);
}
}));
app.listen(3000);
i.e. -- I can make a request to http://localhost:3000, and it will proxy the request to my personal website.
I've also successfully setup a response listener (onProxyRes) above. I seem to have access to a proxy response object, a request object, and a response object. What I'd like to do is, in the onProxyRes method, is change the response if certain things are true or not.
However, it's not clear how to do this with the proxyRes or res objects, and I'm not sure how to look up the available methods on these objects. I tried console.loging them for useful properties, and found nothing useful.
If someone know how i can modify the bodies of the response objects, that would be great. If someone can tell me how I can figure out what methods exist on those objects, that would be extra great.

Related

How to rewrite request path using http-proxy-middleware in a react app?

I am using http-proxy-middleware in my react app, where I have a setup like this:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
"/api/v1",
createProxyMiddleware({
target: "https:test.com/",
changeOrigin: true
})
);
};
Some api requests of my applications has request url as:
http://localhost:3000/products-and-details/api/v1/proxy/trend/api/v1/listProducts
which I want to change it to:
http://localhost:3000/api/v1/proxy/trend/api/v1/listProducts.
To achieve this I am modifying request paths before requests are send to the target using pathRewrite as below:
pathRewrite: {
'^/products-and-details': ''
}
However this doesn't seem to work and the request url remains the same with no changes. Can anyone please help to point out what am I doing wrong here?
Edit: My http-proxy-middleware dependency has a version ^2.0.3
My senior colleague helped me to fix this. It seemed that the request, http://localhost:3000/products-and-details/api/v1/proxy/trend/api/v1/listProducts was not at all intercepted by the proxy. Hence, to intercept those requests we have to make sure to include it in the context for our proxy middleware.
So, in my case it was as described below:
app.use(["/api/v1", "/products-and-details/api/v1"])
And then use PathRewrite to modify request paths before the actual request is made to server as depicted below:
pathRewrite: {"/products-and-details": ""}

router handler returns an array of object but client doesn't get them in json though response with 200 status

I am implementing a express.js project with Typescript.
I have defined a enum and a interface :
export enum ProductType {
FOOD = 'food',
CLOTH = 'cloth',
TOOL = 'tool'
}
export interface MyProduct {
type: ProductType;
info: {
price: number;
date: Date;
};
}
One of my router handler needs to return an array of MyProduct to client. I tried this :
const productArr: MyProduct[] = // call another service returns an array of MyProduct
app.get('/products', (req, res) => {
res.status(200).send({products: productArr});
});
I use Postman tested this endpoint, it responses with status 200 but with a default HTML page instead of the array of objects in JSON.
What do I miss? Is it because express.js can't automatically parse the enum and interface to json object??
P.S. I have set up json parser, so it is not about that, other endpoints work fine with json response:
const app = express();
app.use(express.json());
...
As mentioned in the comments, your code should work. I'll list some steps which can be used to try to find the problem.
Show debug info
Set DEBUG=* in your environment. DEBUG is an environment variable which controls logging for many Node modules. You'll be able to see the flow of a request through Express. If there is too much info, you can limit the output like so: DEBUG=*,-babel,-babel:*,-nodemon,-nodemon:*,-router:layer,-follow-redirects,-send (use a comma-separated list and put a - in front of any module you'd like to exclude)
This should help you trace the life of a request through the various routers and routes. You're now in a position to...
Check for another route that is short-circuiting the request
The fact that you're seeing an HTML page when the Express route is sending an object might indicate that your request is matching a different route. Look for catch-all routes such as non-middleware app.use() or wildcard routes which appear ABOVE your route.
Other suggestions
Don't explicitly set the status
Adding .status(200) is more code and unnecessary.
Use res.json()
Use .json() instead of .send(). If will always add the Content-Type: application/json header, whereas .send() will not when it cannot determine the content type (e.g. .send(null) or .send('hello') will not set the Content Type header to application/json, which may confuse clients).
As there is a lack of full response headers and server environment, assuming you are using AWS service with reverse proxy. So, there might be few possibilities listed here that need to look upon :
If router handler returns an array of object but client doesn't get them in json though response with 200 status then there might be a reverse proxy acting as a backend server, serving default content with status code 200 for unknown routes from the client. So in this scenario, you need to whitelist a new route in your reverse proxy server, assuming you are using AWS Amplify for API rewrite and redirects then you need to whitelist this route in your AWS amplify settings, or else it will serve the default content like it is happening in current scenrio.
If issue still persists then :
Make sure you have proper CORS specification on your server.
Make sure productArr is an array returned by service, because if some service returns this value - it might be an unresolved promise. So, proper test cases will help you out here or for debugging purposes set DEBUG=* in your environment and make sure it should return value as expected.
Check for another route that is short-circuiting the request: The fact that you're seeing an HTML page when the Express route is sending an object might indicate that your request is matching a different route. Look for catch-all routes such as non-middleware app.use() or wildcard routes that appear above your route.

Change response cookies with node-http-proxy?

So, I am proxying my API requests through a node-http-proxy for several reasons.
The external API has a different origin than the actual client, so cookies are not being set correctly. The proxy obviously runs at the same origin, so I want to receive the response from the API, and inside the proxy, change the cookie value to reflect the proper origin.
Here's my current setup:
// Proxy to API server
app.use('/api', (req, res) => {
proxy.web(req, res, { target: targetUrl })
})
proxy.on('proxyRes', function (proxyRes, req, res) {
console.log('RAW Response from the target', JSON.stringify(proxyRes.headers, true, 2))
console.log('The original request', req.headers.host)
})
Basically, I need to modify the cookie to req.headers.host, as this is the correct origin.
I've seen Harmon, but this looks very involved and changes how you instantiate your entire app, if I understand correctly.
Is there a way to simply modify the proxyRes after receiving it, in a synchronous fashion?
It seems very strange that there is a proxyReq event that allows you to alter the proxy request before it's sent, but not an equivalent that allows you to alter the response...
For anyone facing the same issue, I found a solution. They just merged a PR a few days ago that hasn't made it into a new release yet.
This PR introduces a new option called cookieDomainRewrite that does exactly what it sounds like. Simply include this in your config and it's all taken care of.

Making a proxy with request and express in node

I'm trying to make a proxy with the express and request module. This post shows how to easily pipe() the request to the response:
app.use(express.json()); // to support JSON-encoded bodies
app.use(express.urlencoded()); // to support URL-encoded bodies
//app.use(express.multipart());
app.use('/api', function(req, res) {
var url =proxyUrl + req.url;
req.pipe(request(url)).pipe(res);
});
It works very well for all GET request. But on a POST request it fails. Can't figure out what is happening because it only runs into a timeout. The POST request works on the proxy.
What am I doing wrong? Is there any changes that I can debug the request? I have tried the following, but the file is empty:
req.pipe(fs.createWriteStream("test.txt"));
We are also experiencing same issues, its because express.json(). After disabling json parser, it work ok. But that not the idle solution as you might need json parser.

Express request is called twice

To learn node.js I'm creating a small app that get some rss feeds stored in mongoDB, process them and create a single feed (ordered by date) from these ones.
It parses a list of ~50 rss feeds, with ~1000 blog items, so it's quite long to parse the whole, so I put the following req.connection.setTimeout(60*1000); to get a long enough time out to fetch and parse all the feeds.
Everything runs quite fine, but the request is called twice. (I checked with wireshark, I don't think it's about favicon here).
I really don't get it.
You can test yourself here : http://mighty-springs-9162.herokuapp.com/feed/mde/20 (it should create a rss feed with the last 20 articles about "mde").
The code is here: https://github.com/xseignard/rss-unify
And if we focus on the interesting bits :
I have a route defined like this : app.get('/feed/:name/:size?', topics.getFeed);
And the topics.getFeed is like this :
function getFeed(req, res) {
// 1 minute timeout to get enough time for the request to be processed
req.connection.setTimeout(60*1000);
var name = req.params.name;
var callback = function(err, topic) {
// if the topic has been found
if (topic) {
// aggregate the corresponding feeds
rssAggregator.aggregate(topic, function(err, rssFeed) {
if (err) {
res.status(500).send({error: 'Error while creating feed'});
}
else {
res.send(rssFeed);
}
},
req);
}
else {
res.status(404).send({error: 'Topic not found'});
}};
// look for the topic in the db
findTopicByName(name, callback);
}
So nothing fancy, but still, this getFeed function is called twice.
What's wrong there? Any idea?
This annoyed me for a long time. It's most likely the Firebug extension which is sending a duplicate of each GET request in the background. Try turning off Firebug to make sure that's not the issue.
I faced the same issue while using Google Cloud Functions Framework (which uses express to handle requests) on my local machine. Each fetch request (in browser console and within web page) made resulted in two requests to the server. The issue was related to CORS (because I was using different ports), Chrome made a OPTIONS method call before the actual call. Since OPTIONS method was not necessary in my code, I used an if-statement to return an empty response.
if(req.method == "OPTIONS"){
res.set('Access-Control-Allow-Origin', '*');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.status(204).send('');
}
Spent nearly 3hrs banging my head. Thanks to user105279's answer for hinting this.
If you have favicon on your site, remove it and try again. If your problem resolved, refactor your favicon url
I'm doing more or less the same thing now, and noticed the same thing.
I'm testing my server by entering the api address in chrome like this:
http://127.0.0.1:1337/links/1
my Node.js server is then responding with a json object depending on the id.
I set up a console log in the get method and noticed that when I change the id in the address bar of chrome it sends a request (before hitting enter to actually send the request) and the server accepts another request after I actually hit enter. This happens with and without having the chrome dev console open.
IE 11 doesn't seem to work in the same way but I don't have Firefox installed right now.
Hope that helps someone even if this was a kind of old thread :)
/J
I am to fix with listen.setTimeout and axios.defaults.timeout = 36000000
Node js
var timeout = require('connect-timeout'); //express v4
//in cors putting options response code for 200 and pre flight to false
app.use(cors({ preflightContinue: false, optionsSuccessStatus: 200 }));
//to put this middleaware in final of middleawares
app.use(timeout(36000000)); //10min
app.use((req, res, next) => {
if (!req.timedout) next();
});
var listen = app.listen(3333, () => console.log('running'));
listen.setTimeout(36000000); //10min
React
import axios from 'axios';
axios.defaults.timeout = 36000000;//10min
After of 2 days trying
you might have to increase the timeout even more. I haven't seen the express source but it just sounds on timeout, it retries.
Ensure you give res.send(); The axios call expects a value from the server and hence sends back a call request after 120 seconds.
I had the same issue doing this with Express 4. I believe it has to do with how it resolves request params. The solution is to ensure your params are resolved by for example checking them in an if block:
app.get('/:conversation', (req, res) => {
let url = req.params.conversation;
//Only handle request when params have resolved
if (url) {
res.redirect(301, 'http://'+ url + '.com')
}
})
In my case, my Axios POST requests were received twice by Express, the first one without body, the second one with the correct payload. The same request sent from Postman only received once correctly. It turned out that Express was run on a different port so my requests were cross origin. This caused Chrome to sent a preflight OPTION method request to the same url (the POST url) and my app.all routing in Express processed that one too.
app.all('/api/:cmd', require('./api.js'));
Separating POST from OPTIONS solved the issue:
app.post('/api/:cmd', require('./api.js'));
app.options('/', (req, res) => res.send());
I met the same problem. Then I tried to add return, it didn't work. But it works when I add return res.redirect('/path');
I had the same problem. Then I opened the Chrome dev tools and found out that the favicon.ico was requested from my Express.js application. I needed to fix the way how I registered the middleware.
Screenshot of Chrome dev tools
I also had double requests. In my case it was the forwarding from http to https protocol. You can check if that's the case by looking comparing
req.headers['x-forwarded-proto']
It will either be 'http' or 'https'.
I could fix my issue simply by adjusting the order in which my middlewares trigger.

Resources