BackendStack21 / fast-gateway

Fast-gateway is an easy to use Node.js API gateway framework built to handle large scale API traffic with great performance and scalability. Written with JavaScript, it is a framework for the masses!
MIT License
311 stars 34 forks source link

Not able to Implement Rate Limit #17

Closed rajkumarpb closed 4 years ago

rajkumarpb commented 4 years ago

I am trying to implement rate limiting in fast-gateway but I am not able to use it since it expects the req.ip object which I believe is not available with fast-gateway. Does that mean I need to import express and create app just to do rate-limiting?

It'd be great if someone pointed out some examples to get me started.

Just used the sample code to get started:

require('dotenv').config();
const gateway = require('fast-gateway')
const PORT = process.env.PORT || 9090

const service = gateway({
  middlewares: [
    require('cors')(),
    require('helmet')(),
  ],
  // Can add routes here!
  routes: [{
    prefix: '/verify',
    target: process.env.VERIFICATION_API
  }]
});

service.start(PORT).then(server => {
    console.log(`API Gateway listening on ${PORT} port!`)
})
jkyberneees commented 4 years ago

Hi @rajkumarpb thanks for asking. You don't need to use express in order to populate the user remote IP address, instead, I would recommend you just extend fast-gateway by adding a custom middleware like this:

const requestIp = require('request-ip');

middlewares: [
    (req, res, next) => {
        req.ip = requestIp.getClientIp(req) // you might need to check the expected ip object format here...
        next()
    },
    require('cors')(),
    require('helmet')(),
],

Using express on a performance critical component is not a good idea, fast-gateway is also not compatible with it.

You can read more about fast-gateway HTTP server performance here: https://medium.com/sharenowtech/node-js-api-gateway-a-developer-perspective-8defe575ed21

Thanks

rajkumarpb commented 4 years ago

Implemented request-ip as you mentioned. Now trying to implement express-rate-limiter and used below code.

require('dotenv').config();
const gateway = require('fast-gateway')
const requestIp = require('request-ip');
const PORT = process.env.PORT || 9090
const rateLimit = require('express-rate-limit');

const service = gateway({
  middlewares: [
    (req, res, next) => {
        req.ip = requestIp.getClientIp(req) // you might need to check the expected ip object format here...
        next()
    },
    require('cors')(),
    require('helmet')(),
  ],
  // Can add routes here!
  routes: [{
    prefix: '/authapi',
    target: process.env.AUTH_API
  }]
});

service.use(
    rateLimit({
      windowMs: 1 * 60 * 1000, // 1 minutes
      max: 60, // limit each IP to 60 requests per windowMs
    }),
);

service.start(PORT).then(server => {
    console.log(`API Gateway listening on ${PORT} port!`)
})

If i use loadtest tool to send 100 requests, it works fine. But if I increase it to more than 100, I am getting below error. Any idea why am I getting this error? I am using the same package in my other project and it works just fine. Please help me out!

(node:5129) UnhandledPromiseRejectionWarning: TypeError: res.status is not a function
    at Object.handler (/opt/nestws/api-gateway/node_modules/express-rate-limit/lib/express-rate-limit.js:22:13)
    at /opt/nestws/api-gateway/node_modules/express-rate-limit/lib/express-rate-limit.js:131:28
jkyberneees commented 4 years ago

I will provide you more feedback here ASAP

jkyberneees commented 4 years ago

Hi @rajkumarpb, next I describe a full example:

Gateway:

const gateway = require('../index')
const rateLimit = require('express-rate-limit')
const requestIp = require('request-ip')

gateway({
  middlewares: [
    // acquire request IP
    (req, res, next) => {
      req.ip = requestIp.getClientIp(req)
      return next()
    },
    // rate limiter
    rateLimit({
      windowMs: 1 * 60 * 1000, // 1 minutes
      max: 60, // limit each IP to 60 requests per windowMs
      handler: (req, res) => {
        res.send('Too many requests, please try again later.', 429)
      }
    })
  ],
  routes: [{
    prefix: '/public',
    target: 'http://localhost:3000'
  }]
}).start(8080).then(() => console.log('API Gateway listening on 8080 port!'))

Remote Service:

const service = require('restana')({})
service
  .get('/hi', (req, res) => res.send('Hello World!'))
  .start(3000).then(() => console.log('Public service listening on 3000 port!'))

Please let me know if it works for you now?

Regards

rajkumarpb commented 4 years ago

@jkyberneees Yeah, it's working fine now. Before this I tried the same way but without handler in rateLimit method. Thanks for the help. It would be great if you could add this in documentation as this is common scenario for any gateway and having it in official documentation would be helpful for many like me. Once again, thanks for the quick help!

jkyberneees commented 4 years ago

hi @rajkumarpb, thanks. You are right, I will refer to this feature in the main documentation.

Do not hesitate to reach back again if you find any blocker.