API Design

📖 5 min read 📄 Part 5 of 10

Distributed Counter - API Design

REST API

Increment Counter

POST /api/v1/counters/{counter_id}/increment
Content-Type: application/json

{
  "delta": 1,
  "metadata": {
    "user_id": "user_123",
    "source": "web"
  }
}

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "value": 1234568,
  "delta": 1,
  "timestamp": "2024-01-08T10:00:00Z"
}

Response 429 Too Many Requests:
{
  "error": "rate_limit_exceeded",
  "message": "Too many requests",
  "retry_after": 60
}

Decrement Counter

POST /api/v1/counters/{counter_id}/decrement
Content-Type: application/json

{
  "delta": 1
}

Response 200 OK:
{
  "counter_id": "likes:post_456",
  "value": 999,
  "delta": -1,
  "timestamp": "2024-01-08T10:00:00Z"
}

Get Counter Value

GET /api/v1/counters/{counter_id}

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "value": 1234567,
  "type": "simple",
  "created_at": "2024-01-01T00:00:00Z",
  "updated_at": "2024-01-08T10:00:00Z"
}

# Get with metadata
GET /api/v1/counters/{counter_id}?include_metadata=true

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "value": 1234567,
  "type": "sharded",
  "shards": 4,
  "config": {
    "num_shards": 4,
    "sync_interval": 1000
  },
  "metadata": {
    "created_at": "2024-01-01T00:00:00Z",
    "updated_at": "2024-01-08T10:00:00Z",
    "last_sync_at": "2024-01-08T09:59:59Z"
  }
}

Batch Operations

POST /api/v1/counters/batch/increment
Content-Type: application/json

{
  "operations": [
    {"counter_id": "page_views:article_123", "delta": 1},
    {"counter_id": "page_views:article_456", "delta": 1},
    {"counter_id": "page_views:article_789", "delta": 1}
  ]
}

Response 200 OK:
{
  "results": [
    {"counter_id": "page_views:article_123", "value": 1234568, "status": "success"},
    {"counter_id": "page_views:article_456", "value": 567891, "status": "success"},
    {"counter_id": "page_views:article_789", "value": 234567, "status": "success"}
  ],
  "total_operations": 3,
  "successful": 3,
  "failed": 0
}

Reset Counter

POST /api/v1/counters/{counter_id}/reset
Content-Type: application/json

{
  "value": 0  # Optional, defaults to 0
}

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "old_value": 1234567,
  "new_value": 0,
  "timestamp": "2024-01-08T10:00:00Z"
}

Create Counter

POST /api/v1/counters
Content-Type: application/json

{
  "counter_id": "page_views:article_123",
  "type": "sharded",
  "initial_value": 0,
  "config": {
    "num_shards": 4,
    "sync_interval": 1000
  }
}

Response 201 Created:
{
  "counter_id": "page_views:article_123",
  "type": "sharded",
  "value": 0,
  "created_at": "2024-01-08T10:00:00Z"
}

Delete Counter

DELETE /api/v1/counters/{counter_id}

Response 204 No Content

Time-Windowed Counter API

Increment with Time Window

POST /api/v1/counters/{counter_id}/increment/windowed
Content-Type: application/json

{
  "delta": 1,
  "window_size": 3600  # 1 hour in seconds
}

Response 200 OK:
{
  "counter_id": "api_requests:user_123",
  "current_window_value": 150,
  "window_size": 3600,
  "window_start": "2024-01-08T10:00:00Z",
  "window_end": "2024-01-08T11:00:00Z"
}

Get Time-Windowed Count

GET /api/v1/counters/{counter_id}/windowed?window_size=3600

Response 200 OK:
{
  "counter_id": "api_requests:user_123",
  "value": 150,
  "window_size": 3600,
  "window_start": "2024-01-08T10:00:00Z",
  "window_end": "2024-01-08T11:00:00Z",
  "buckets": [
    {"timestamp": "2024-01-08T10:00:00Z", "count": 50},
    {"timestamp": "2024-01-08T10:30:00Z", "count": 100}
  ]
}

Aggregation API

Get Multiple Counters

POST /api/v1/counters/query
Content-Type: application/json

{
  "counter_ids": [
    "page_views:article_123",
    "page_views:article_456",
    "page_views:article_789"
  ]
}

Response 200 OK:
{
  "counters": [
    {"counter_id": "page_views:article_123", "value": 1234567},
    {"counter_id": "page_views:article_456", "value": 567891},
    {"counter_id": "page_views:article_789", "value": 234567}
  ],
  "total": 2036025
}

Pattern-Based Query

GET /api/v1/counters?pattern=page_views:article_*&limit=10

Response 200 OK:
{
  "counters": [
    {"counter_id": "page_views:article_123", "value": 1234567},
    {"counter_id": "page_views:article_456", "value": 567891}
  ],
  "total_matched": 2,
  "limit": 10
}

Top-K Counters

GET /api/v1/counters/top?k=10&pattern=page_views:*

Response 200 OK:
{
  "top_counters": [
    {"counter_id": "page_views:article_123", "value": 1234567, "rank": 1},
    {"counter_id": "page_views:article_456", "value": 567891, "rank": 2},
    {"counter_id": "page_views:article_789", "value": 234567, "rank": 3}
  ],
  "k": 10,
  "total_counters": 1000
}

Unique Counter API (HyperLogLog)

Add to Unique Counter

POST /api/v1/counters/{counter_id}/unique/add
Content-Type: application/json

{
  "items": ["user_123", "user_456", "user_789"]
}

Response 200 OK:
{
  "counter_id": "unique_visitors:article_123",
  "estimated_count": 1523,
  "items_added": 3
}

Get Unique Count

GET /api/v1/counters/{counter_id}/unique

Response 200 OK:
{
  "counter_id": "unique_visitors:article_123",
  "estimated_count": 1523,
  "error_rate": 0.0081,  # 0.81%
  "type": "hyperloglog"
}

Monitoring API

Get Counter Statistics

GET /api/v1/counters/{counter_id}/stats

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "value": 1234567,
  "stats": {
    "increment_rate": 100,  # ops/second
    "read_rate": 50,  # ops/second
    "last_increment": "2024-01-08T10:00:00Z",
    "cache_hit_rate": 0.95,
    "sync_lag": 0.5  # seconds
  }
}

Get System Metrics

GET /api/v1/metrics

Response 200 OK:
{
  "total_counters": 100000000,
  "active_counters": 1000000,
  "increment_rate": 10000000,  # ops/second
  "read_rate": 1000000,  # ops/second
  "cache_hit_rate": 0.95,
  "average_latency_ms": 0.8,
  "p99_latency_ms": 2.5
}

Client SDKs

Python SDK

from counter_client import CounterClient

# Initialize client
client = CounterClient(
    base_url="https://api.example.com",
    api_key="your_api_key"
)

# Increment counter
result = client.increment("page_views:article_123", delta=1)
print(f"New value: {result.value}")

# Get counter value
value = client.get("page_views:article_123")
print(f"Current value: {value}")

# Batch increment
results = client.batch_increment([
    ("page_views:article_123", 1),
    ("page_views:article_456", 1),
    ("page_views:article_789", 1)
])

# Time-windowed counter
result = client.increment_windowed(
    "api_requests:user_123",
    delta=1,
    window_size=3600
)
print(f"Requests in last hour: {result.current_window_value}")

# Unique counter
client.add_unique("unique_visitors:article_123", ["user_123", "user_456"])
count = client.get_unique("unique_visitors:article_123")
print(f"Unique visitors: {count}")

JavaScript SDK

import { CounterClient } from 'counter-client';

// Initialize client
const client = new CounterClient({
  baseUrl: 'https://api.example.com',
  apiKey: 'your_api_key'
});

// Increment counter
const result = await client.increment('page_views:article_123', 1);
console.log(`New value: ${result.value}`);

// Get counter value
const value = await client.get('page_views:article_123');
console.log(`Current value: ${value}`);

// Batch operations
const results = await client.batchIncrement([
  { counterId: 'page_views:article_123', delta: 1 },
  { counterId: 'page_views:article_456', delta: 1 }
]);

// Time-windowed counter
const windowedResult = await client.incrementWindowed(
  'api_requests:user_123',
  1,
  3600
);
console.log(`Requests in last hour: ${windowedResult.currentWindowValue}`);

// Subscribe to counter updates (WebSocket)
client.subscribe('page_views:article_123', (update) => {
  console.log(`Counter updated: ${update.value}`);
});

Java SDK

import com.example.CounterClient;

// Initialize client
CounterClient client = new CounterClient.Builder()
    .baseUrl("https://api.example.com")
    .apiKey("your_api_key")
    .build();

// Increment counter
CounterResult result = client.increment("page_views:article_123", 1);
System.out.println("New value: " + result.getValue());

// Get counter value
long value = client.get("page_views:article_123");
System.out.println("Current value: " + value);

// Batch operations
List<CounterOperation> operations = Arrays.asList(
    new CounterOperation("page_views:article_123", 1),
    new CounterOperation("page_views:article_456", 1)
);
List<CounterResult> results = client.batchIncrement(operations);

// Time-windowed counter
WindowedCounterResult windowedResult = client.incrementWindowed(
    "api_requests:user_123",
    1,
    3600
);
System.out.println("Requests in last hour: " + windowedResult.getCurrentWindowValue());

Configuration API

Update Counter Configuration

PATCH /api/v1/counters/{counter_id}/config
Content-Type: application/json

{
  "num_shards": 8,
  "sync_interval": 500
}

Response 200 OK:
{
  "counter_id": "page_views:article_123",
  "config": {
    "num_shards": 8,
    "sync_interval": 500
  },
  "updated_at": "2024-01-08T10:00:00Z"
}

This comprehensive API design provides flexible, efficient, and developer-friendly interfaces for distributed counting operations.