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.
- 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
npm install lightrate-expressOr with yarn:
yarn add lightrate-expressconst 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);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
}
});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.'
});
}
});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);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);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);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
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
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.
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
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 the global client configuration. Useful for testing.
Get the current global configuration.
Returns: Configuration object or null if not configured
interface LightrateConfigureOptions {
apiKey: string;
applicationId: string;
clientOptions?: ClientOptions;
}
interface LightrateMiddlewareOptions {
getUserIdentifier?: (req: Request) => string | undefined;
onRateLimitExceeded?: (req: Request, res: Response) => void;
}lightrateMiddleware({
getUserIdentifier: (req) => req.user?.id
})lightrateMiddleware({
getUserIdentifier: (req) => req.headers['x-api-key']
})lightrateMiddleware({
getUserIdentifier: (req) => req.ip
})lightrateMiddleware({
getUserIdentifier: (req) => {
// Combine multiple identifiers
return `${req.user?.id}:${req.user?.organizationId}`;
}
})lightrateMiddleware({
getUserIdentifier: (req) => {
// Skip rate limiting for admin users
if (req.user?.role === 'admin') {
return undefined; // No rate limiting
}
return req.user?.id;
}
})npm installnpm run buildThis creates:
dist/index.js- CommonJS builddist/index.esm.js- ES Module builddist/index.d.ts- TypeScript definitions
# 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.jsBug reports and pull requests are welcome on GitHub at https://github.com/lightbourne-technologies/lightrate-client-express.
The package is available as open source under the terms of the MIT License.
- lightrate-client-javascript - Core JavaScript client
- lightrate-rails - Rails integration