Url forwarding does not work when the target has a base url (NodeJs http-express-proxy) - node.js

I am calling a backend API service with this line in my NodeJs application (Angular):
this.http.get<Car[]>('/server/api/v1/cars')
But get the following error : GET http://127.0.0.1:4200/server/api/v1/cars 404 (Not Found)
I did expect the url to be translated to http://127.0.0.1:8080/api-baseurl/api/v1/cars with the following server.js (run withnode server.js) :
const proxy = require('express-http-proxy');
.
.
app.use('/server', proxy('http://localhost:8080/api-baseurl'));
But it looks like proxy does not handle forwarding when the target has a base url : using this line app.use('/server', proxy('http://localhost:8080/api-baseurl')); worked when the api did not have a base url, but that is non the case anymore.

I got the answer. What I did not understand form https://www.npmjs.com/package/express-http-proxy is that the first proxy() parameter matches the host, not the specific resource path. ProxyReqPathResolver must be used for the resource path, a function in which I can edit the path :
const proxy = require('express-http-proxy');
.
.
// Set our api routes proxy to point to backend server
app.use('/server', proxy('http://localhost:8080', {
proxyReqPathResolver: function (req) {
updatedPathComplete = '/api-baseurl' + req.url;
console.log('proxy on /server: ' + req.url + ' => ' + updatedPathComplete);
return updatedPathComplete;
}
}));
In the console log :
> node server-prod.js
API running on 4200
proxy on /server: /api/v1/cars => /api-baseurl/api/v1/cars

Related

How to Connect Reactivesearch to an external Elasticsearch cluster?

I am trying to connect my reacetivesearch application to external elasticsearch provider( not AWS). They dont allow making changes to the elasticsearch cluster and also using nginx in front of the cluster .
As per the reacetivesearch documentation I have cloned the proxy code and only made changes to the target and the authentication setting(as per the code below ) .
https://github.com/appbaseio-apps/reactivesearch-proxy-server/blob/master/index.js
Proxy is successfully starting and able to connect the remote cluster . However when I connect reacetivesearch app through proxy I get the following error.
Access to XMLHttpRequest at 'http://localhost:7777/testing/_msearch?' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource
I repeated the same steps with my local elasticsearch cluster using the same proxy code and getting the same error .
Just was wondering do we need to make any extra changes to make sure the proxy sending the right request to the elasticsearch cluster ? I am using the below code for the proxy.
const express = require('express');
const proxy = require('http-proxy-middleware');
const btoa = require('btoa');
const app = express();
const bodyParser = require('body-parser')
/* This is where we specify options for the http-proxy-middleware
* We set the target to appbase.io backend here. You can also
* add your own backend url here */
const options = {
target: 'http://my_elasticsearch_cluster_adddress:9200/',
changeOrigin: true,
onProxyReq: (proxyReq, req) => {
proxyReq.setHeader(
'Authorization',
`Basic ${btoa('username:password')}`
);
/* transform the req body back from text */
const { body } = req;
if (body) {
if (typeof body === 'object') {
proxyReq.write(JSON.stringify(body));
} else {
proxyReq.write(body);
}
}
}
}
/* Parse the ndjson as text */
app.use(bodyParser.text({ type: 'application/x-ndjson' }));
/* This is how we can extend this logic to do extra stuff before
* sending requests to our backend for example doing verification
* of access tokens or performing some other task */
app.use((req, res, next) => {
const { body } = req;
console.log('Verifying requests ✔', body);
/* After this we call next to tell express to proceed
* to the next middleware function which happens to be our
* proxy middleware */
next();
})
/* Here we proxy all the requests from reactivesearch to our backend */
app.use('*', proxy(options));
app.listen(7777, () => console.log('Server running at http://localhost:7777 🚀'));
Regards
Yep you need to apply CORS settings to your local elasticsearch.yaml as well as your ES service provider.
Are you using Elastic Cloud by any chance? They do allow you to modify Elasticsearch settings.
If so:
Login to your Elastic Cloud control panel
Navigate to the Deployment Edit page for your cluster
Scroll to your '[Elasticsearch] Data' deployment configuration
Click the User setting overrides text at the bottom of the box to expand the settings editor.
There's some example ES CORS settings about halfway down the reactivebase page that provide a great starting point.
https://opensource.appbase.io/reactive-manual/getting-started/reactivebase.html
You'll need to update the provided http.cors.allow-origin: setting based on your needs.

Nodejs REST API to rewrite url or work as middleware for accessing distant photos

I am making a rest api with node/express for exposing data with assests urls from another servers , like this :
Client ------> RestAPI with nodeJs/express (API Y) --------> API for Images (API X)
The API for Images (API X): provide json or links for images with url like "http://APIX.com/link1/X.jpg"
The assets / images are located in (API X) server .
What am trying to do is when the client calls the (API Y) and want to get data with image urls like "http://APIY.com/api/X.jpg" ,the (API Y) fetch the images from (API X) and return it to client with links from (API Y), so the client will not know the correct source of images and think that the images are hosted in (API Y) server.
Any idea on how can i implement this in NODE js/express ? thx.
Thanks #jfriend00 , i manage to find a solution with your "proxy" suggestion, i used the http-proxy-middlware npm package , like follow :
I am using express and http-proxy-middleware with Typescript
import express from "express";
import proxy from "http-proxy-middleware";
export default class APIYServer {
constructor(private port: number) {}
public start(): void {
const app = express();
app.use(
//the Url exposed with APIY
"/APIY/assets",
proxy({
//the Url of the APIX server
target: "https://www.APIX.com/",
changeOrigin: true,
pathRewrite: {
//"/APIX-assets-path/" is the path where images are located
"^/APIY/assets": "/APIX-assets-path/"
},
onProxyRes
})
);
//function for handling proxy response
function onProxyRes(proxyResponse: any, request: any, response: any) {
if (proxyResponse.statusCode != 200) {
console.log("---FAIL ---");
}
// DELLETING COOKIES INFOS so the client can't find source of images
Object.keys(proxyResponse.headers).forEach(function(key) {
delete proxyResponse.headers[key];
});
delete proxyResponse.headers['Set-Cookie'];
}
//run APIY server on port 8888
app.listen(this.port, () => {
console.log("Server Started on 8888");
});
}
}
When calling "localhost:8888/APIY/assets/01.png" its give me the images located in "https://www.APIX.com/APIX-assets-path/01.png"...and that's what i m looking for :D

nodejs app is not able to connect to REST api nodejs app

I have a nodejs REST api hosted on localhost and I have a nodejs app that is consuming it. This app too is running on localhost. Everything was working fine but after a restart the webapp just could not connect to the REST api anymore. I am running Windows 10.
I tested the REST api with postman and also with browser, it worked. There is no issue with the REST api.
Tried changing the port numbers - same result.
I ran wireshark to see the difference between when requesting from browser and from nodejs webapp. Below is the screenshot. First two lines are when the nodejs app made the request and the next two are from browser.
I am not able to understand what is wrong here. I tried with a standalone nodejs script, that too failed. Below is the script I used.
var request = require('request');
var u = "xxx";
var p = "xxx";
var auth = "Basic " + new Buffer(u + ":" + p).toString("base64");
var username = "qqqq";
var password = "eeee";
var options = {
url : 'http://localhost:4001/api/v1/transaction',
headers : {
"Authorization" : auth
},
};
console.log(options.url);
request.get(options, function(error,response,body){
//console.log(options);
//console.log(response);
console.log(response.statusCode);
console.log(body);
if (!error && response.statusCode == 200) {
var userObj = JSON.parse(body);
callback(userObj);
} else {
console.log("---- Error ----")
console.log(error);
}
});
I have found the problem and I am posting the answer in the hope that someone would find it useful.
My hint was from the wireshark. (screenshot in the question) All successful requests went to [::1] not localhost or 127.0.0.1. After the reboot of the windows 10 machine, the REST api nodejs app was actually no longer serving on the ip v4 localhost but was serving on ip v6 localhost. There was absolutely no issue with the code.
Instead of using localhost in the url in the consuming webapp, I changed it to [::1] and it started to work.
.....
.....
var options = {
//url : 'http://localhost:4001/api/v1/transaction',
// replaced localhost with [::1]
url : 'http://[::1]:4001/api/v1/transaction',
headers : {
"Authorization" : auth
},
};
.....
.....

node.js http-proxy proxy based on url

I would like to create a proxy based on URL, such that you can go to: blah.com:8000/tolley-ltm and it proxies that request to my local workstation such as tolley.internal.blah.com based on the URL. So I could also do blah.com:8000/someguy-ltm and it goes to some guy's workstation at someguy-ltm.internal.blah.com. The end user will only ever see blah.com:8000/tolley-ltm
I have this sort of working, but not 100% and I would like your help! Here is the current code:
var fulltld ="internal.blah.com";
var proxyServer = httpProxy.createProxy();
var options = {
'/tolley-ltm': 'tolley-ltm',
'/someguy-ltm': 'someguy-ltm'
}
require('http').createServer(function(req, res) {
var dest = req.url;
req.url = '/';
fail = function(err){
console.log("Something bad happened: " + err);
}
proxyServer.web(req, res, {
target: {
host: options[dest] + '.' + fulltld,
port: 6109
}
},fail);
}).listen(8000);
So what happens currently is I go to blah.com:8000/tolley-ltm in my browser and it successfully goes to tolley.internal.blah.com:6109, however when I navigate since I changed the req.url to / instead of tolley-ltm every subsequent actions then goes to blah.com:8000/ instead of blah.com:8000/tolley-ltm and this causes the proxy to stop working.
Sidenote: If I don't change the req.url to '/' it ends up proxying to tolley.internal.blah.com:6109/tolley-ltm instead of just tolley.internal.blah.com:6109/
Is there a way I can keep the url to the end user looking like blah.com/8000/tolley-ltm and have all actions call back to blah.com/8000-ltm, such as if I clicked a link to go to /products, it would take me to blah.com/8000/tolley-ltm/products instead of blah.com/8000/products

How to redirect multiple subdomains to the same running express app

I'm building a SaaS app in NodeJS and using the Express framework. The individual members of the website has a url with a custom subdomain to login.
For example, a company called ABC Corp may login at abc.example.com and another company called Sony may login at sony.example.com
Any idea how I can redirect/route multiple subdomains to the same app instance?
You can use the express-subdomain package. Assuming you have a routes folder, containing abc.js and sony.js files that respectively export login routes for the abc and sony subdomains, you could have the following in index.js or whatever file from which your express server is listening.
const subdomain = require('express-subdomain');
const express = require('express');
const app = express();
const abcRoutes = require('./routes/abc');
const sonyRoutes = require('./routes/sony');
const port = process.env.PORT || 3000;
// use the subdomain middleware
app.use(subdomain('abc', abcRoutes));
app.use(subdomain('sony', sonyRoutes));
// a simple get route on the top-level domain
app.get('/', (req, res) => {
res.send('Welcome to the Home Page!');
});
// add any other needed routes
module.exports = app.listen(port, () => {
console.log('Server listening on port ' + port);
});
Your server will then be live and working as expected
http://example.com/ --> Welcome to the Home Page!
http://abc.example.com/login --> (Your login page for abc)
http://sony.example.com/login --> (Your login page for sony)
To tests subdomains locally you need to add them to your /etc/hosts file. (it requires sudo permissions)
127.0.0.1 localhost
127.0.0.1 example.com
127.0.0.1 abc.example.com
127.0.0.1 sony.example.com
The equivalent for /etc/hosts file on windows is at %systemroot%\system32\drivers\etc
For better details on setting up localhost domains locally check here
You can do more with the subdomain package. It accepts wildcards and you can use it to check API keys if you need such a feature.
Checkout the documentation for the express-subdomain package at https://npmjs.com/package/express-subdomain
You can actually handle that particular route or a wide range then go for Reg Exp (which allows you to do this app.get(new RegExp('(your|string)\/here'), function…) which you want to redirect and then follow the redirecting action something like below code is doing:
response.writeHead(302, {
'Location': 'yourpath/page.html'
//add other headers here...
});
response.end();
Update 1 : [as per the comments and other updates]
Then you try to handle all requests with the following app:
express()
.use(express.vhost('abc.example.com', require('/path/to/loginApp').app))
.use(express.vhost('sony.example.com', require('/path/to/loginApp').app))
.listen(80)
where /path/to/loginApp can be absolute paths or relative paths.
I hope this solves your problem.
Update 2:
Actually when a request comes in the request event is raised on a HTTP Server. So basically it is handled by express, express.vhost is a middle-ware function which raises the request event on another instance of a HTTP Server, that is how it works.
Below is the code:
function vhost(req, res, next){
if (!req.headers.host) return next();
var host = req.headers.host.split(':')[0];
if (req.subdomains = regexp.exec(host)) {
req.subdomains = req.subdomains[0].split('.').slice(0, -1);
server.emit('request', req, res);
} else {
next();
}
};

Resources