Skip to main content

Overview

CryptoPulse is built on a modern, scalable architecture designed to efficiently handle concurrent cryptocurrency price requests while minimizing external API calls through intelligent batching.

Tech Stack

The system leverages proven technologies optimized for high-performance API services:

NestJS + Fastify

Enterprise-grade Node.js framework with high-performance HTTP server

PostgreSQL + TypeORM

Reliable persistence layer for price history storage

Redis (ioredis)

Distributed coordination for batching and rate limiting

JWT Authentication

Secure token-based API access control

System Components

Application Layer

The application is initialized in src/main.ts using NestJS with Fastify adapter:
src/main.ts
const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
  { bufferLogs: true }
);

app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
    whitelist: true,
    forbidNonWhitelisted: true,
  }),
);

Authentication Layer

JWT-based authentication protects all price endpoints. The JwtAuthGuard validates bearer tokens on every protected request.

Service Layer

The PriceService orchestrates:
  • Request batching coordination via Redis
  • External API calls to CoinGecko
  • Price persistence to PostgreSQL
  • Result distribution to waiting clients

Data Layer

Stores historical price records with timestamps for trending and analysis.Schema: PriceRecord entity tracks coinId, vsCurrency, price, and fetchedAt

Request Flow

The following diagram illustrates how a price request flows through the system:

Flow Steps

1

Request arrives

Client sends GET /v1/price/:coinId with bearer token
2

Authentication

JwtAuthGuard validates the token and extracts user payload
3

Batch admission

Redis counter increments for this coinId; request joins in-memory waiters
4

Timer or threshold

Batch flushes when:
  • Counter reaches BATCH_THRESHOLD (default: 3), OR
  • Timer expires after BATCH_WINDOW_MS (default: 5000ms)
5

External fetch

Single CoinGecko API call retrieves the current price
6

Persistence

Price record saved to PostgreSQL for history queries
7

Result distribution

Redis pub/sub broadcasts result; all waiting requests receive the same response

Multi-Instance Architecture

CryptoPulse supports horizontal scaling through Redis-based coordination: Multi-instance deployment Key features:
  • Shared Redis ensures only ONE batch flush occurs per coin, even across instances
  • Rate limiting counters are synchronized
  • Each instance maintains its own in-memory waiters but coordinates via pub/sub
  • Load balancer (e.g., Nginx) distributes incoming requests

Configuration for Multi-Instance

# docker-compose.multi.yml
services:
  nginx:
    image: nginx:alpine
    ports:
      - "3000:80"
  
  api1:
    build: .
    environment:
      - REDIS_URL=redis://redis:6379
      - DATABASE_URL=postgresql://...
  
  api2:
    build: .
    environment:
      - REDIS_URL=redis://redis:6379
      - DATABASE_URL=postgresql://...

Error Handling

The architecture includes comprehensive error handling:
ScenarioResponse
Redis unavailable during batch admission503 Service Unavailable
Batch result timeout (> 8s)504 Gateway Timeout
CoinGecko API failure502 Bad Gateway
Rate limit exceeded429 Too Many Requests
Invalid JWT401 Unauthorized
All errors are logged with structured JSON for monitoring and debugging.

Performance Characteristics

With a threshold of 3 and 5-second window, the system reduces external API calls by ~66% under concurrent load.
  • First request in batch: 5s max (window timeout) + CoinGecko latency
  • Subsequent requests: Near-instant once batch flushes
  • Threshold-triggered flush: Minimal delay
Stateless API instances can scale horizontally. Bottlenecks are:
  • Redis throughput (typically 100k+ ops/sec)
  • PostgreSQL write capacity
  • CoinGecko API rate limits

Next Steps

Request Batching

Deep dive into the batching algorithm

Authentication

Learn how JWT auth secures endpoints

Build docs developers (and LLMs) love