Document CSGOWTFD backend API and update domain modeling plans

Reflect backend audit findings in TODO.md:
- 12 REST endpoints documented for player, match, matches, and sitemap
- Data models aligned with backend schemas (Match, Player, MatchPlayer,
  etc.)
- CS2 compatibility confirmed with Premier rating support (0-30000)

Add comprehensive API documentation covering:
- Endpoint specifications and response structures
- Integration guide with TypeScript examples
- Error handling and caching strategies
- CS2 migration notes for rank system and MR12 changes
This commit is contained in:
2025-11-04 19:32:08 +01:00
parent 366bfbeb54
commit 0404188d4d
2 changed files with 1035 additions and 17 deletions

54
TODO.md
View File

@@ -13,9 +13,23 @@
- `/match/[id]/flashes` - Flashbang effectiveness data
- `/match/[id]/damage` - Damage visualization and heatmaps
- `/match/[id]/chat` - In-game chat log
- [x] Audit the `csgowtfd` backend APIs and data models:
- Backend: Go + Gin framework + Ent ORM + PostgreSQL
- API endpoints documented in `docs/API.md`
- 12 REST endpoints identified (player, match, matches listing, sitemap)
- Data models: Match, Player, MatchPlayer, RoundStats, Weapon, Message
- Swagger documentation available at `/api/swagger`
- **Decision**: Continue using existing REST API (no replacement needed)
- CS2 compatibility: Backend ready with rank field supporting 0-30000 Premier rating
- [x] Document CS2 demo parsing pipeline:
- Demo parsing handled by backend (6 concurrent workers)
- Share code submission via `/match/parse/:sharecode`
- Demos expire after 30 days (configurable)
- Parsing SLA: ~2-5 minutes per match (depends on queue)
- Storage: PostgreSQL for structured data, Redis for caching
- Data freshness: Player profiles update every 7 days, match check every 30 min
- [ ] Host workshops with stakeholders, shoutcasters, and data consumers to gather CS2-specific requirements (new stats, volumetric smokes, MR12 changes).
- [ ] Audit the `csgowtfd` backend APIs and data models; decide whether to extend or replace services for CS2 telemetry.
- [ ] Document CS2 demo parsing pipeline expectations, storage strategy, and data freshness SLAs.
- **Note**: Planned for implementation period - feedback will inform feature priorities
## Phase 1 Technical Foundations
- [ ] Standardize on Node.js ≥ 18 (add `.nvmrc` / `.tool-versions`) and lock package manager choice (npm or yarn).
@@ -79,27 +93,33 @@
- [ ] Run accessibility review on mockups (contrast, color blindness variants, keyboard flows).
## Phase 3 Domain Modeling & Data Layer
- [ ] Define core TypeScript interfaces in `src/lib/types/`:
- `Match.ts`: match metadata, teams, score, map, date, duration, game mode (Premier, Competitive, etc.)
- `Player.ts`: Steam ID, name, avatar, rank, stats summary, recent matches
- `Round.ts`: round number, winner, reason, duration, economy state, events
- `PlayerStats.ts`: kills, deaths, assists, ADR, HS%, KAST, rating, utility damage
- `Economy.ts`: money spent/saved, equipment value, loadouts, utility purchases
- `UtilityEvent.ts`: flashbangs (effectiveness, enemies blinded), smokes (volumetric coverage), HE grenades, molotovs
- `DamageEvent.ts`: attacker, victim, weapon, damage, hit group, distance, tick
- `ChatMessage.ts`: timestamp, round, player, message, team chat flag
- `Metadata.ts`: CS2 version, demo hash, server info, tickrate
**Reference**: See `docs/API.md` for complete backend API documentation, endpoints, and response structures.
- [ ] Define core TypeScript interfaces in `src/lib/types/` (based on backend schemas):
- `Match.ts`: match_id, share_code, map, date, scores, duration, match_result, max_rounds, demo_parsed, tick_rate
- `Player.ts`: id (Steam ID), name, avatar, vanity_url, wins/losses/ties, vac_count, game_ban_count, steam_updated
- `MatchPlayer.ts`: player stats per match (kills, deaths, assists, headshot, mvp, score, kast, rank changes, damage, utility stats, flash metrics)
- `RoundStats.ts`: round number, bank (money), equipment value, spent
- `Weapon.ts`: weapon statistics (victim, damage, eq_type, hit_group)
- `Message.ts`: chat messages (message text, all_chat flag, tick, player reference)
- Derived types: `MatchWithPlayers`, `PlayerProfile`, `MatchRoundsData`, `MatchChatData`
- **Important**: Backend uses `looses` (typo) - map to `losses` in frontend types
- [ ] Create Zod schemas for runtime validation:
- Match all TypeScript interfaces with Zod schemas for API response validation
- Add custom validators for Steam IDs, match IDs, rank tiers
- Export type-safe parsers: `parseMatch()`, `parsePlayerStats()`, etc.
- Add custom validators for Steam IDs (uint64), match IDs, rank tiers (0-30000 for CS2)
- Handle nullable fields properly (many optional fields in backend)
- Export type-safe parsers: `parseMatch()`, `parsePlayer()`, `parseMatchPlayer()`, etc.
- Add schema for error responses
- [ ] Design API client architecture:
- Evaluate backend API (likely REST from csgowtfd) and create typed client
- Build `src/lib/api/client.ts` with fetch wrapper
- Backend confirmed: REST API (Go + Gin) at `localhost:8000` (default)
- Build `src/lib/api/client.ts` with axios/fetch wrapper (see `docs/API.md` for example)
- Create endpoint modules: `players.ts`, `matches.ts`, `sitemap.ts`
- Implement error handling (network errors, API errors, validation errors)
- Add retry logic with exponential backoff for transient failures
- Add retry logic with exponential backoff for transient failures (500, 503)
- Implement request cancellation (AbortController) for navigation
- Add response caching strategy (in-memory cache, TTL-based invalidation)
- Handle CORS (backend supports configurable CORS domains)
- [ ] Set up MSW (Mock Service Worker) for testing:
- Install: `msw@^2.0.0`
- Create mock handlers in `src/mocks/handlers/` for all endpoints

998
docs/API.md Normal file
View File

@@ -0,0 +1,998 @@
# CSGOWTFD Backend API Documentation
**Version**: 1.0
**Base URL**: `http://localhost:8000` (default)
**Backend Repository**: https://somegit.dev/CSGOWTF/csgowtfd
**Framework**: Go + Gin + Ent ORM
**Database**: PostgreSQL
---
## Table of Contents
1. [Overview](#overview)
2. [API Endpoints](#api-endpoints)
- [Player Endpoints](#player-endpoints)
- [Match Endpoints](#match-endpoints)
- [Matches Listing](#matches-listing)
- [Sitemap](#sitemap)
3. [Data Models](#data-models)
4. [Integration Guide](#integration-guide)
5. [Error Handling](#error-handling)
---
## Overview
The CSGOWTFD backend is a REST API service that provides Counter-Strike match statistics, player profiles, and demo parsing capabilities. It uses:
- **Web Framework**: Gin (Go)
- **ORM**: Ent (Entity Framework for Go)
- **Database**: PostgreSQL with pgx driver
- **Cache**: Redis
- **Steam Integration**: Steam API + demo parsing
### Configuration
Default backend configuration:
```yaml
httpd:
listen: ":8000"
cors_allow_domains: ["*"]
database:
driver: "pgx"
connection: "postgres://username:password@localhost:5432/database_name"
redis:
addr: "localhost:6379"
```
### CORS
The backend supports CORS with configurable allowed domains. By default, all origins are allowed in development.
---
## API Endpoints
### Player Endpoints
#### 1. Get Player Profile
Retrieves comprehensive player statistics and match history.
**Endpoint**: `GET /player/:id`
**Alternative**: `GET /player/:id/next/:time`
**Parameters**:
- `id` (path, required): Steam ID (uint64)
- `time` (path, optional): Unix timestamp for pagination (get matches before this time)
**Response** (200 OK):
```json
{
"id": 76561198012345678,
"name": "PlayerName",
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/...",
"vanity_url": "custom-url",
"vanity_url_real": "custom-url",
"steam_updated": "2024-11-04T10:30:00Z",
"profile_created": "2015-03-12T00:00:00Z",
"wins": 1250,
"looses": 980,
"ties": 45,
"vac_count": 0,
"vac_date": null,
"game_ban_count": 0,
"game_ban_date": null,
"oldest_sharecode_seen": "CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"matches": [
{
"match_id": 3589487716842078322,
"map": "de_inferno",
"date": "2024-11-01T18:45:00Z",
"score_team_a": 13,
"score_team_b": 10,
"duration": 2456,
"match_result": 1,
"max_rounds": 24,
"demo_parsed": true,
"vac_present": false,
"gameban_present": false,
"tick_rate": 64.0,
"stats": {
"team_id": 2,
"kills": 24,
"deaths": 18,
"assists": 6,
"headshot": 12,
"mvp": 3,
"score": 56,
"kast": 78,
"rank_old": 18500,
"rank_new": 18650,
"dmg_enemy": 2450,
"dmg_team": 120,
"flash_assists": 4,
"flash_duration_enemy": 15.6,
"flash_total_enemy": 8,
"ud_he": 450,
"ud_flames": 230,
"ud_flash": 5,
"ud_smoke": 3,
"avg_ping": 25.5,
"color": "yellow"
}
}
]
}
```
**Use Case**: Display player profile page with career stats and recent matches.
---
#### 2. Get Player Metadata
Retrieves lightweight player metadata (recent matches summary).
**Endpoint**: `GET /player/:id/meta`
**Alternative**: `GET /player/:id/meta/:limit`
**Parameters**:
- `id` (path, required): Steam ID
- `limit` (path, optional): Number of recent matches to include (default: 10)
**Response** (200 OK):
```json
{
"id": 76561198012345678,
"name": "PlayerName",
"avatar": "...",
"recent_matches": 25,
"last_match_date": "2024-11-01T18:45:00Z",
"avg_kills": 21.3,
"avg_deaths": 17.8,
"avg_kast": 75.2,
"win_rate": 56.5
}
```
**Use Case**: Quick player card/preview without full match data.
---
#### 3. Track Player
Adds a player to the tracking system for automatic match updates.
**Endpoint**: `POST /player/:id/track`
**Parameters**:
- `id` (path, required): Steam ID
**Request Body**:
```json
{
"auth_code": "XXXX-XXXXX-XXXX"
}
```
**Response** (200 OK):
```json
{
"success": true,
"message": "Player added to tracking queue"
}
```
**Use Case**: Allow users to track their own profile for automatic updates.
---
#### 4. Untrack Player
Removes a player from the tracking system.
**Endpoint**: `DELETE /player/:id/track`
**Parameters**:
- `id` (path, required): Steam ID
**Response** (200 OK):
```json
{
"success": true,
"message": "Player removed from tracking"
}
```
---
### Match Endpoints
#### 5. Parse Match from Share Code
Triggers parsing of a CS:GO/CS2 match from a share code.
**Endpoint**: `GET /match/parse/:sharecode`
**Parameters**:
- `sharecode` (path, required): CS:GO match share code (e.g., `CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX`)
**Response** (200 OK):
```json
{
"match_id": 3589487716842078322,
"status": "parsing",
"message": "Demo download and parsing initiated"
}
```
**Response** (202 Accepted):
```json
{
"match_id": 3589487716842078322,
"status": "queued",
"estimated_time": 120
}
```
**Use Case**: User submits match share code for analysis.
---
#### 6. Get Match Details
Retrieves full match details including all player statistics.
**Endpoint**: `GET /match/:id`
**Parameters**:
- `id` (path, required): Match ID (uint64)
**Response** (200 OK):
```json
{
"match_id": 3589487716842078322,
"share_code": "CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
"map": "de_inferno",
"date": "2024-11-01T18:45:00Z",
"score_team_a": 13,
"score_team_b": 10,
"duration": 2456,
"match_result": 1,
"max_rounds": 24,
"demo_parsed": true,
"vac_present": false,
"gameban_present": false,
"tick_rate": 64.0,
"players": [
{
"id": 76561198012345678,
"name": "Player1",
"avatar": "...",
"team_id": 2,
"kills": 24,
"deaths": 18,
"assists": 6,
"headshot": 12,
"mvp": 3,
"score": 56,
"kast": 78,
"rank_old": 18500,
"rank_new": 18650,
"dmg_enemy": 2450,
"dmg_team": 120,
"flash_assists": 4,
"flash_duration_enemy": 15.6,
"flash_duration_team": 2.3,
"flash_duration_self": 1.2,
"flash_total_enemy": 8,
"flash_total_team": 1,
"flash_total_self": 1,
"ud_he": 450,
"ud_flames": 230,
"ud_flash": 5,
"ud_smoke": 3,
"ud_decoy": 0,
"mk_2": 4,
"mk_3": 2,
"mk_4": 1,
"mk_5": 0,
"avg_ping": 25.5,
"color": "yellow",
"crosshair": "CSGO_XXXXXXXXXXXX"
}
]
}
```
**Use Case**: Display match overview page with scoreboard.
---
#### 7. Get Match Weapons
Retrieves weapon statistics for all players in a match.
**Endpoint**: `GET /match/:id/weapons`
**Parameters**:
- `id` (path, required): Match ID
**Response** (200 OK):
```json
{
"match_id": 3589487716842078322,
"weapons": [
{
"player_id": 76561198012345678,
"weapon_stats": [
{
"eq_type": 7,
"weapon_name": "AK-47",
"kills": 12,
"damage": 1450,
"hits": 48,
"hit_groups": {
"head": 8,
"chest": 25,
"stomach": 8,
"left_arm": 3,
"right_arm": 2,
"left_leg": 1,
"right_leg": 1
}
}
]
}
]
}
```
**Use Case**: Display detailed weapon performance page for match.
---
#### 8. Get Match Rounds
Retrieves round-by-round statistics for a match.
**Endpoint**: `GET /match/:id/rounds`
**Parameters**:
- `id` (path, required): Match ID
**Response** (200 OK):
```json
{
"match_id": 3589487716842078322,
"rounds": [
{
"round": 1,
"winner": 2,
"win_reason": "elimination",
"players": [
{
"player_id": 76561198012345678,
"bank": 800,
"equipment": 650,
"spent": 650,
"kills_in_round": 2,
"damage_in_round": 234
}
]
}
]
}
```
**Use Case**: Display economy tab with round-by-round breakdown.
---
#### 9. Get Match Chat
Retrieves in-game chat messages from a match.
**Endpoint**: `GET /match/:id/chat`
**Parameters**:
- `id` (path, required): Match ID
**Response** (200 OK):
```json
{
"match_id": 3589487716842078322,
"messages": [
{
"player_id": 76561198012345678,
"player_name": "Player1",
"message": "nice shot!",
"tick": 15840,
"round": 8,
"all_chat": true,
"timestamp": "2024-11-01T19:12:34Z"
}
]
}
```
**Use Case**: Display chat log page for match.
---
### Matches Listing
#### 10. Get Matches List
Retrieves a paginated list of matches.
**Endpoint**: `GET /matches`
**Alternative**: `GET /matches/next/:time`
**Parameters**:
- `time` (path, optional): Unix timestamp for pagination
- Query parameters:
- `limit` (optional): Number of matches to return (default: 50, max: 100)
- `map` (optional): Filter by map name (e.g., `de_inferno`)
- `player_id` (optional): Filter by player Steam ID
**Response** (200 OK):
```json
{
"matches": [
{
"match_id": 3589487716842078322,
"map": "de_inferno",
"date": "2024-11-01T18:45:00Z",
"score_team_a": 13,
"score_team_b": 10,
"duration": 2456,
"demo_parsed": true,
"player_count": 10
}
],
"next_page_time": 1698871200,
"has_more": true
}
```
**Use Case**: Display matches listing page with filters.
---
### Sitemap
#### 11. Get Sitemap Index
Returns XML sitemap index for SEO.
**Endpoint**: `GET /sitemap.xml`
**Response** (200 OK):
```xml
<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>https://csgow.tf/sitemap/1</loc>
<lastmod>2024-11-04</lastmod>
</sitemap>
</sitemapindex>
```
---
#### 12. Get Sitemap Pages
Returns XML sitemap for specific page range.
**Endpoint**: `GET /sitemap/:id`
**Parameters**:
- `id` (path, required): Sitemap page number
**Response** (200 OK):
```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://csgow.tf/match/3589487716842078322</loc>
<lastmod>2024-11-01</lastmod>
<changefreq>never</changefreq>
<priority>0.8</priority>
</url>
</urlset>
```
---
## Data Models
### Match
```typescript
interface Match {
match_id: number; // uint64, unique identifier
share_code: string; // CS:GO share code
map: string; // Map name (e.g., "de_inferno")
date: string; // ISO 8601 timestamp
score_team_a: number; // Final score for team A (T/CT)
score_team_b: number; // Final score for team B (CT/T)
duration: number; // Match duration in seconds
match_result: number; // 0 = tie, 1 = team_a win, 2 = team_b win
max_rounds: number; // Max rounds (24 for MR12, 30 for MR15)
demo_parsed: boolean; // Demo parsing status
vac_present: boolean; // Any VAC bans in match
gameban_present: boolean; // Any game bans in match
tick_rate: number; // Server tick rate (64 or 128)
players?: MatchPlayer[]; // Array of player stats
}
```
### Player
```typescript
interface Player {
id: number; // uint64, Steam ID
name: string; // Steam display name
avatar: string; // Avatar URL
vanity_url?: string; // Custom Steam URL
vanity_url_real?: string; // Actual vanity URL
steam_updated: string; // Last Steam profile update
profile_created?: string; // Steam account creation date
wins?: number; // Total competitive wins
looses?: number; // Total competitive losses (note: typo in backend)
ties?: number; // Total ties
vac_count?: number; // Number of VAC bans
vac_date?: string; // Date of last VAC ban
game_ban_count?: number; // Number of game bans
game_ban_date?: string; // Date of last game ban
oldest_sharecode_seen?: string; // Oldest match on record
matches?: Match[]; // Recent matches
}
```
### MatchPlayer
```typescript
interface MatchPlayer {
match_stats: number; // Match ID reference
player_stats: number; // Player ID reference
team_id: number; // 2 = T, 3 = CT
// Performance
kills: number;
deaths: number;
assists: number;
headshot: number; // Headshot kills
mvp: number; // MVP stars
score: number; // In-game score
kast: number; // KAST percentage (0-100)
// Rank
rank_old?: number; // Rating before match (CS2: 0-30000)
rank_new?: number; // Rating after match
// Damage
dmg_enemy?: number; // Damage to enemies
dmg_team?: number; // Team damage
// Multi-kills
mk_2?: number; // Double kills
mk_3?: number; // Triple kills
mk_4?: number; // Quad kills
mk_5?: number; // Ace (5k)
// Utility damage
ud_he?: number; // HE grenade damage
ud_flames?: number; // Molotov/Incendiary damage
ud_flash?: number; // Flash grenades used
ud_smoke?: number; // Smoke grenades used
ud_decoy?: number; // Decoy grenades used
// Flash statistics
flash_assists?: number; // Assists from flashes
flash_duration_enemy?: number; // Total enemy blind time
flash_duration_team?: number; // Total team blind time
flash_duration_self?: number; // Self-flash time
flash_total_enemy?: number; // Enemies flashed count
flash_total_team?: number; // Teammates flashed count
flash_total_self?: number; // Self-flash count
// Other
crosshair?: string; // Crosshair settings
color?: 'green' | 'yellow' | 'purple' | 'blue' | 'orange' | 'grey';
avg_ping?: number; // Average ping
}
```
### RoundStats
```typescript
interface RoundStats {
round: number; // Round number (1-24 for MR12)
bank: number; // Money at round start
equipment: number; // Equipment value purchased
spent: number; // Total money spent
match_player_id: number; // Reference to MatchPlayer
}
```
### Weapon
```typescript
interface Weapon {
victim: number; // Player ID who was hit/killed
dmg: number; // Damage dealt
eq_type: number; // Weapon type ID
hit_group: number; // Hit location (1=head, 2=chest, etc.)
match_player_id: number; // Reference to MatchPlayer
}
```
### Message
```typescript
interface Message {
message: string; // Chat message content
all_chat: boolean; // true = all chat, false = team chat
tick: number; // Game tick when sent
match_player_id: number; // Reference to MatchPlayer
}
```
---
## Integration Guide
### Frontend Setup
1. **Install HTTP Client**
```bash
npm install axios
# or use native fetch
```
2. **Create API Client** (`src/lib/api/client.ts`)
```typescript
import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';
const API_TIMEOUT = import.meta.env.VITE_API_TIMEOUT || 10000;
class APIClient {
private client: AxiosInstance;
constructor() {
this.client = axios.create({
baseURL: API_BASE_URL,
timeout: API_TIMEOUT,
headers: {
'Content-Type': 'application/json',
},
});
// Response interceptor for error handling
this.client.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
// Server responded with error
console.error('API Error:', error.response.status, error.response.data);
} else if (error.request) {
// Request made but no response
console.error('Network Error:', error.message);
}
return Promise.reject(error);
}
);
}
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.get<T>(url, config);
return response.data;
}
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.post<T>(url, data, config);
return response.data;
}
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
const response = await this.client.delete<T>(url, config);
return response.data;
}
}
export const apiClient = new APIClient();
```
3. **Create API Endpoints** (`src/lib/api/players.ts`)
```typescript
import { apiClient } from './client';
import type { Player, Match } from '$lib/types';
export const playersAPI = {
async getPlayer(steamId: string | number, beforeTime?: number): Promise<Player> {
const url = beforeTime
? `/player/${steamId}/next/${beforeTime}`
: `/player/${steamId}`;
return apiClient.get<Player>(url);
},
async getPlayerMeta(steamId: string | number, limit: number = 10) {
return apiClient.get(`/player/${steamId}/meta/${limit}`);
},
async trackPlayer(steamId: string | number, authCode: string) {
return apiClient.post(`/player/${steamId}/track`, { auth_code: authCode });
},
async untrackPlayer(steamId: string | number) {
return apiClient.delete(`/player/${steamId}/track`);
},
};
```
4. **Create API Endpoints** (`src/lib/api/matches.ts`)
```typescript
import { apiClient } from './client';
import type { Match } from '$lib/types';
export const matchesAPI = {
async getMatch(matchId: string | number): Promise<Match> {
return apiClient.get<Match>(`/match/${matchId}`);
},
async getMatchWeapons(matchId: string | number) {
return apiClient.get(`/match/${matchId}/weapons`);
},
async getMatchRounds(matchId: string | number) {
return apiClient.get(`/match/${matchId}/rounds`);
},
async getMatchChat(matchId: string | number) {
return apiClient.get(`/match/${matchId}/chat`);
},
async parseMatch(shareCode: string) {
return apiClient.get(`/match/parse/${shareCode}`);
},
async getMatches(params?: {
limit?: number;
time?: number;
map?: string;
player_id?: number;
}) {
const queryString = params ? `?${new URLSearchParams(params as any).toString()}` : '';
const url = params?.time
? `/matches/next/${params.time}${queryString}`
: `/matches${queryString}`;
return apiClient.get(url);
},
};
```
5. **Use in SvelteKit Routes** (`src/routes/player/[id]/+page.ts`)
```typescript
import { playersAPI } from '$lib/api/players';
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ params }) => {
try {
const player = await playersAPI.getPlayer(params.id);
return { player };
} catch (err) {
throw error(404, 'Player not found');
}
};
```
---
## Error Handling
### HTTP Status Codes
| Status | Meaning | Common Causes |
|--------|---------|---------------|
| 200 | Success | Request completed successfully |
| 202 | Accepted | Request queued (async operations) |
| 400 | Bad Request | Invalid parameters or malformed request |
| 404 | Not Found | Player/match doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Backend issue |
| 503 | Service Unavailable | Backend down or maintenance |
### Error Response Format
```json
{
"error": "Match not found",
"code": "MATCH_NOT_FOUND",
"details": {
"match_id": 3589487716842078322
}
}
```
### Retry Strategy
For transient errors (500, 503), implement exponential backoff:
```typescript
async function retryRequest<T>(
fn: () => Promise<T>,
maxRetries: number = 3,
baseDelay: number = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) throw error;
const delay = baseDelay * Math.pow(2, i);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
```
---
## CS2 Migration Notes
### Rank System Changes
**CS:GO** used 18 ranks (Silver I to Global Elite):
- Values: 0-18
**CS2** uses Premier Rating:
- Values: 0-30,000
- No traditional ranks in Premier mode
**Backend Compatibility**:
- `rank_old` and `rank_new` fields now store Premier rating (0-30000)
- Frontend must detect game version and display accordingly
### MR12 vs MR15
**CS:GO**: MR15 (max 30 rounds)
**CS2**: MR12 (max 24 rounds)
Check `max_rounds` field in Match data:
- `24` = MR12 (CS2)
- `30` = MR15 (CS:GO)
### Game Mode Detection
To determine if a match is CS:GO or CS2:
1. Check `date` field (CS2 released Sept 2023)
2. Check `max_rounds` (24 = likely CS2)
3. Backend may add `game_version` field in future updates
---
## Rate Limiting
**Current Limits** (may vary by deployment):
- **Steam API**: 1 request per second (backend handles this)
- **Demo Parsing**: Max 6 concurrent parses
- **Frontend API**: No explicit limit, but use reasonable request rates
**Best Practices**:
- Implement client-side caching (5-15 minutes for match data)
- Use debouncing for search inputs (300ms)
- Batch requests when possible
---
## Caching Strategy
### Backend Caching (Redis)
The backend uses Redis for:
- Steam API responses (7 days)
- Match data (permanent until invalidated)
- Player profiles (7 days)
### Frontend Caching Recommendations
```typescript
// In-memory cache with TTL
class DataCache<T> {
private cache = new Map<string, { data: T; expires: number }>();
set(key: string, data: T, ttlMs: number) {
this.cache.set(key, {
data,
expires: Date.now() + ttlMs,
});
}
get(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() > entry.expires) {
this.cache.delete(key);
return null;
}
return entry.data;
}
}
// Usage
const matchCache = new DataCache<Match>();
const cachedMatch = matchCache.get(`match:${matchId}`);
if (!cachedMatch) {
const match = await matchesAPI.getMatch(matchId);
matchCache.set(`match:${matchId}`, match, 15 * 60 * 1000); // 15 min
}
```
---
## Testing
### Mock Data
Use MSW (Mock Service Worker) for testing:
```typescript
// src/mocks/handlers/matches.ts
import { http, HttpResponse } from 'msw';
export const matchHandlers = [
http.get('/match/:id', ({ params }) => {
return HttpResponse.json({
match_id: parseInt(params.id as string),
map: 'de_inferno',
date: '2024-11-01T18:45:00Z',
score_team_a: 13,
score_team_b: 10,
// ... rest of mock data
});
}),
];
```
---
## Swagger Documentation
The backend provides interactive API documentation at:
**URL**: `{API_BASE_URL}/api/swagger`
This Swagger UI allows you to:
- Explore all endpoints
- Test API calls directly
- View request/response schemas
- See authentication requirements
---
## Support & Updates
- **Backend Issues**: https://somegit.dev/CSGOWTF/csgowtfd/issues
- **Frontend Issues**: https://somegit.dev/CSGOWTF/csgowtf/issues
- **API Changes**: Watch the backend repository for updates
---
**Document Version**: 1.0
**Last Updated**: 2025-11-04
**Maintained By**: CSGOW.TF Development Team