Skip to content

lightbourne-technologies/lightrate-client-express

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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-express

Or with yarn:

yarn add lightrate-express

Quick 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:

  1. Call configure() once at application startup to create the global LightRate client
  2. All middleware instances share the same client and token buckets
  3. 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:

  1. Middleware extracts user identifier using getUserIdentifier()
  2. Checks local token bucket for tokens
  3. If tokens available locally, consumes one and continues
  4. If bucket empty, fetches tokens from LightRate API and refills bucket
  5. 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 key
  • options.applicationId (string, required): Your LightRate Application ID
  • options.clientOptions (object, optional): Client configuration options
    • defaultLocalBucketSize (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 request
  • options.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 install

Build

npm run build

This creates:

  • dist/index.js - CommonJS build
  • dist/index.esm.js - ES Module build
  • dist/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.js

Contributing

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published