How to set up http-proxy-middleware on a dynamic route? - node.js

I'm trying to proxy some asset routes where a dynamic part of the path comes from a config file. I had this working using the request library, but I can't quite get it working with http-proxy-middleware.
Here's the code that works when I use the request library:
const assets = express.Router();
app.use(`/${p.name}/assets`, assets);
assets.get('*', async (req, res) => {
return request(`${p.address}/${p.version}/assets${req.path}`).pipe(res);
});
I've tried a few different variations with http-proxy-middleware, and none of these work. Example 1:
app.use(
`/${p.name}/assets`,
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);
Example 2:
app.use(
`/${p.name}/assets`,
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: function(path) {
return path.replace(p.name, p.version);
}
})
);
I've also tried using /${p.name}/assets/** as the first argument to app.use, and I've also tried adding a /assets to the end of both the key and value in the pathRewrite object. The result is always the same: I get a 302 for the asset requested by the browser.
I've even tried adding a middleware function right before the httpProxy call that logs to the console so that I know my request is hitting the correct route:
app.use(
`/${p.name}/assets`,
(req, _res, next) => {
console.log("I'm an asset proxy, short and stout: ", req.url);
next();
},
httpProxy({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);
But I don't ever see that output. Probably a simple mistake. Perhaps my first argument to app.use just isn't right? Help appreciated!
Update
I also tried a combination of the old way and the new. I mounted a new router on /${p.name}/assets, and after discovering the onProxyRequest option, I added a function there to log some output.
const assets = express.Router();
app.use(`/${p.name}/assets`, assets);
assets.use(
'*',
httpProxy({
target: p.address,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: p.version,
},
onProxyReq: (proxyReq, req, res) => {
console.log("Hello I'm being proxied now! ", proxyReq, req, res);
},
})
);
Still getting a 302, and I never see the output from the onProxyReq function.

Documentation has custom router option.
-
const proxyTable = {
'integration.localhost:3000': 'http://localhost:8001', // host only
'staging.localhost:3000': 'http://localhost:8002', // host only
'localhost:3000/api': 'http://localhost:8003', // host + path
'/rest': 'http://localhost:8004', // path only
};
const options = {
target: 'http://localhost:8000',
router: proxyTable,
};
const myProxy = createProxyMiddleware(options);
or
function customRouter(req) {
// I dont know what "p" stands for
// check the req object to see which property you want to modify
console.log(req)
const { hostname } = req
const hostName = hostname.split('.')[0]
return `https://${hostName}.sub.domain.com`
}
then add this option:
router: customRouter

Can you check your installed version - looks like you are using the v0.x.x.
interface to the http-proxy-middleware package. Latest is version 2.X.X and it exposes the following function
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use(
`/${p.name}/assets`,
createProxyMiddleware({
target: `${p.address}`,
changeOrigin: true,
pathRewrite: {
[`^${p.name}`]: `${p.version}`,
},
})
);

Related

Add IP parameter to proxy with http-proxy-middleware

I am using http-proxy-middleware for a proxy to a new version of an api. But I need to apped the URL with the request client IP address.
Example:
/api?param1=ok&param2=ok proxies to newapi.example.com/request?param1=ok&param2=ok, but I need it to be proxied to newapi.example.com/request?param1=ok&param2=ok&ip=x.x.x.x
Here is what I am using now.
const { createProxyMiddleware } = require('http-proxy-middleware');
const apiProxy = createProxyMiddleware({
target: "https://newapi.example.com/request",
changeOrigin: true,
pathRewrite: {
"^/api": "" // strip "/api" from the URL
},
onProxyRes(proxyRes) {
//proxyRes.headers['x-added'] = 'foobar'; // add new header to response
//delete proxyRes.headers['x-removed']; // remove header from response
}
});
// Expose the proxy on the "/api/*" endpoint.
export default function (req, res) {
return apiProxy(req, res);
};
Any clue will help greatly, thank you.

http-proxy-middleware: pathFilter not working as expected

I am trying so setup a npm-based HTML development environment using two servers. One hosts my HTML app of static files (standard way: http://localhost:8080) while the other exposes an application api running locally(http://127.0.0.1:57146/api/someEntity). The latter is beyond my control.
Calling the api URLs directly from my app brings up CORS issues. So i want to setup a proxy that redirects my calls from http://localhost:8080/apiBase/someEntity to http://127.0.0.1:57146/api/someEntity... hoping to avoid the CORS problems that way.
I am using lite-server, which builds on top of Browsersync.
Since my static app files do not need redirection, they should not be affected by the proxy.To specify, which calls are redirected, i am trying to use the "pathFilter" field in the options. But i cannot get that functioning. Instead all calls are always proxied to http://127.0.0.1:57146. "pathRewrite" does not seems to work, either. So it seems like i am missing something basic here. What am i doing wrong?
Here is my config file:
//bs-config.cjs
const { createProxyMiddleware } = require('http-proxy-middleware');
const apiProxy = createProxyMiddleware({
target: "http://127.0.0.1:57146/",
pathFilter:"apiBase/",
pathRewrite: {
"apiBase/": "api/"
},
logger: "console",
logLevel: "debug"
});
module.exports = {
port: 8080,
index: "parent.htm",
startPath: "parent.htm",
cors: true,
server: {
baseDir: "./dist",
index: "parent.htm",
cors: true,
middleware: [ apiProxy ]
}
};
With the help of a (very) patient backend guy, i finally found a solution.
We could not get "pathRewrite" to work, but after getting the filter functioning (by changing some syntax), tweaking the paths ended up in a working setup.
//bs-config.cjs
const { createProxyMiddleware } = require('http-proxy-middleware');
const proxy_filter = function (path, req) {
const regx = new RegExp("/apiBase");
return path.match(regx);
};
const proxy_options = {
target: "http://127.0.0.1:57146/api/",
changeOrigin: true,
logger: "console",
logLevel: "debug"
}
const apiProxy = createProxyMiddleware(proxy_filter, proxy_options);
module.exports = {
port: 8080,
index: "parent.htm",
startPath: "parent.htm",
server: {
baseDir: "./dist",
index: "parent.htm",
middleware: [
apiProxy
]
}
};

http-proxy-middleware, dynamic target

I'm trying to setup a proxy in express js, i am using http-proxy-middleware, it works perfect with hardcoded target.
server.use('/login', createProxyMiddleware({
target: 'https://pretest.test.example',
changeOrigin: true,
}))
in this case if i make a post request from my local domain (https://localhost:4200/login) it will proxy to https://pretest.test.example/login
but unfortunately its a multi site setup so i need the target to change based on the incoming request i have tried multiple ways but it doesnt seem to work.
server.use('/login', (req) => {
createProxyMiddleware({
target: resolveApiUrl(req),
changeOrigin: true
})
})
server.use('/login', createProxyMiddleware({
target: '',
changeOrigin: true,
router: (req: string) => resolveApiUrl(req)
}));
resolveApiUrl is just a function and takes the incoming request and formats the hostname to what i need it to be that works, also tried hardcoding values in the last 2 examples and that still doesnt work.
has anyone successfully managed to do dynamic targets based on incoming request with http-proxy-middleware?
After creating the middleware, you must invoke it with all three arguments, req, res and next:
server.use('/login', function(req, res, next) {
createProxyMiddleware({
target: resolveApiUrl(req),
changeOrigin: true,
})(req, res, next);
});

React js - problem with proxy for web socket link in react

I am setting up the proxy server using setupProxy for my react app using graphql as backend which is running on a different part, doing so The HTTP link proxy is working fine but WebSocket link proxy is giving me an error
For solving the problem I have tried to include options as ws: true, but it's not working.
The error is as follows:
SyntaxError: Failed to construct 'WebSocket': The URL '/ws' is invalid.
Error:
setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/graphql", { target: "http://localhost:8001/graphql" }));
app.use(
proxy("/ws", {
target: "ws://localhost:8001",
ws: true,
})
);
};
index.js
import { WebSocketLink } from "apollo-link-ws";
import { createUploadLink } from "apollo-upload-client";
//Apollo Imports End
// Declaring constants for GraphQL
const httpLink = new createUploadLink({
uri: "/graphql"
});
const wsLink = new WebSocketLink({
uri: "/ws",
options: {
reconnect: true
}
});
I expected the call should be same as normal call but its throwing an error.
/ws is not a valid URI for a WebSocket class.
A websocket expect a full URL to connect, you can try it in your browser console:
In this case, Apollo is using the native web socket class behind the scenes, so if you can make this work, it will work in Apollo too.
Try using ws://localhost:8001 instead.
Just replace with this
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/graphql", { target: "http://localhost:8001/graphql" }));
app.use(proxy("ws://locahost:8001"));
};
OR
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/graphql", { target: "http://localhost:8001/graphql" }));
app.use(proxy('/ws', {
target: 'http://localhost:8001',
ws: true
})
);
};
Adding this in setupProxy.js file worked for me :
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = app => {
const wsProxy = createProxyMiddleware("/graphql", {
ws: true,
changeOrigin: true,
autoRewrite: true,
target: "http://localhost:8001",
});
app.use(wsProxy);
};
This is what I have in my apollo-client.ts file
const httpLink = new HttpLink({
uri: "/graphql",
credentials: "include",
});
const wsLink = new WebSocketLink({
uri: `ws://${window.location.host}/graphql`,
options: {
reconnect: true,
lazy: true,
connectionParams: {
headers: {
//add your headers here
},
},
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink,
);

Websocket error with NodeBB running through a proxy server

I'm trying to run NodeBB through a node.js reverse proxy (https://docs.nodebb.org/configuring/proxies/node/)
I've followed every tutorial, hint/tip I can but am still experiencing issues with websocket connections to my NodeBB server, causing session problems, can't log in etc.
My setup is as follows:
App 1 - http://www.mywebsite.co.uk/
Node.js & Express
Routing for API & frontend website
Nothing out of the ordinary
Full code snippet at bottom of post
I am using the 'http-proxy' npm module to proxy anyone who loads http://mywebsite.co.uk/forum to http://www.myforum.co.uk/forum
This part is working, assets load as expected. However, there is a part of NodeBB which uses websockets to poll the forum for functionality purposes, user sessions. This part is not proxying correctly, or at least the NodeBB response is not correct and therefore giving me lots of errors:
"You are accessing the forum from an unknown origin. This will likely
result in websockets failing to connect. To fix this, set the "url"
value in config.json to the URL at which you access the site. For
more information, see this FAQ topic:
https://community.nodebb.org/topic/13388"
Also:
"Looks like your connection to NodeBB was lost, please wait while we
try to reconnect."
And, in the network panel, lots of 'pending' requests which eventually fail with an empty response from NodeBB.
http://mywebsite.co.uk/forum/socket.io/?EIO=3&transport=polling&t=MgJQSMk
App 2 - http://www.myforum.co.uk/forum
This app is a basic NodeBB installation running, with one plugin - (https://github.com/julianlam/nodebb-plugin-session-sharing)
The config JSON file looks like this (note the URL is my frontend app's URL as per the instructions when proxying.
{
"url": "http://www.mywebsite.co.uk/forum",
"secret": "secret",
"database": "postgres",
"port": "4567",
"postgres": {
"host": "HOST",
"port": "PORT",
"password": "PASSWORD",
"database": "DATABASE"
}
}
App 1 code:
// app
const express = require("express");
const app = express();
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'secret', resave: true, saveUninitialized: true }));
//
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Methods",
"OPTIONS, GET, POST, PUT, PATCH, DELETE"
);
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
});
// serve the content
app.use(express.static("dist"));
// Frontend
app.set('view engine', 'pug');
// serve out the api
// app.use ...
// Server set up
const httpProxy = require('http-proxy');
const HttpProxyRules = require('http-proxy-rules');
// Forum urls
let rules = {
rules: {
'/forum': 'http://www.myforum.co.uk/forum',
'/forum/*': 'http://www.myforum.co.uk/forum',
},
};
const proxyRules = new HttpProxyRules(rules);
const proxy = httpProxy.createProxy();
app.use(function (req, res, next) {
try {
if (req.url.includes("socket.io") === true) {
// console.log("SOCKET.IO", req.url)
return proxy.web(req, res, {
target: 'wss://www.myforum.co.uk',
ws: true,
changeOrigin: true
}, function (e) {
// console.log('PROXY ERR', e)
// next();
});
} else {
var target = proxyRules.match(req);
if (target) {
// console.log("TARGET", target, req.url)
return proxy.web(req, res, {
target: target,
changeOrigin: true
}, function (e) {
// console.log('PROXY ERR', e)
});
} else {
next();
}
}
} catch (e) {
// res.sendStatus(500);
res.json({ error: e });
}
});
// Frontend routes
// app.use ...
// HTTP
const http = require('http');
// Create server
mainserver = http.createServer(app);
const PORT = process.env.PORT || 3000;
mainserver.listen(PORT);
mainserver.on('listening', onListening);
mainserver.on('error', function (error, req, res) {
let json;
console.log('proxy error', error);
if (!res.headersSent) {
res.writeHead(500, { 'content-type': 'application/json' });
}
json = { error: 'proxy_error', reason: error.message };
res.end(JSON.stringify(json));
});
function onListening() {
console.log(`Listening on :${PORT}`);
}
To resolve this, I changed the Proxy module I was using. It uses less code to implement and works with websockets. I think the NodeBB Node.js proxy documentation is out of date so I'll be updating with the new method I found.
Working code is as follows:
/**
* Proxy forum
*/
const proxy = require('http-proxy-middleware');
// Forum Proxy
app.use(
'/forum',
proxy({
target: 'http://www.myforum.co.uk/forum',
changeOrigin: true,
ws: true,
})
);
Another potential GOTCHA here is that this Prixy setup needs to be declared ABOVE the 'body-parser' module (if it's being used). The wrong order will prevent post requests from being proxied.

Resources