DEV Community

Emiliano Roberti
Emiliano Roberti

Posted on

Redis API Gateway Rate Limiter with Express.js

Redis API Gateway Rate Limiter with Express.js

This guide explains how to implement a rate limiter using Redis and Express.js, suitable for use in an API Gateway. Each step of the implementation is annotated and explained.

Overview

A rate limiter helps control how many requests a client (usually identified by IP address) can make to an API endpoint in a specific time window. This protects services from abuse and overload.

We’ll use Redis for its fast, atomic increment and TTL (time to live) features.

Why Redis?

Redis is an excellent choice for implementing rate limiting due to:

  • Speed: Redis operates in-memory, making it extremely fast and well-suited for real-time operations like request tracking.
  • Atomic operations: Commands like INCR and EXPIRE are atomic, which avoids race conditions when multiple requests hit the server concurrently.
  • TTL (Time to Live): Keys can automatically expire after a set time, making implementation of time windows straightforward.
  • Scalability: Redis can be clustered and horizontally scaled, supporting high-throughput environments.

Features

  • Per-IP rate limiting using Redis
  • Customisable limit and time window per endpoint
  • Express.js middleware integration

Project Structure

project/
├── rate-limiter.ts  # Main application and rate limiter class
└── serverEnvConfig.ts  # Contains Redis credentials and host config
Enter fullscreen mode Exit fullscreen mode

Setup Instructions

Install Dependencies

npm install express cors redis request-ip
Enter fullscreen mode Exit fullscreen mode

2. Configure Environment

Ensure you export or define these in serverEnvConfig.ts:

export const serverEnvConfig = {
  redis_user: 'default',
  redis_password: 'your_redis_password',
  redis_host: 'your_redis_host',
  redis_port: 6379,
};
Enter fullscreen mode Exit fullscreen mode

Code Explanation

1. Redis Client Setup

const redisClient = createClient({
  username: serverEnvConfig.redis_user,
  password: serverEnvConfig.redis_password,
  socket: {
    host: serverEnvConfig.redis_host,
    port: serverEnvConfig.redis_port,
  },
});
Enter fullscreen mode Exit fullscreen mode

Why? Initializes the Redis client with credentials from your config.


2. Rate Limiter Rule Interface

interface RateLimiterRule {
  endpoint: string;
  rate_limit: {
    time: number; // In seconds
    limit: number; // Max allowed requests
  };
}
Enter fullscreen mode Exit fullscreen mode

Why? Allows per-endpoint configuration of time window and limit.


3. RedisRateLimiterHandler Class

class RedisRateLimiterHandler {
  public async connect() {
    await redisClient.connect();
  }

  public rateLimiter(rule: RateLimiterRule) {
    return async (req: Request, res: Response, next: NextFunction) => {
      const ipAddress = req.ip;
      const redisId = `${rule.endpoint}/${ipAddress}`;

      const requests = await redisClient.incr(redisId);
      if (requests === 1) {
        await redisClient.expire(redisId, rule.rate_limit.time);
      }

      if (requests > rule.rate_limit.limit) {
        return res.status(429).json({
          success: false,
          message: 'ERROR:Too many requests',
        });
      }
      next();
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • incr(redisId): Atomically increments the count for a given IP.
  • expire(redisId, time): Sets the TTL for the key (starts a sliding window).
  • If the request count exceeds the limit, a 429 Too Many Requests response is returned.

4. Express Middleware Integration

const app = express();
app.set('trust proxy', true);
app.use(cors());
app.use(express.json());

const rateLimiter = new RedisRateLimiterHandler();
rateLimiter.connect();

const USER_RATE_LIMIT_RULES = {
  endpoint: 'rate-limiter',
  rate_limit: { time: 60, limit: 3 },
};

app.get('/users', rateLimiter.rateLimiter(USER_RATE_LIMIT_RULES), (req, res) => {
  res.status(200).json({ success: true });
});
Enter fullscreen mode Exit fullscreen mode

Note: trust proxy allows Express to correctly see the IP address when behind a proxy/load balancer.


Example

Send requests using curl or Postman:

curl http://localhost:3000/users
Enter fullscreen mode Exit fullscreen mode

After 3 requests within 60 seconds from the same IP, you’ll receive:

{
  "success": false,
  "message": "ERROR:Too many requests"
}
Enter fullscreen mode Exit fullscreen mode

Redis Keys

Redis keys are structured as:

<endpoint>/<ip-address>
Enter fullscreen mode Exit fullscreen mode

Example:

rate-limiter/::ffff:192.168.1.1
Enter fullscreen mode Exit fullscreen mode

Each key:

  • Is incremented on each request
  • Is automatically removed after 60s via TTL

Note on IP Format: The IP addresses logged and used as keys are compatible with both IPv4 and IPv6. For example, ::ffff:192.168.1.1 is an IPv6-mapped IPv4 address. This ensures the rate limiter is compatible with modern mobile and IPv6-only networks.


Pros

  • Fast and scalable due to Redis performance
  • Easy to extend to per-user or per-token rate limiting
  • Minimal external dependencies
  • Fully IPv6 compatible (critical for mobile devices and modern networks)

Security Notes

  • Don’t expose Redis to the public internet.
  • Use Redis AUTH + TLS where supported.
  • Always validate client IPs carefully if behind proxies.

Development Considerations

  • Use Redis Lua scripts for atomic increment+expire in one step.
  • Support burst tokens or leaky bucket algorithms.
  • Track per-user tokens or API keys instead of just IP.

References

Author

Emiliano Roberti

Top comments (1)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.