How We Built LoL Wrapped

A deep dive into the serverless architecture powering your year in League of Legends

By Alejandro Figueroa October 2025

The Challenge

We wanted to create a Spotify Wrapped-style experience for League of Legends players - a beautiful, personalized summary of their gaming year. But there was a catch: we needed to process potentially hundreds of matches, analyze complex statistics, and deliver results in under 2 minutes.

The solution? A modern serverless architecture combining AWS services, AI-powered insights, and the Riot Games API.

System Architecture

Here's the complete infrastructure that powers LoL Wrapped:

LoL Wrapped Architecture Diagram

The full serverless stack: Frontend, GraphQL API, Database, Compute, and AI

Key Components

🎨 AWS Amplify

Hosts our Astro-based frontend with global CDN distribution. Every code push triggers an automatic build and deployment - zero-downtime updates in minutes.

Astro React TypeScript Tailwind

⚡ AWS AppSync

Our GraphQL API layer. Handles all client requests, manages real-time subscriptions for live updates, and orchestrates between DynamoDB and Lambda.

GraphQL VTL Resolvers API Key Auth

💾 Amazon DynamoDB

NoSQL database storing wrapped data with millisecond latency. On-demand scaling means we only pay for what we use - perfect for bursty workloads.

NoSQL DynamoDB Streams On-Demand

🔥 AWS Lambda

Serverless Python functions that fetch data from Riot API, process hundreds of matches, calculate statistics, and generate insights. Auto-scales from zero to thousands of concurrent executions.

Python 3.11 Boto3 Serverless

🚀 AWS App Runner

Runs our MCP (Model Context Protocol) server - a persistent HTTP service that exposes Riot API tools to AI agents. Container-based deployment with automatic scaling and health checks.

FastMCP Docker SSE

🤖 Amazon Bedrock

Powers AI-generated insights using Claude 4.5 Sonnet. Analyzes your playstyle, generates personalized commentary, and creates natural language summaries of your gaming year.

Claude 4.5 Pydantic AI MCP

The Data Flow

When you request your Wrapped, here's what happens behind the scenes:

  1. Request Initiation: Your browser sends a GraphQL mutation to AppSync with your summoner name and region.
  2. Status Tracking: AppSync creates a record in DynamoDB with status "PROCESSING" and returns immediately.
  3. Lambda Invocation: AppSync triggers a Lambda function asynchronously to start data collection.
  4. Riot API Calls: Lambda fetches your PUUID, summoner info, match history (up to 100 games), and champion masteries from Riot's API.
  5. Analytics Processing: Our analytics engine calculates 200+ statistics including KDA, winrates, temporal patterns, and achievement tracking.
  6. AI Enhancement: Bedrock's Claude analyzes the data to generate personalized insights and natural language summaries.
  7. Asset Enrichment: Champion splash arts and icons are fetched from Data Dragon CDN.
  8. Final Update: Lambda writes the complete wrapped data back to DynamoDB with status "COMPLETED".
  9. Client Display: Frontend polls AppSync every 3 seconds, detects completion, and renders your beautiful wrapped visualization.

MCP: The Secret Sauce

One of the most innovative parts of our architecture is the Model Context Protocol (MCP) server. This standardized interface lets AI models interact with external data sources in a structured way.

Our MCP server exposes Riot API functionality as "tools" that Claude can use. Instead of hardcoding API calls, we let the AI agent decide when and how to fetch data - making the system more flexible and intelligent.

# Example MCP tool
@mcp.tool()
async def get_player_wrapped(
    game_name: str,
    tag_line: str,
    region: str,
    max_matches: int = 100
) -> str:
    """
    Generate a complete Wrapped for a player.
    Returns JSON with stats, rankings, and insights.
    """
    # Fetch and analyze data...
    return wrapped_json

Performance & Scalability

  • Sub-2-minute processing: From request to completed wrapped in ~90 seconds
  • Rate limit handling: Smart throttling to respect Riot's 20 req/sec limit
  • Cost-efficient: Serverless = $0 when idle, scales only when needed
  • Global CDN: Frontend loads in <1s from anywhere in the world
  • Automatic scaling: Handles 1 user or 10,000 users without code changes

Complete Tech Stack

Frontend

Astro, React, TypeScript, Tailwind CSS, AWS Amplify

Backend

AWS Lambda (Python), AppSync (GraphQL), DynamoDB, App Runner

AI & Analytics

Amazon Bedrock (Claude 4.5), Pydantic AI, FastMCP, NumPy

Infrastructure

Terraform, CloudFormation, Docker, GitHub Actions

External APIs

Riot Games API, Data Dragon CDN

Challenges We Solved

⚡ Rate Limiting

Riot's API has strict rate limits (20 req/s, 100 req/2min). We implemented intelligent request batching and exponential backoff to maximize throughput while staying compliant.

🔄 Real-time Updates

Users don't want to wait staring at a loading screen. We use polling with GraphQL subscriptions ready for upgrade, showing progress updates as matches are processed.

💰 Cost Optimization

By using serverless architecture and on-demand DynamoDB, we keep costs near zero during development and scale gracefully under load. Estimated cost: $0-10/month for moderate usage.

What's Next?

  • WebSocket subscriptions for true real-time updates
  • Social sharing with custom OG images
  • Multi-year comparison (compare 2024 vs 2025)
  • Team wrapped (aggregate stats for friend groups)
  • More AI-powered insights and predictions

Try It Yourself

Ready to see your year in League of Legends? Get your personalized Wrapped now.