UNPKG

@quell/server

Version:

Quell is an open-source NPM package providing a light-weight caching layer implementation and cache invalidation for GraphQL responses on both the client- and server-side. Use Quell to prevent redundant client-side API requests and to minimize costly serv

72 lines (71 loc) 4.52 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createRateLimiter = void 0; const cacheUtils_1 = require("../helpers/cacheUtils"); /** * A redis-based IP rate limiter middleware function that limits the number of requests per second based on IP address using Redis. * @param {Request} req - Express request object, including request body with GraphQL query string. * @param {Response} res - Express response object, will carry query response to next middleware. * @param {NextFunction} next - Express next middleware function, invoked when QuellCache completes its work. * @returns {void} Passes an error to Express if no query was included in the request or if the number of requests by the current IP * exceeds the IP rate limit. */ function createRateLimiter(config) { return (req, res, next) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; // Set ipRate to the ipRate limit from the request body or use default. const ipRateLimit = (_b = (_a = req.body.costOptions) === null || _a === void 0 ? void 0 : _a.ipRate) !== null && _b !== void 0 ? _b : config.ipRate; // Get the IP address from the request. const ipAddress = req.ip; // Get the current time in seconds. const currentTimeSeconds = Math.floor(Date.now() / 1000); // Create a Redis IP key using the IP address and current time. const redisIpTimeKey = `${ipAddress}:${currentTimeSeconds}`; // Return an error if no query is found on the request. if (!req.body.query) { const err = { log: "Error: no GraphQL query found on request body, inside rateLimiter", status: 400, message: { err: "Error in rateLimiter: Bad Request. Check server log for more details.", }, }; return next(err); } try { // Create a Redis multi command queue. const redisRunQueue = config.redisCache.multi(); // Add to queue: increment the key associated with the current IP address and time in Redis. redisRunQueue.incr(redisIpTimeKey); // Add to queue: set the key to expire after 1 second. redisRunQueue.expire(redisIpTimeKey, 1); // Execute the Redis multi command queue. const redisResponse = (yield redisRunQueue.exec()).map((result) => JSON.stringify(result)); // Save result of increment command, which will be the number of requests made by the current IP address in the last second. // Since the increment command was the first command in the queue, it will be the first result in the returned array. const numRequestsString = (_c = redisResponse[0]) !== null && _c !== void 0 ? _c : "0"; const numRequests = parseInt(numRequestsString, 10); // If the number of requests is greater than the IP rate limit, throw an error. if (numRequests > ipRateLimit) { next((0, cacheUtils_1.createServerError)(`Redis cache error: Express error handler caught too many requests from this IP address (${ipAddress}): limit is: ${ipRateLimit} requests per second, inside rateLimiter`, 429, "Error in rateLimiter middleware. Check server log for more details.")); return; } console.log(`IP ${ipAddress} made a request. Limit is: ${ipRateLimit} requests per second. Result: OK.`); return next(); } catch (error) { next((0, cacheUtils_1.createServerError)(`Catch block in rateLimiter middleware, ${error}`, 500, "IPRate Limiting Error. Check server log for more details.")); return; } }); } exports.createRateLimiter = createRateLimiter;