Skip to content
This repository has been archived by the owner on Nov 23, 2022. It is now read-only.

Commit

Permalink
Merge pull request #20 from ozkanonur/feat/express-graphql-support
Browse files Browse the repository at this point in the history
Feat/express graphql support
  • Loading branch information
onur-ozkan authored Sep 6, 2020
2 parents 6eae11f + 2bd5798 commit ca55f70
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 26 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ duplicate `keyPrefix` or reuse the same class and method names with the decorato
### Constructor Options
| Option Name | Required | Type | Default |
| ------ | ------ | ------ | ------|
| for | false | 'Express' - 'Fastify' - 'Microservice' | 'Express' |
| for | false | 'Express' - 'Fastify' - 'Microservice' - 'ExpressGraphql' | 'Express' |
| type | false | 'Memory' - 'Redis' - 'Memcache' - 'Postgres' - 'MySQL' | 'Memory' |
| points | false | number | 4 |
| duration | false | number | 1 |
Expand Down Expand Up @@ -164,14 +164,15 @@ config needed. For a full list see <https://github.com/animir/node-rate-limiter-

The main important options (and the ones used solely by this library) are below.

### for: 'Express' | 'Fastify' | 'Microservice'
### for: 'Express' | 'Fastify' | 'Microservice' | 'ExpressGraphql'

This is the value which is based technology of your project. The default Nest applications are Express therefore this value is also comes with Express value as default.

See official documentation for other supported technologies other than Express:

- [Fastify](https://docs.nestjs.com/techniques/performance)
- [Microservices](https://docs.nestjs.com/microservices/basics)
- [Graphql](https://docs.nestjs.com/graphql/quick-start)

### type: 'Memory' | 'Redis' | 'Memcached' | 'Postgres' | 'MySQL'

Expand Down Expand Up @@ -401,3 +402,9 @@ export class ApplicationModule {}
Note that this limiter also supports using [knex](https://knexjs.org/) or [sequelize](http://docs.sequelizejs.com/) with
an additional parameter as noted at
<https://github.com/animir/node-rate-limiter-flexible/wiki/MySQL#sequelize-and-knex-support>.

## TODO
- [ ] Fastify based Graphql Apps
- [ ] Websocket
- [ ] Rpc
- [ ] Tests & Github Actions (for automatic npm deployment on master branch)
53 changes: 32 additions & 21 deletions lib/rate-limiter.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Reflector } from '@nestjs/core'
import { Observable } from 'rxjs'
import { NestInterceptor, Injectable, ExecutionContext, CallHandler, Inject, HttpStatus, Logger } from '@nestjs/common'
import {
RateLimiterMemory,
Expand All @@ -26,16 +25,10 @@ export class RateLimiterInterceptor implements NestInterceptor {
this.options = { ...defaultRateLimiterOptions, ...this.options }
}

async getRateLimiter(
keyPrefix: string,
options?: RateLimiterModuleOptions
): Promise<RateLimiterMemory & { for?: 'Express' | 'Fastify' | 'Microservice'; errorMessage?: string }> {
async getRateLimiter(keyPrefix: string, options?: RateLimiterModuleOptions): Promise<RateLimiterMemory> {
this.options = { ...this.options, ...options }

let rateLimiter: RateLimiterMemory & {
for?: 'Express' | 'Fastify' | 'Microservice'
errorMessage?: string
} = this.rateLimiters.get(keyPrefix)
let rateLimiter: RateLimiterMemory = this.rateLimiters.get(keyPrefix)

const limiterOptions: RateLimiterModuleOptions = {
...this.options,
Expand Down Expand Up @@ -95,7 +88,7 @@ export class RateLimiterInterceptor implements NestInterceptor {
return rateLimiter
}

async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
async intercept(context: ExecutionContext, next: CallHandler): Promise<any> {
let points: number = this.options.points
let pointsConsumed: number = this.options.pointsConsumed
let keyPrefix: string = this.options.keyPrefix
Expand Down Expand Up @@ -125,25 +118,45 @@ export class RateLimiterInterceptor implements NestInterceptor {
}
}

const rateLimiter: RateLimiterMemory & {
for?: 'Express' | 'Fastify' | 'Microservice'
errorMessage?: string
} = await this.getRateLimiter(keyPrefix, reflectedOptions)
const request = this.httpHandler(context).req
const response = this.httpHandler(context).res

const request = context.switchToHttp().getRequest()
const response = context.switchToHttp().getResponse()
const rateLimiter: RateLimiterMemory = await this.getRateLimiter(keyPrefix, reflectedOptions)
const key = request?.user ? request.user.id : request.ip

const key = request.user ? request.user.id : request.ip
await this.responseHandler(response, key, rateLimiter, points, pointsConsumed)
return next.handle()
}

private httpHandler(context: ExecutionContext) {
if (this.options.for === 'ExpressGraphql') {
return {
req: context.getArgByIndex(2).req,
res: context.getArgByIndex(2).req.res
}
} else {
return {
req: context.switchToHttp().getRequest(),
res: context.switchToHttp().getResponse()
}
}
}

if (this.options.for == 'Fastify') {
private async responseHandler(
response: any,
key: any,
rateLimiter: RateLimiterMemory,
points: number,
pointsConsumed: number
) {
if (this.options.for === 'Fastify') {
try {
const rateLimiterResponse: RateLimiterRes = await rateLimiter.consume(key, pointsConsumed)

response.header('Retry-After', Math.ceil(rateLimiterResponse.msBeforeNext / 1000))
response.header('X-RateLimit-Limit', points)
response.header('X-Retry-Remaining', rateLimiterResponse.remainingPoints)
response.header('X-Retry-Reset', new Date(Date.now() + rateLimiterResponse.msBeforeNext).toUTCString())

} catch (rateLimiterResponse) {
response.header('Retry-After', Math.ceil(rateLimiterResponse.msBeforeNext / 1000))
response.code(429).header('Content-Type', 'application/json; charset=utf-8').send({
Expand All @@ -160,7 +173,6 @@ export class RateLimiterInterceptor implements NestInterceptor {
response.set('X-RateLimit-Limit', points)
response.set('X-Retry-Remaining', rateLimiterResponse.remainingPoints)
response.set('X-Retry-Reset', new Date(Date.now() + rateLimiterResponse.msBeforeNext).toUTCString())

} catch (rateLimiterResponse) {
response.set('Retry-After', Math.ceil(rateLimiterResponse.msBeforeNext / 1000))
response.status(429).json({
Expand All @@ -170,6 +182,5 @@ export class RateLimiterInterceptor implements NestInterceptor {
})
}
}
return next.handle()
}
}
2 changes: 1 addition & 1 deletion lib/rate-limiter.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { IRateLimiterMongoOptions } from 'rate-limiter-flexible'
import { ModuleMetadata, Type } from '@nestjs/common/interfaces'

export interface DefaultOptions {
for?: 'Express' | 'Fastify' | 'Microservice'
for?: 'Express' | 'Fastify' | 'Microservice' | 'ExpressGraphql'
type?: 'Memory' | 'Redis' | 'Memcache' | 'Postgres' | 'MySQL'
points?: number
duration?: number
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nestjs-rate-limiter",
"version": "2.1.0",
"version": "2.2.0",
"description": "Highly configurable a rate limiter library",
"repository": {
"type": "git",
Expand Down

0 comments on commit ca55f70

Please sign in to comment.