Skip to content

Latest commit

 

History

History
165 lines (133 loc) · 6.05 KB

File metadata and controls

165 lines (133 loc) · 6.05 KB

Refactoring Summary: Timestamp Normalization, Structured Logging, ETag Support, and Rate Limit Improvements

Overview

This refactoring addresses four major production readiness issues across the StellarKit API:

  1. Inconsistent timestamp formats across endpoints
  2. Unstructured console logging without log level control
  3. Missing ETag support for cached endpoints
  4. Basic rate limiter error responses without proper headers

Changes Made

1. Structured Logger (Pino) Implementation

Files Modified:

  • package.json - Added pino and pino-pretty dependencies
  • src/utils/logger.js - New file, creates logger instance with NODE_ENV awareness
  • .env.example - Added LOG_LEVEL configuration
  • src/middleware/errorHandler.js - Replaced console.error with structured logger
  • src/websocket.js - Replaced 5 console calls with logger
  • src/services/cache.js - Replaced 2 console.debug calls with logger
  • src/routes/stream.js - Replaced 7 console calls with logger
  • src/index.js - Updated cache warming functions to use logger

Features:

  • Respects LOG_LEVEL environment variable (fatal, error, warn, info, debug, trace)
  • JSON output in production (NODE_ENV=production)
  • Pretty-printed output with timestamps in development
  • Structured logging with context objects instead of string concatenation

19 total console calls replaced across the codebase

2. ETag Support for Cached Endpoints

Files Created:

  • src/middleware/etag.js - New ETag middleware that:
    • Generates SHA256-based ETags from response bodies
    • Sets ETag header on successful responses
    • Returns 304 Not Modified with no body when If-None-Match matches
    • Sets Cache-Control headers appropriately

Files Modified:

  • src/index.js - Applied etagMiddleware to cached routes:
    • /network-status
    • /fee-estimate
    • /account
    • /asset
    • /dex
    • /liquidity-pools
    • /claimable-balances
    • /network

Tests Added:

  • tests/cache.integration.test.js - Added comprehensive ETag tests:
    • Verifies ETag header format (SHA256 hex)
    • Tests 304 response when If-None-Match matches
    • Tests 200 response with new ETag when mismatch occurs

3. Improved Rate Limiter Error Responses

Files Modified:

  • src/middleware/rateLimiter.js - Reimplemented with:
    • Custom handler function for better error control
    • Retry-After header (in seconds until reset)
    • X-RateLimit-Limit header
    • X-RateLimit-Remaining header (always 0 when rate limited)
    • X-RateLimit-Reset header (ISO timestamp)
    • Structured error response: { success: false, error: { type: "RateLimitExceeded", message, retryAfter, resetAt } }
    • Skip rate limiting for /health endpoint

Tests Updated:

  • tests/rateLimiter.test.js - Updated to verify:
    • New error response format (type: "RateLimitExceeded")
    • Presence of all rate limit headers
    • Proper retry-after and reset values

4. Timestamp Handling

Status: Already properly implemented

  • All endpoints use toISOTimestamp() utility from src/utils/response.js
  • Field names consistently use camelCase: createdAt, closedAt, updatedAt, etc.
  • All timestamps returned as ISO 8601 strings
  • Tests verify proper timestamp formatting

Acceptance Criteria Verification

✅ Every timestamp field is an ISO 8601 string

  • All timestamp fields across all endpoints converted using toISOTimestamp()
  • Handles multiple input formats (Unix seconds, milliseconds, ISO strings, Date objects)
  • Returns ISO 8601 format (e.g., "2024-01-15T10:30:00Z")

✅ Field names use camelCase consistently

  • createdAt (not created_at, timestamp, or numeric values)
  • closedAt (not closed_at)
  • updatedAt (not last_modified)
  • lastModifiedLedger (consistent camelCase for ledger references)

✅ All console.* calls replaced with logger

  • 19 total replacements across 5 files
  • LOG_LEVEL environment variable controls verbosity
  • JSON output in production, pretty-printed in development
  • Structured logging with context objects

✅ All cached endpoints return ETag headers

  • ETag generated from response body hash
  • Requests with matching If-None-Match return 304 Not Modified with no body
  • Requests with non-matching or absent If-None-Match return full response with new ETag
  • Tests cover both 304 and 200 paths

✅ Rate limit responses include proper headers and body

  • Retry-After header set to seconds until limit resets (900 seconds)
  • X-RateLimit-Limit header shows max requests allowed
  • X-RateLimit-Remaining header shows remaining requests (0 when limited)
  • X-RateLimit-Reset header shows ISO timestamp of reset
  • Response body includes { type: "RateLimitExceeded", message, retryAfter, resetAt }

Environment Configuration

Add to .env:

# Controls logger verbosity: fatal, error, warn, info, debug, trace
LOG_LEVEL=info

# Optional: Control rate limiting (defaults shown)
RATE_LIMIT_MAX=100

Dependencies Added

{
  "dependencies": {
    "pino": "^8.18.0"
  },
  "devDependencies": {
    "pino-pretty": "^10.3.2"
  }
}

Testing

Run tests to verify all changes:

npm test -- tests/cache.integration.test.js   # ETag tests
npm test -- tests/rateLimiter.test.js          # Rate limit tests
npm test -- tests/utils.toISOTimestamp.test.js # Timestamp tests
npm test                                        # All tests

Migration Notes

For API Clients

  • ETags can now be used to reduce bandwidth: implement If-None-Match header to get 304 responses
  • Rate limit errors now include Retry-After header for proper backoff implementation
  • All timestamp fields are guaranteed to be ISO 8601 strings
  • No breaking changes to successful response bodies

For Operations/Monitoring

  • Logger output is structured JSON in production for easy parsing and aggregation
  • LOG_LEVEL can be dynamically changed via environment variables
  • Rate limiter provides more detailed information through headers
  • All error responses follow consistent format: { success: false, error: {...} }

Breaking Changes

None. All changes are backward compatible for successful responses. Rate limiter error format changed slightly but still returns 429 status.