Express
Express middleware for LightRate API rate limiting with local token buckets. This package provides seamless integration with the LightRate API, automatically throttling requests using local token buckets that refill from the server when needed.
Features
- Automatic Rate Limiting: Middleware automatically throttles requests based on path and user identifier
- Local Token Buckets: Efficient local token buckets with automatic server refills
- Flexible User Identification: Customize user identification per-route (user ID, API key, IP address, etc.)
- Singleton Client Architecture: Single global client shared across all middleware instances for consistent rate limiting
- Custom Error Handlers: Customize rate limit exceeded responses per-route
- TypeScript Support: Full TypeScript definitions included
- Graceful Error Handling: API errors don’t break your application
Installation
npm install lightrate-expressOr with yarn:
yarn add lightrate-expressQuick Start
const express = require('express');
const { configure, lightrateMiddleware } = require('lightrate-express');
const app = express();
// Step 1: Configure the global client (do this once at startup)
configure({
apiKey: process.env.LIGHTRATE_API_KEY,
applicationId: process.env.LIGHTRATE_APPLICATION_ID
});
// Step 2: Create and apply middleware
const rateLimiter = lightrateMiddleware({
getUserIdentifier: (req) => req.user?.id
});
app.use(rateLimiter);
// Your routes here
app.get('/api/data', (req, res) => {
res.json({ data: 'Your data' });
});
app.listen(3000);Configuration
Global Client Configuration
Before using the middleware, you must configure the global LightRate client with your API credentials:
const { configure } = require('lightrate-express');
configure({
// Required: Your LightRate API key
apiKey: process.env.LIGHTRATE_API_KEY,
// Required: Your LightRate Application ID
applicationId: process.env.LIGHTRATE_APPLICATION_ID,
// Optional: Client options
clientOptions: {
// Size for local token buckets (default: 5)
defaultLocalBucketSize: 10,
// API request timeout in seconds (default: 30)
timeout: 30,
// Number of retry attempts for API requests (default: 3)
retryAttempts: 3
}
});Middleware Options
Each middleware instance can be customized with the following options:
const rateLimiter = lightrateMiddleware({
// Optional: Function to extract user identifier from request
// Defaults to req.user?.id if not provided
getUserIdentifier: (req) => {
return req.user?.id; // or req.headers['x-api-key'], req.ip, etc.
},
// Optional: Custom handler for rate limit exceeded responses
// Defaults to JSON response with 429 status
onRateLimitExceeded: (req, res) => {
res.status(429).json({
error: 'Too Many Requests',
message: 'Rate limit exceeded. Please try again later.'
});
}
});Usage Examples
Basic Usage - Single Global Middleware
Apply rate limiting to all routes:
const express = require('express');
const { configure, lightrateMiddleware } = require('lightrate-express');
const app = express();
// Configure the client
configure({
apiKey: process.env.LIGHTRATE_API_KEY,
applicationId: process.env.LIGHTRATE_APPLICATION_ID,
clientOptions: {
defaultLocalBucketSize: 10
}
});
// Create middleware
const rateLimiter = lightrateMiddleware({
getUserIdentifier: (req) => req.user?.id
});
// Apply to all routes
app.use(rateLimiter);
app.get('/api/users', (req, res) => {
res.json({ users: [] });
});
app.listen(3000);Per-Route Customization
Create different middleware instances for different scenarios:
const express = require('express');
const { configure, lightrateMiddleware } = require('lightrate-express');
const app = express();
// Configure the global client once
configure({
apiKey: process.env.LIGHTRATE_API_KEY,
applicationId: process.env.LIGHTRATE_APPLICATION_ID,
clientOptions: {
defaultLocalBucketSize: 10
}
});
// User-authenticated routes (rate limit by user ID)
const userRateLimiter = lightrateMiddleware({
getUserIdentifier: (req) => req.user?.id,
onRateLimitExceeded: (req, res) => {
res.status(429).json({
error: 'Too Many Requests',
message: 'You have exceeded your rate limit. Please try again later.',
userId: req.user?.id
});
}
});
// API key routes (rate limit by API key)
const apiKeyRateLimiter = lightrateMiddleware({
getUserIdentifier: (req) => req.headers['x-api-key'],
onRateLimitExceeded: (req, res) => {
res.status(429).json({
error: 'API Rate Limit Exceeded',
message: 'Your API key has exceeded its rate limit.',
retryAfter: 60
});
}
});
// Public routes (rate limit by IP address)
const ipRateLimiter = lightrateMiddleware({
getUserIdentifier: (req) => req.ip,
onRateLimitExceeded: (req, res) => {
res.status(429).json({
error: 'Too Many Requests',
message: 'Please slow down your requests.'
});
}
});
// Apply different middleware to different routes
app.use('/api/user/*', userRateLimiter);
app.use('/api/v1/*', apiKeyRateLimiter);
app.use('/api/public/*', ipRateLimiter);
// Or apply to specific routes
app.post('/api/admin/reports', apiKeyRateLimiter, (req, res) => {
res.json({ report: 'data' });
});
app.get('/api/public/status', ipRateLimiter, (req, res) => {
res.json({ status: 'ok' });
});
app.get('/api/user/profile', userRateLimiter, (req, res) => {
res.json({ profile: req.user });
});
app.listen(3000);TypeScript Usage
import express, { Request, Response } from 'express';
import { configure, lightrateMiddleware, LightrateMiddlewareOptions } from 'lightrate-express';
const app = express();
// Configure the client
configure({
apiKey: process.env.LIGHTRATE_API_KEY!,
applicationId: process.env.LIGHTRATE_APPLICATION_ID!,
clientOptions: {
defaultLocalBucketSize: 10
}
});
// Create middleware with type safety
const middlewareOptions: LightrateMiddlewareOptions = {
getUserIdentifier: (req: Request): string | undefined => {
return req.user?.id;
},
onRateLimitExceeded: (req: Request, res: Response): void => {
res.status(429).json({
error: 'Rate limit exceeded'
});
}
};
const rateLimiter = lightrateMiddleware(middlewareOptions);
app.use(rateLimiter);
app.listen(3000);How It Works
Singleton Client Architecture
The middleware uses a singleton client pattern:
- Call
configure()once at application startup to create the global LightRate client - All middleware instances share the same client and token buckets
- Token buckets are keyed by
userIdentifier:path:method
This ensures:
- Consistent rate limiting: Same user hitting the same endpoint always uses the same bucket
- Efficiency: Single HTTP client and shared buckets across your application
- Memory efficient: No duplicate clients or buckets
Token Bucket Management
When a request comes in:
- Middleware extracts user identifier using
getUserIdentifier() - Attempts to consume from local token bucket
- If tokens available locally, consumes one and continues
- If bucket empty, fetches tokens from LightRate API and refills bucket
- If API returns no tokens, calls
onRateLimitExceeded()handler
Graceful Error Handling
The middleware gracefully handles errors:
- Configuration errors: Thrown immediately (must be fixed)
- API errors: Logged as warning, request continues (doesn’t break app)
- Network errors: Logged as warning, request continues
- Missing user identifier: Request continues without rate limiting
This ensures that rate limiting failures don’t cause application downtime.
API Reference
Functions
configure(options)
Configure the global LightRate client. Must be called before using middleware.
Parameters:
options.apiKey(string, required): Your LightRate API keyoptions.applicationId(string, required): Your LightRate Application IDoptions.clientOptions(object, optional): Client configuration optionsdefaultLocalBucketSize(number): Bucket size (default: 5)timeout(number): Request timeout in seconds (default: 30)retryAttempts(number): Number of retry attempts (default: 3)
Throws: Error if apiKey or applicationId is missing
lightrateMiddleware(options)
Create a LightRate middleware instance.
Parameters:
options.getUserIdentifier(function, optional): Extract user ID from requestoptions.onRateLimitExceeded(function, optional): Custom rate limit handler
Returns: Express middleware function
Throws: Error if global client not configured
reset()
Reset the global client configuration. Useful for testing.
getConfiguration()
Get the current global configuration.
Returns: Configuration object or null if not configured
TypeScript Types
interface LightrateConfigureOptions {
apiKey: string;
applicationId: string;
clientOptions?: ClientOptions;
}
interface LightrateMiddlewareOptions {
getUserIdentifier?: (req: Request) => string | undefined;
onRateLimitExceeded?: (req: Request, res: Response) => void;
}Common Patterns
Rate Limiting by User ID
lightrateMiddleware({
getUserIdentifier: (req) => req.user?.id
})Rate Limiting by API Key
lightrateMiddleware({
getUserIdentifier: (req) => req.headers['x-api-key']
})Rate Limiting by IP Address
lightrateMiddleware({
getUserIdentifier: (req) => req.ip
})Composite User Identifier
lightrateMiddleware({
getUserIdentifier: (req) => {
// Combine multiple identifiers
return `${req.user?.id}:${req.user?.organizationId}`;
}
})