|  | 
| 7 | 7 |  </div> | 
| 8 | 8 | 
 | 
| 9 | 9 |   | 
| 10 |  | -## Summary | 
|  | 10 | +## Summary | 
| 11 | 11 | 
 | 
| 12 |  | -Developed under tech-accelerator [OSLabs](https://opensourcelabs.io/), GraphQLGate strives for a principled approach to complexity analysis and rate-limiting for GraphQL queries by accurately estimating an upper-bound of the response size of the query. Within a loosely opinionated framework with lots of configuration options, you can reliably throttle GraphQL queries by complexity and depth to protect your GraphQL API. Our solution is inspired by [this paper](https://github.com/Alan-Cha/fse20/blob/master/submissions/functional/FSE-24/graphql-paper.pdf) from IBM research teams. | 
|  | 12 | +Developed under tech-accelerator [OSLabs](https://opensourcelabs.io/), GraphQLGate strives for a principled approach to complexity analysis and rate-limiting for GraphQL queries by accurately estimating an upper-bound of the response size of the query. Within a loosely opinionated framework with lots of configuration options, you can reliably throttle GraphQL queries by complexity and depth to protect your GraphQL API. Our solution is inspired by [this paper](https://github.com/Alan-Cha/fse20/blob/master/submissions/functional/FSE-24/graphql-paper.pdf) from IBM research teams. | 
| 13 | 13 | 
 | 
| 14 | 14 | ## Table of Contents | 
| 15 | 15 | 
 | 
| @@ -201,29 +201,76 @@ This package exposes 3 additional functionalities which comprise the internals o | 
| 201 | 201 | 
 | 
| 202 | 202 | ### Complexity Analysis | 
| 203 | 203 | 
 | 
| 204 |  | -1. #### `typeWeightsFromSchema` | create the type weight object from the schema for complexity analysis | 
| 205 |  | - - `schema: GraphQLSchema` | GraphQL schema object | 
| 206 |  | - - `typeWeightsConfig: TypeWeightConfig = defaultTypeWeightsConfig` | type weight configuration | 
| 207 |  | - - `enforceBoundedLists = false`  | 
| 208 |  | - - returns: `TypeWeightObject`  | 
|  | 204 | +1. #### `typeWeightsFromSchema` | function to create the type weight object from the schema for complexity analysis | 
| 209 | 205 | 
 | 
| 210 |  | -2. #### `ComplexityAnalysis` | calculate the complexity of the query based on the type weights and variables | 
| 211 |  | - - `typeWeights: TypeWeightObject` | 
| 212 |  | - - `variables: Variables` | variables on request | 
| 213 |  | - - returns a class with method: | 
| 214 |  | - - `processQuery(queryAST: DocumentNode): number` | 
| 215 |  | - - returns: complexity of the query and exposes `maxDepth` property for depth limiting | 
|  | 206 | + - `schema: GraphQLSchema` | GraphQL schema object | 
|  | 207 | + - `typeWeightsConfig: TypeWeightConfig = defaultTypeWeightsConfig` | type weight configuration | 
|  | 208 | + - `enforceBoundedLists = false` | 
|  | 209 | + - returns: `TypeWeightObject` | 
|  | 210 | + - usage: | 
| 216 | 211 | 
 | 
| 217 |  | -### Rate-limiting | 
|  | 212 | + ```ts | 
|  | 213 | + import { typeWeightsFromSchema } from 'graphql-limiter'; | 
|  | 214 | + import { GraphQLSchema } from 'graphql/type/schema'; | 
|  | 215 | + | 
|  | 216 | + let schema: GraphQLSchema = buildSchema(`...`); | 
|  | 217 | + | 
|  | 218 | + const typeWeights: TypeWeightObject = typeWeightsFromSchema(schema); | 
|  | 219 | + ``` | 
|  | 220 | + | 
|  | 221 | +2. #### `ComplexityAnalysis` | class to calculate the complexity of the query based on the type weights and variables | 
|  | 222 | + | 
|  | 223 | + - `typeWeights: TypeWeightObject` | 
|  | 224 | + - `variables: Variables` | variables on request | 
|  | 225 | + - returns a class with method: | 
|  | 226 | + | 
|  | 227 | + - `processQuery(queryAST: DocumentNode): number` | 
|  | 228 | + - returns: complexity of the query and exposes `maxDepth` property for depth limiting | 
| 218 | 229 | 
 | 
| 219 |  | -3. #### `rateLimiter` | returns a rate limiting implementation based on selections | 
| 220 |  | - - `rateLimiter: RateLimiterConfig` | see "configuration" -> rateLimiter | 
| 221 |  | - - `client: Redis` | an ioredis client | 
| 222 |  | - - `keyExpiry: number` | time (ms) for key to persist in cache | 
| 223 |  | - - returns a class with method: | 
| 224 |  | - - `processRequest(uuid: string, timestamp: number, tokens = 1): Promise<RateLimiterResponse>` | 
| 225 |  | - - returns: `{ success: boolean, tokens: number, retryAfter?: number }` | where tokens is tokens available, retryAfter is time to wait in seconds before the request would be successful and success is false if the request is blocked | 
|  | 230 | + ```ts | 
|  | 231 | + import { typeWeightsFromSchema } from 'graphql-limiter'; | 
|  | 232 | + import { parse } from 'graphql'; | 
|  | 233 | + | 
|  | 234 | + let query: DocumentNode = parse(`...`); | 
|  | 235 | + | 
|  | 236 | + const queryParser: ASTParser = new ComplexityAnalysis(typeWeights, variables); | 
|  | 237 | + | 
|  | 238 | + const complexity: number = queryParser.parse(query); | 
|  | 239 | + ``` | 
|  | 240 | + | 
|  | 241 | +### Rate-limiting | 
| 226 | 242 | 
 | 
|  | 243 | +3. #### `rateLimiter` | returns a rate limiting class instance based on selections | 
|  | 244 | + | 
|  | 245 | + - `rateLimiter: RateLimiterConfig` | see "configuration" -> rateLimiter | 
|  | 246 | + - `client: Redis` | an ioredis client | 
|  | 247 | + - `keyExpiry: number` | time (ms) for key to persist in cache | 
|  | 248 | + - returns a class with method: | 
|  | 249 | + | 
|  | 250 | + - `processRequest(uuid: string, timestamp: number, tokens = 1): Promise<RateLimiterResponse>` | 
|  | 251 | + - returns: `{ success: boolean, tokens: number, retryAfter?: number }` | where tokens is tokens available, retryAfter is time to wait in seconds before the request would be successful and success is false if the request is blocked | 
|  | 252 | + | 
|  | 253 | + ```ts | 
|  | 254 | + import { rateLimiter } from 'graphql-limiter'; | 
|  | 255 | + | 
|  | 256 | + const limiter: RateLimiter = rateLimiter( | 
|  | 257 | + { | 
|  | 258 | + type: 'TOKEN_BUCKET', | 
|  | 259 | + refillRate: 1, | 
|  | 260 | + capacity: 10, | 
|  | 261 | + }, | 
|  | 262 | + typeWeights, | 
|  | 263 | + true | 
|  | 264 | + ); | 
|  | 265 | + | 
|  | 266 | + const response: RateLimiterResponse = limiter.processRequest( | 
|  | 267 | + 'user-1', | 
|  | 268 | + new Date().valueOf(), | 
|  | 269 | + 5 | 
|  | 270 | + ); | 
|  | 271 | + | 
|  | 272 | + const complexity: number = queryParser.parse(query); | 
|  | 273 | + ``` | 
| 227 | 274 | 
 | 
| 228 | 275 | ## <a name="future-development"></a> Future Development | 
| 229 | 276 | 
 | 
|  | 
0 commit comments