https://github.com/copyleftdev/api-metering-libary

https://github.com/copyleftdev/api-metering-libary

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (6.7%) to scientific vocabulary
Last synced: 10 months ago · JSON representation

Repository

Basic Info
  • Host: GitHub
  • Owner: copyleftdev
  • License: mit
  • Language: TypeScript
  • Default Branch: main
  • Size: 0 Bytes
Statistics
  • Stars: 0
  • Watchers: 1
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created about 1 year ago · Last pushed about 1 year ago
Metadata Files
Readme Contributing License Codeowners

README.md

API Metering Library for Stripe

A TypeScript library for metering API calls with Stripe's usage-based billing capabilities. This library provides a reusable, framework-agnostic solution for tracking API usage and reporting it to Stripe for billing purposes.

Architecture

```mermaid classDiagram %% Interfaces class IMeteringStrategy { <> +recordUsage(customerId: string, usageValue: number, context?: any) Promise~void~ +dispose() Promise~void~ }

%% Main Classes
class MeteringService {
    -strategy: IMeteringStrategy
    +recordApiCall(customerId: string, usageValue?: number, apiEndpoint?: string) Promise~void~
    +dispose() Promise~void~
}

class MeteringServiceFactory {
    <<static>>
    +createService(config: MeteringServiceConfig) MeteringService
}

%% Strategies
class BatchedUsageRecordStrategy {
    -stripeClient: Stripe
    -batches: Map~string, UsageRecord[]~
    -intervalId: NodeJS.Timeout
    +recordUsage(customerId: string, usageValue: number) Promise~void~
    +dispose() Promise~void~
    -flushAllBatches() Promise~void~
    -flushBatch(batch: UsageRecord[]) Promise~void~
}

class ImmediateMeterEventStrategy {
    -stripeClient: Stripe
    -meterEventName: string
    -idempotencyKeyPrefix: string
    +recordUsage(customerId: string, usageValue: number, context?: any) Promise~void~
    +dispose() Promise~void~
}

%% Error Classes
class MeteringError {
    +name: string
    +message: string
}

class ConfigurationError {
    +name: string
    +message: string
}

class StripeApiError {
    +name: string
    +message: string
    +stripeCode: string
}

%% Relationships
MeteringService --> IMeteringStrategy : uses
MeteringServiceFactory ..> MeteringService : creates
BatchedUsageRecordStrategy ..|> IMeteringStrategy : implements
ImmediateMeterEventStrategy ..|> IMeteringStrategy : implements
MeteringError <|-- ConfigurationError : extends
MeteringError <|-- StripeApiError : extends

```

Features

  • Framework-agnostic: Works with any JavaScript/TypeScript backend
  • Multiple reporting strategies:
    • Immediate: Report each API call to Stripe immediately as a meter event
    • Batched: Accumulate API calls and report them in batches as usage records
  • Type-safe with comprehensive TypeScript definitions
  • Built using best practices for code reusability and maintainability
  • Defensive programming techniques to handle errors gracefully

Installation

bash npm install api-metering-library stripe

Quick Start

```typescript import { MeteringServiceFactory } from 'api-metering-library';

// Create a metering service with the immediate strategy const meteringService = MeteringServiceFactory.createService({ stripeApiKey: 'sktestyourstripekey', strategyType: 'immediate', meterEventName: 'api_call' });

// Use the service to record API calls async function handleApiRequest(req, res) { const customerId = req.headers['x-customer-id'];

try { // Record the API call await meteringService.recordApiCall(customerId);

// Process the API request...

res.send({ success: true });

} catch (error) { console.error('Error processing API request:', error); res.status(500).send({ error: 'An error occurred' }); } } ```

Usage Examples

Setting Up with Immediate Reporting Strategy

```typescript import { MeteringServiceFactory } from 'api-metering-library';

const meteringService = MeteringServiceFactory.createService({ stripeApiKey: process.env.STRIPESECRETKEY || '', strategyType: 'immediate', meterEventName: 'api_call', idempotencyKeyPrefix: 'my-api' });

// Record an API call with default usage value (1) await meteringService.recordApiCall('cus_customer123');

// Record an API call with custom usage value and endpoint information await meteringService.recordApiCall('cus_customer123', 5, '/api/data/query'); ```

Setting Up with Batched Reporting Strategy

```typescript import { MeteringServiceFactory } from 'api-metering-library';

const meteringService = MeteringServiceFactory.createService({ stripeApiKey: process.env.STRIPESECRETKEY || '', strategyType: 'batched', batchIntervalMs: 30000, // 30 seconds maxBatchSize: 50, flushOnDispose: true });

// Record multiple API calls await meteringService.recordApiCall('cuscustomer123'); await meteringService.recordApiCall('cuscustomer456', 3);

// When shutting down your application, dispose the service to ensure all pending usage is reported process.on('SIGTERM', async () => { await meteringService.dispose(); process.exit(0); }); ```

Express.js Middleware Example

```typescript import express from 'express'; import { MeteringServiceFactory } from 'api-metering-library';

const app = express(); const meteringService = MeteringServiceFactory.createService({ stripeApiKey: process.env.STRIPESECRETKEY || '', strategyType: 'immediate', meterEventName: 'api_call' });

// Middleware to meter all API calls app.use(async (req, res, next) => { const customerId = req.headers['x-customer-id'] as string;

if (customerId) { try { await meteringService.recordApiCall(customerId, 1, req.path); } catch (error) { console.error('Error recording API usage:', error); // Continue processing the request even if metering fails } }

next(); });

app.get('/api/data', (req, res) => { res.json({ message: 'This API call was metered' }); });

app.listen(3000, () => { console.log('Server running on port 3000'); }); ```

API Reference

MeteringServiceFactory

The factory class used to create instances of the MeteringService.

createService(config: MeteringServiceConfig): MeteringService

Creates a new MeteringService instance with the specified configuration.

MeteringService

The main service class for recording API usage.

recordApiCall(customerId: string, usageValue?: number, apiEndpoint?: string): Promise<void>

Records an API call for the specified customer.

  • customerId: The ID of the customer making the API call
  • usageValue: The amount of usage to record (defaults to 1)
  • apiEndpoint: Optional endpoint information for context

dispose(): Promise<void>

Disposes of the service, cleaning up any resources and ensuring all pending usage is reported (for batched strategies).

Configuration Types

ImmediateMeterEventConfig

Configuration for the immediate meter event strategy:

  • stripeApiKey: Stripe API key (secret key) used for authentication
  • strategyType: Must be 'immediate'
  • meterEventName: The name of the meter event that will be reported to Stripe
  • idempotencyKeyPrefix (optional): Prefix for generating idempotency keys

BatchedUsageRecordConfig

Configuration for the batched usage record strategy:

  • stripeApiKey: Stripe API key (secret key) used for authentication
  • strategyType: Must be 'batched'
  • batchIntervalMs (optional): The interval in milliseconds at which batched usage records will be sent to Stripe (default: 60000)
  • flushOnDispose (optional): Whether to flush any pending usage records when the service is disposed (default: true)
  • maxBatchSize (optional): Maximum number of usage records to accumulate before forcing a flush (default: 100)

Error Handling

The library provides custom error types for better error handling:

  • MeteringError: Base error class for all errors from the library
  • ConfigurationError: Error related to invalid configuration
  • StripeApiError: Error communicating with the Stripe API
  • InvalidInputError: Error related to invalid input parameters
  • DependencyError: Error related to missing dependencies

Example of handling errors:

```typescript import { MeteringServiceFactory, StripeApiError } from 'api-metering-library';

try { await meteringService.recordApiCall(customerId); } catch (error) { if (error instanceof StripeApiError) { console.error(Stripe API error (${error.stripeCode}): ${error.message}); } else { console.error('Error recording API call:', error); } } ```

Requirements

  • Node.js >= 14
  • Stripe account with metered billing configured

License

MIT

Owner

  • Name: Donald Johnson
  • Login: copyleftdev
  • Kind: user
  • Location: Los Angeles

GitHub Events

Total
  • Watch event: 1
  • Push event: 4
  • Create event: 2
Last Year
  • Watch event: 1
  • Push event: 4
  • Create event: 2

Dependencies

.github/workflows/ci.yml actions
  • actions/checkout v3 composite
  • actions/setup-node v3 composite
  • codecov/codecov-action v3 composite
.github/workflows/release.yml actions
  • actions/checkout v3 composite
  • actions/setup-node v3 composite
  • softprops/action-gh-release v1 composite
package-lock.json npm
  • 395 dependencies
package.json npm
  • @types/jest ^29.5.3 development
  • @types/node ^20.5.0 development
  • @typescript-eslint/eslint-plugin ^6.4.0 development
  • @typescript-eslint/parser ^6.4.0 development
  • eslint ^8.47.0 development
  • eslint-config-prettier ^9.0.0 development
  • jest ^29.6.2 development
  • prettier ^3.0.2 development
  • ts-jest ^29.1.1 development
  • ts-node ^10.9.1 development
  • tslib ^2.6.1 development
  • typescript ^5.1.6 development
  • stripe ^14.0.0