Basic IP rate-limiting middleware for Express stored in mongoDB. Use to throttle or limit incoming request rate.
Clone or download

README.md

Mongo Throttle

Basic IP rate-limiting middleware for Express stored in MongoDB. Use to throttle or limit incoming request rate.

Let MongoDB do the heavy lifting and synchronization of throttling/rate-limiting requestors. This Express middleware registers and maintains a rate limit for each requesting IP address. It uses MongoDB's built-in expiration index to automatically sweep out expired Throttle records.

This rate-limiting implementation will scale across multiple servers with the same implementation.

Usage

var throttler = require('mongo-throttle');

// In Express-style app:
app.use(throttle());

// Optionally only rate-limit requests that begin with /api/
app.use('/api/', throttle());

// Optionally configure limits:
app.use(throttle({ rateLimit: { ttl: 600, max: 5 } }));

// Optionally configure a custom limit handler:
app.use(throttle(function(req, res, hits, remaining) {
    var until = new Date((new Date()).getTime() + remaining);
    res.statusCode = 420;
    res.send('You shall not pass ' + hits + ' until ' + until + '!');
}));

API

Headers

# Header fields set:
X-Rate-Limit-Limit      # Maximum requests within TTL
X-Rate-Limit-Remaining  # Requests remaining for IP
X-Rate-Limit-Reset      # msec until limit reset for IP

Configuration

// Configuration options/defaults:
{
    "rateLimit": {
        "ttl": 600      // TTL window for each IP limit
        "max": 600      // Max hits for IP within window
    },
    "response": {
        "code":    429, // Response code when limit is reached
        "message": "Rate Limit reached. Please wait and try again."
    },
    "mongoose":{
        "uri": false    // Optional Mongo URI connection string
    },
    "useCustomHeader": "x-custom-header" // Count hits by the distinct values found in this header, instead of by IP
}

// When using a custom limit handler:
/**
 * Custom Limit-reached handler
 *
 * @param {object} req          Express request
 * @param {object} res          Express response
 * @param {number} hits         Total hits for this IP within TTL
 * @param {number} remaining    msec reminaing before this IP resets TTL window
 */
function custom_limit(req, res, hits, remaining) {
    // handler response here
}

Models

// Upon registration, this middleware will create
// a Mongoose model collection with schema:
Throttle = {
    createdAt:   Date,
    ip:          String,
    hits:        Number
}

Install

npm install mongo-throttle [--save]

Acknowledgments/Notes

Mongo has a useful feature called a TTL index.

TTL collections make it possible to store data in MongoDB and have the mongod automatically remove data after a specified number of seconds or at a specific clock time.

You can tell Mongo to remove data for you! We will use this to remove expired request counts from our rate-limiting check. There are a couple important things to note about this feature:

  • As an index, it is set upon collection creation. If you want to change it, you'll have to do so manually.
  • The index-specific field, expireAfterSeconds, is in seconds. Unlike most other timestamps in your JavaScript code, don't divide this by 1000.

See Also

License

MIT