This document describes three production-ready features added to the Stellar Goal Vault backend:
Implements request-level authentication using API keys for production deployments. Protects write operations and sensitive endpoints while allowing public read access to certain endpoints.
Set the API_KEYS environment variable with comma-separated valid API keys:
API_KEYS=key1,key2,key3Include the API key in the Authorization header using Bearer token format:
curl -H "Authorization: Bearer your-api-key" https://api.example.com/api/campaignsGET /api/health- Health checkGET /api/config- Client configurationGET /api/stats- Global statisticsGET /api/leaderboard- Top contributorsGET /api/open-issues- GitHub issues
POST /api/campaigns- Create campaignPOST /api/campaigns/:id/pledges- Add pledgePOST /api/campaigns/:id/pledges/reconcile- Reconcile on-chain pledgePOST /api/campaigns/:id/claim- Claim campaignPOST /api/campaigns/:id/refund- Refund contributorGET /api/campaigns/:id/pledges- List pledgesGET /api/campaigns/:id/contributors- Get contributorsGET /api/campaigns/:id/history- Get campaign history
{
"success": false,
"error": {
"code": "UNAUTHORIZED",
"message": "Missing or invalid Authorization header. Use format: Bearer <api-key>",
"requestId": "uuid"
}
}- File:
src/middleware/apiKeyAuth.ts - Middleware:
apiKeyAuthMiddleware - Only enabled in production (
NODE_ENV=production) - Development mode allows all requests if
API_KEYSis not set
Implements a distributed caching layer using Redis for production deployments. Caches GET request responses to reduce database load and improve API response times.
Set the REDIS_URL environment variable:
REDIS_URL=redis://localhost:6379
# or with authentication
REDIS_URL=redis://:password@host:port- Automatic Cache Management: GET requests are automatically cached with configurable TTL
- Cache Invalidation: Cache is automatically invalidated on write operations
- Graceful Degradation: API continues to work if Redis is unavailable
- Production-Only: Cache is only enabled in production (
NODE_ENV=production)
Default TTL: 300 seconds (5 minutes)
Customize TTL in src/middleware/cacheMiddleware.ts:
app.use(cacheMiddleware(600)); // 10 minutesResponses include cache status headers:
X-Cache: HIT- Response served from cacheX-Cache: MISS- Response generated fresh and cached
All GET endpoints are cached:
GET /api/campaigns- Campaign listGET /api/campaigns/:id- Campaign detailsGET /api/campaigns/:id/pledges- Campaign pledgesGET /api/campaigns/:id/contributors- Contributor summaryGET /api/campaigns/:id/history- Campaign historyGET /api/stats- Global statisticsGET /api/leaderboard- Top contributors
Cache is automatically cleared when:
- New campaign is created
- New pledge is added
- Campaign is claimed
- Contributor is refunded
- Files:
src/services/cache.ts- Redis client and cache operationssrc/middleware/cacheMiddleware.ts- Express middleware for caching
- Functions:
initRedisCache()- Initialize Redis connectiongetCacheValue(key)- Retrieve cached valuesetCacheValue(key, value, ttl)- Store value in cachedeleteCacheValue(key)- Remove cached valueclearCachePattern(pattern)- Clear cache by patternisCacheAvailable()- Check cache availability
- Cache failures are logged but don't affect API functionality
- If Redis is unavailable, API continues to work without caching
- Connection errors are automatically logged
Comprehensive test suite for detecting and validating behavior under concurrent pledge operations. Tests ensure data consistency and proper handling of race conditions.
src/services/campaignStore.concurrent.test.ts
Tests that multiple concurrent pledges from different contributors are all recorded correctly.
- 4 concurrent pledges of 250 each
- Expected: All pledges recorded, total = 1000Tests behavior when concurrent pledges exceed campaign target.
- 3 concurrent pledges of 300 each (total 900, target 500)
- Expected: All pledges recorded (no hard cap), total = 900Tests enforcement of per-contributor pledge limits under concurrent conditions.
- 2 concurrent pledges of 150 each from same contributor (limit 200)
- Expected: Both pledges recorded (race condition), total = 300
- Note: This demonstrates a known race conditionTests data consistency under heavy concurrent load.
- 20 concurrent pledges of 50 each
- Expected: All pledges recorded, total = 1000, no data corruptionTests interaction between claim and pledge operations.
- Concurrent claim and pledge on expired campaign
- Expected: Both operations succeed, campaign claimed, pledge recordedTests handling of duplicate pledges from same contributor.
- 3 concurrent identical pledges from same contributor
- Expected: All pledges recorded (no deduplication at this level)# Run all tests
npm test
# Run only concurrent tests
npm test -- campaignStore.concurrent.test.ts
# Run with coverage
npm test -- --coverageThe tests document the following race conditions:
-
Per-Contributor Limit Race Condition
- When multiple pledges from the same contributor are submitted concurrently, the limit check may not see previous pledges
- Result: Contributor can exceed their limit
- Mitigation: Implement database-level constraints or use transactions
-
Campaign Funding Cap Race Condition
- When pledges are submitted concurrently, the total can exceed the target
- Result: Campaign can be over-funded
- Mitigation: Implement atomic operations or use database locks
- Uses Vitest for testing
- Isolated SQLite database per test
- Async/await for concurrent operations
- Promise.all() for parallel execution
- Comprehensive assertions on final state
- Database Transactions: Wrap pledge operations in transactions
- Optimistic Locking: Add version fields to campaigns
- Distributed Locks: Use Redis for cross-instance coordination
- Event Sourcing: Record all operations for audit trail
- Monitoring: Track pledge success/failure rates
NODE_ENV=production
API_KEYS=key1,key2,key3
REDIS_URL=redis://localhost:6379# Cache TTL in seconds (default: 300)
CACHE_TTL=600
# Redis connection timeout
REDIS_TIMEOUT=5000
# Log level
LOG_LEVEL=info- Set
NODE_ENV=production - Generate and configure
API_KEYS - Set up Redis instance and configure
REDIS_URL - Run concurrent tests to verify behavior
- Monitor cache hit rates and Redis performance
- Set up alerts for authentication failures
- Configure log aggregation for cache errors
- Test API key rotation procedure
- Document API key management process
- Hit Rate: Monitor X-Cache headers to track hit rate
- TTL Tuning: Adjust TTL based on data freshness requirements
- Memory: Monitor Redis memory usage
- Eviction: Configure Redis eviction policy (e.g., allkeys-lru)
- Overhead: API key validation adds minimal overhead (~1ms)
- Scaling: Stateless design allows horizontal scaling
- Key Rotation: No downtime required for key rotation
- Database: SQLite WAL mode supports concurrent reads
- Writes: Concurrent writes may cause contention
- Scaling: Consider PostgreSQL for higher concurrency
- Check
REDIS_URLis set and Redis is running - Check
NODE_ENV=production - Review logs for Redis connection errors
- Verify Redis credentials and network access
- Verify API key is in
API_KEYSenvironment variable - Check Authorization header format:
Bearer <key> - Ensure
NODE_ENV=productionfor authentication to be active - Review logs for authentication attempts
- Review concurrent test results
- Monitor database lock contention
- Consider implementing optimistic locking
- Use database transactions for critical operations