Express JS proxy to call web api - node.js

I have the following code. And a web api which returns string array,
const express = require('express');
const proxy = require('express-http-proxy');
var app = express();
app.use('/proxy', proxy('http://localhost:56660/api/values'));
app.listen(3000);
When I tried to do localhost:3000/proxy I do not get a response,
But when I use app.use('/proxy', proxy('www.google.com')); , it redirects to google web site.
Please suggest me a best approach/solution:
I want to create a proxy server which gets url from browser (Application), modify the url, call the new url and send the response back to browser(Application).

You can get the URL to be proxied as a query parameter, modify it and then pass that URL to proxy, like this (use instead of app.use('/proxy', proxy('http://localhost:56660/api/values'));):
app.get('/proxy', (req, res, next) => {
const modifiedURL = modifyURL(req.query.url)
return proxy(modifiedURL)(req, res, next)
})
You can call you server with an URL like this (GET method):
https://my.server.com/proxy?url=https://urltobeproxied.com
UPDATE:
I think this would work according to your needs:
app.use('/proxy', (req, res, next) => {
const requestedUrl = `${req.protocol}://${req.get('Host')}${req.url}`
const modifiedURL = modifyURL(requestedUrl)
proxy(modifiedURL)(req, res, next)
})
UPDATE2:
app.use('/proxy', proxy('http://localhost:56660/api/values', {
proxyReqPathResolver: function(req) {
const requestedUrl = `${req.protocol}://${req.get('Host')}${req.url}`
const modifiedURL = modifyURL(requestedUrl)
return require('url').parse(modifiedURL).path;
}
}))
UPDATE3:
An example of proxy modifying the response (extracted from the package docs);
app.use('/proxy', proxy('http://localhost:56660/api/values', {
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
data = JSON.parse(proxyResData.toString('utf8'));
data.newProperty = 'exciting data';
return JSON.stringify(data);
}
}))

Related

Header name must be a valid HTTP token, nodejs?

I am trying to create API which will return self hosted URL.
This is my code
const app = require('./app');
const config = require('./config/config');
const logger = require('./config/logger');
let server;
server = app.listen(config.port, config.host, () => {
logger.info(`Listening to port ${config.port} on host ${config.host}`);
});
app.get('/:sid/:ui', (req, res, next) => {
console.log('OK')
})
app.get('/url', (req, res, next) => {
console.log('inside')
res.append(config.host+":"+config.port+"/:sid/:ui")
})
I am looking for result to be as
http://127.0.0.1:3005/sid/ui
When I hit the 2 url http://127.0.0.1:3005/url I get the following error
TypeError [ERR_INVALID_HTTP_TOKEN]: Header name must be a valid HTTP token ["127.0.0.1:3005/:sid/:ui"]
at ServerResponse.setHeader (node:_http_outgoing:578:3)
at ServerResponse.header (C:\Users\wgupta\node_modules\express\lib\response.js:794:10)
at ServerResponse.append (C:\Users\wgupta\node_modules\express\lib\response.js:755:15)
at C:\Backend\temp1\src\index.js:14:7
Use res.send() instead of res.append()
Syntax issue solved it using
res.send(config.host+":"+config.port+"/sid/ui")

How to duplicate and forward a request with koa router

For several reasons, I have a server that has to forward requests to another server. The response should be the response of the final server. I also need to add an extra header onto the request but remove this header again from the response before returning. As such, redirect isn't going to cut it.
I'm currently doing it manually copying the headers & body as required but I would like to know if there's a simple generic way to do it?
A proxy would work for this. Assuming #koa/router or something simliar and the http-proxy module (there are also wrapper modules for Koa that may work:
const proxy = httpProxy.createProxyServer({
target: 'https://some-other-server.com',
// other options, see https://www.npmjs.com/package/http-proxy
})
proxy.on('proxyReq', (proxyReq, req, res, options) => {
proxyReq.setHeader('x-foo', 'bar')
})
proxy.on('proxyRes', (proxyRes, req, res) => {
proxyRes.removeHeader('x-foo')
})
router.get('/foo', async (ctx) => {
// ctx.req and ctx.res are the Node req and res, not Koa objects
proxy.web(ctx.req, ctx.res, {
// other options, see docs
})
})
You could also lift the proxy out of a route if you happen to be starting your Koa server with http.createServer rather than app.listen:
// where app = new Koa()
const handler = app.callback()
http.createServer((req, res) => {
if (req.url === '/foo') {
return proxy.web(req, res, options)
}
return handler(req, res)
})

Nodejs Express - return 405 for un-supported method

I'm running a standard NodeJs 8 with Express and currently when a request for an existing path but un-supported method comes in, Express return 404.
For example 'POST /login' is supported, but 'GET /login' is not, but it returns 404.
How can I make Express return 405 in such a case?
Here's the routes file:
const express = require('express');
const router = express.Router();
const loginController = require('../controllers/login');
router.route('/login').post(loginController.loginUser);
module.exports = router;
Please advise.
You can simply add the .all() handler to your route chain, like so:
const methodNotAllowed = (req, res, next) => res.status(405).send();
router
.route(`/login`)
.post(loginController.loginUser)
.all(methodNotAllowed);
Explanation
This works because requests are passed to the handlers in the order they are attached to the route (the request "waterfall"). The .post() handler will catch your POST requests, and the rest will fall through to the .all() handler.
Also see this question for more details.
Authenticating all POST routes
If you would like to ensure that the user is logged in for all POST requests, but return a 405 response for any other requests, you can use a regular expression to match all routes with router.post('*'), like so:
router
.post(`*`, loginController.loginUser)
.all(methodNotAllowed);
The problem with this approach, however, is that no 404 errors will ever be returned to the client, only 405. Therefore I recommend attaching the methodNotAllowed handler to each individual route, like in the first code snippet above. This approach will return 404 errors for routes that don't exist, but 405 errors for routes that do.
Determining the available methods for a route
To determine which methods are allowed for a route, use router.stack:
app.use((req, res, next) => {
const methods = router.stack
// Filter for the route that matches the currently matched route
.filter(layer => layer.route.path === req.path)[0]
.route
.methods;
if (!methods[req.method]) methodNotAllowed(req, res, next);
else next();
});
You can try this that way:
app.route("/login")
.get((req, res) => {
/* HANDLE GET */
})
.post((req, res) => {
/* HANDLE POST */
})
.all((req, res) => {
res.status(405).send();
});
How it works?
If request matches the route. It will go through the handlers. If a handler is present, it will be handled using that specific one. Otherwise, it will reach the 'all' handler that will set the status code to 405 and send the response.
Here You can find the discussion about it:
405 issue
#You question below:
You can try that way:
loginRoutes.js content:
const router = require('express').Router();
router.route('/')
.get((req, res) => {
res.status(200).send()
})
module.exports = router
server file content:
const express = require('express')
const app = express();
const router = express.Router();
const loginRoutes = require('./loginRoutes')
const PORT = process.env.PORT || 8080;
router.use('/login', loginRoutes)
router.route('/login').all((req, res) => { res.status(405).send() })
app.use(router);
app.listen(PORT, () => console.log(`started on port: ${PORT}`))
You can use this snippet of code to automatically send 405 status code when route from the same path exist but not with the current method
app.use(function (req, res, next) {
const AllLayers = app._router.stack
const Layers = AllLayers.filter(x => x.name === 'bound dispatch' && x.regexp.test(req.path))
const Methods = [];
Layers.forEach(layer => {
for (let method in layer.route.methods) {
if (layer.route.methods[method] === true) {
Methods.push(method.toUpperCase());
}
}
})
if (Layers.length !== 0 && !Methods.includes(req.method)) {
res.setHeader('Allow', Methods.join(','))
if (req.method === "OPTIONS") {
return res.send(Methods.join(', '))
}
else {
return res.sendStatus(405);
}
}
else {
next();
}
});
Hope this could be helpfull to someone
If you want to determine what methods COULD have been used you need to do a lot of digging in the app function you start your server with, and through some string manipulation and the like you can figure out what the possible methods are and return them in the error. If you're interested in how its done check out https://github.com/Justinlkirk/express-ez-405 or just use the npm package here https://www.npmjs.com/package/express-ez-405

Node.js with Express: how to redirect a POST request

I want to redirect from one URL request to another 'POST' request, like this:
var app = require('express')();
app.get('/', function(req, res) {
res.redirect('/test');
});
app.post('/test', function(req, res) {
res.send('/test page');
});
app.listen(3000, function() {
console.log('listenning on port:3000');
});
However, I can't redirect to '/test' page because it is a POST request. So what should I do to make the redirection work, keeping the '/test' request POST?
You can do this:
app.post('/', function(req, res) {
res.redirect(307, '/test');
});
Which will preserve the send method.
For reference, the 307 http code spec is:
307 Temporary Redirect (since HTTP/1.1) In this occasion, the request
should be repeated with another URI, but future requests can still use
the original URI.2 In contrast to 303, the request method should not
be changed when reissuing the original request. For instance, a POST
request must be repeated using another POST request.
For more info, see: http://www.alanflavell.org.uk/www/post-redirect.html
Keep in mind the middleware architecture: Each handler may manipulate the context, and either respond - or - call next().
By this premise, the express router is basically a middleware function you may use after "correcting" the url.
(BTW, the request app is also a function, although I'm not sure if I recommend going back so early in the chain)
Here's a kind'a example:
const router = new require('express').Router()
const user = require('../model/user')
//assume user implements:
// user.byId(id) -> Promise<user>
// user.byMail(email) -> Promise<user>
const reqUser = userPromise => (req, res, next) =>
req.user
? next()
: userPromise(req)
.then(user => { req.user = user })
.then(next, next)
//assume the sever that uses this router has a
//standard (err, req, res, next) handler in the end of the chain...
const byId = reqUser( req => user.byId(req.params.id) )
const byMail = reqUser( req => user.byMail(req.params.mail) )
router.post('/by-id/:id/friends',
byId,
(req, res) => res.render('user-friends', req.user)
)
router.post('/by-email/:email/friends',
byMail,
(req, res, next) => {
req.url = `/by-id/${req.user.id}/friends`
next()
},
router
)
The only difference between 307 and 302 is that 307 guarantees that the method and the body will not be changed when the redirected request is made.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307
I believe the question is that the node server is receiving a POST request but needs to redirect it to a different server as GET request. I recently had to deal with something similar. Here is how I solved it:
var proxy = require('express-http-proxy');
app.use('incomin/url', proxy('forwarding:server', {
//The proxyRqDecorator allows us to change a few things including the request type.
proxyReqOptDecorator: (proxyReqOpts, srcReq) => {
proxyReqOpts.method = 'GET';
return proxyReqOpts;
},
//The proxyReqPathResolver takes the Given URL and updates it to the forward path
proxyReqPathResolver: function (req) {
return new Promise( (resolve, reject) => {
setTimeout( () =>{
var value = req.body.key;
var resolvedPathValue = 'forwarding/url' + value;
console.log(`Inside forward path. The resolved path is ${resolvedPathValue}`);
resolve(resolvedPathValue);
}, 200);
});
}
}));
Keep in mind that the above proxyReqPathResolver is setup async. The synchronous vesrion and more info on express-http-proxy are described here:
https://www.npmjs.com/package/express-http-proxy

Add custom headers to 'request'

I am proxying my api via following setup in my express config
// Proxy api calls
app.use('/api', function (req, res) {
let url = config.API_HOST + req.url
req.pipe(request(url)).pipe(res)
})
config.API_HOST in here resolves to my api url and req.url is some endpoint i.e. /users I tried following documentation on npm for request and set up my headers like so
// Proxy api calls
app.use('/api', function (req, res) {
let options = {
url: config.API_HOST + req.url,
options: { 'mycustomheader': 'test' }
}
req.pipe(request(options)).pipe(res)
})
But I am not able to see my custom headers in chrome dev tools under Network.
Was able to achieve it this way
app.use('/api', function (req, res) {
let url = config.API_HOST + req.ur
req.headers['someHeader'] = 'someValue'
req.pipe(request(url)).pipe(res)
})

Resources