Files
csgowtf/docs/API.md
vikingowl 8f3b652740 feat: Implement Phase 1 critical features and fix API integration
This commit completes the first phase of feature parity implementation and
resolves all API integration issues to match the backend API format.

## API Integration Fixes

- Remove all hardcoded default values from transformers (tick_rate, kast, player_count, steam_updated)
- Update TypeScript types to make fields optional where backend doesn't guarantee them
- Update Zod schemas to validate optional fields correctly
- Fix mock data to match real API response format (plain arrays, not wrapped objects)
- Update UI components to handle undefined values with proper fallbacks
- Add comprehensive API documentation for Match and Player endpoints

## Phase 1 Features Implemented (3/6)

### 1. Player Tracking System 
- Created TrackPlayerModal.svelte with auth code input
- Integrated track/untrack player API endpoints
- Added UI for providing optional share code
- Displays tracked status on player profiles
- Full validation and error handling

### 2. Share Code Parsing 
- Created ShareCodeInput.svelte component
- Added to matches page for easy match submission
- Real-time validation of share code format
- Parse status feedback with loading states
- Auto-redirect to match page on success

### 3. VAC/Game Ban Status 
- Added VAC and game ban count/date fields to Player type
- Display status badges on player profile pages
- Show ban count and date when available
- Visual indicators using DaisyUI badge components

## Component Improvements

- Modal.svelte: Added Svelte 5 Snippet types, actions slot support
- ThemeToggle.svelte: Removed deprecated svelte:component usage
- Tooltip.svelte: Fixed type safety with Snippet type
- All new components follow Svelte 5 runes pattern ($state, $derived, $bindable)

## Type Safety & Linting

- Fixed all ESLint errors (any types → proper types)
- Fixed form label accessibility issues
- Replaced error: any with error: unknown + proper type guards
- Added Snippet type imports where needed
- Updated all catch blocks to use instanceof Error checks

## Static Assets

- Migrated all files from public/ to static/ directory per SvelteKit best practices
- Moved 200+ map icons, screenshots, and other assets
- Updated all import paths to use /images/ (served from static/)

## Documentation

- Created IMPLEMENTATION_STATUS.md tracking all 15 missing features
- Updated API.md with optional field annotations
- Created MATCHES_API.md with comprehensive endpoint documentation
- Added inline comments marking optional vs required fields

## Testing

- Updated mock fixtures to remove default values
- Fixed mock handlers to return plain arrays like real API
- Ensured all components handle undefined gracefully

## Remaining Phase 1 Tasks

- [ ] Add VAC status column to match scoreboard
- [ ] Create weapons statistics tab for matches
- [ ] Implement recently visited players on home page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-12 19:31:18 +01:00

24 KiB

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
  2. API Endpoints
  3. Data Models
  4. Integration Guide
  5. 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:

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

For detailed documentation on the matches API specifically, see MATCHES_API.md.

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):

{
	"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", // Optional: may not always be provided
	"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, // Optional: not always provided by API
			"stats": {
				"team_id": 2,
				"kills": 24,
				"deaths": 18,
				"assists": 6,
				"headshot": 12,
				"mvp": 3,
				"score": 56,
				"kast": 78, // Optional: not always provided by API
				"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):

{
	"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:

{
	"auth_code": "XXXX-XXXXX-XXXX"
}

Response (200 OK):

{
	"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):

{
	"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):

{
	"match_id": 3589487716842078322,
	"status": "parsing",
	"message": "Demo download and parsing initiated"
}

Response (202 Accepted):

{
	"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):

{
	"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):

{
	"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):

{
	"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):

{
	"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 (use with /matches/next/:time)
  • 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):

IMPORTANT: This endpoint returns a plain array, not an object with properties.

[
	{
		"match_id": "3589487716842078322",
		"map": "de_inferno",
		"date": 1730487900,
		"score": [13, 10],
		"duration": 2456,
		"match_result": 1,
		"max_rounds": 24,
		"parsed": true,
		"vac": false,
		"game_ban": false
	}
]

Field Descriptions:

  • match_id: Unique match identifier (uint64 as string)
  • map: Map name (can be empty string if not parsed)
  • date: Unix timestamp (seconds since epoch)
  • score: Array with two elements [team_a_score, team_b_score]
  • duration: Match duration in seconds
  • match_result: 0 = tie, 1 = team_a win, 2 = team_b win
  • max_rounds: Maximum rounds (24 for MR12, 30 for MR15)
  • parsed: Whether the demo has been parsed
  • vac: Whether any player has a VAC ban
  • game_ban: Whether any player has a game ban

Pagination:

  • To get the next page, use the date field from the last match in the array
  • Request /matches/next/{timestamp} where {timestamp} is the Unix timestamp
  • Continue until the response returns fewer matches than requested

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 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 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

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

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

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

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

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

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
npm install axios
# or use native fetch
  1. Create API Client (src/lib/api/client.ts)
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();
  1. Create API Endpoints (src/lib/api/players.ts)
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`);
	}
};
  1. Create API Endpoints (src/lib/api/matches.ts)
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;
		before_time?: number;
		map?: string;
		player_id?: string;
	}) {
		const url = params?.before_time ? `/matches/next/${params.before_time}` : '/matches';
		const limit = params?.limit || 50;

		// API returns a plain array, not an object
		const data = await apiClient.get<LegacyMatchListItem[]>(url, {
			params: {
				limit: limit + 1, // Request one extra to check if there are more
				map: params?.map,
				player_id: params?.player_id
			}
		});

		// Check if there are more matches
		const hasMore = data.length > limit;
		const matches = hasMore ? data.slice(0, limit) : data;

		// Get timestamp for next page
		const lastMatch = matches[matches.length - 1];
		const nextPageTime =
			hasMore && lastMatch ? Math.floor(new Date(lastMatch.date).getTime() / 1000) : undefined;

		// Transform to new format
		return transformMatchesListResponse(matches, hasMore, nextPageTime);
	}
};
  1. Use in SvelteKit Routes (src/routes/player/[id]/+page.ts)
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

{
	"error": "Match not found",
	"code": "MATCH_NOT_FOUND",
	"details": {
		"match_id": 3589487716842078322
	}
}

Retry Strategy

For transient errors (500, 503), implement exponential backoff:

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

// 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:

// 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


Document Version: 1.0 Last Updated: 2025-11-04 Maintained By: CSGOW.TF Development Team