lightrate-express
v1.0.0
Published
Express middleware for LightRate API rate limiting with local token buckets
Maintainers
Readme
LightRate Client 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: {
// Default 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() - Checks local token bucket for tokens
- 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): Default 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}`;
}
})Skip Rate Limiting for Certain Conditions
lightrateMiddleware({
getUserIdentifier: (req) => {
// Skip rate limiting for admin users
if (req.user?.role === 'admin') {
return undefined; // No rate limiting
}
return req.user?.id;
}
})Development
Setup
npm installBuild
npm run buildThis creates:
dist/index.js- CommonJS builddist/index.esm.js- ES Module builddist/index.d.ts- TypeScript definitions
Run Example
# Build the package first
npm run build
# Set environment variables
export LIGHTRATE_API_KEY=your_api_key
export LIGHTRATE_APPLICATION_ID=your_app_id
# Run the example
node examples/basic-usage.jsContributing
Bug reports and pull requests are welcome on GitHub at https://github.com/lightbourne-technologies/lightrate-client-express.
License
The package is available as open source under the terms of the MIT License.
Related Projects
- lightrate-client-javascript - Core JavaScript client
- lightrate-rails - Rails integration
