# API Proxying with SvelteKit Server Routes This document explains how API requests are proxied to the backend using SvelteKit server routes. ## Why Use Server Routes? The CS2.WTF frontend uses **SvelteKit server routes** to proxy API requests to the backend. This approach provides several benefits: - ✅ **Works in all environments**: Development, preview, and production - ✅ **No CORS issues**: Requests are server-side - ✅ **Single code path**: Same behavior everywhere - ✅ **Flexible backend switching**: Change one environment variable - ✅ **Future-proof**: Can add caching, rate limiting, auth later - ✅ **Better security**: Backend URL not exposed to client ## Architecture ### Request Flow ``` Browser → /api/matches → SvelteKit Server Route → Backend → Response ``` **Detailed Flow**: ``` 1. Browser: GET http://localhost:5173/api/matches?limit=20 ↓ 2. SvelteKit: Routes to src/routes/api/[...path]/+server.ts ↓ 3. Server Handler: Reads VITE_API_BASE_URL environment variable ↓ 4. Backend Call: GET https://api.csgow.tf/matches?limit=20 ↓ 5. Backend: Returns JSON response ↓ 6. Server Handler: Forwards response to browser ↓ 7. Browser: Receives response (no CORS issues!) ``` **SSR (Server-Side Rendering) Flow**: ``` 1. Page Load: +page.ts calls api.matches.getMatches() ↓ 2. API Client: Detects import.meta.env.SSR === true ↓ 3. Direct Call: GET https://api.csgow.tf/matches?limit=20 ↓ 4. Backend: Returns JSON response ↓ 5. SSR: Renders page with data ``` **Note**: SSR bypasses the SvelteKit route and calls the backend directly because relative URLs (`/api`) don't work during server-side rendering. ### Key Components **1. SvelteKit Server Route** (`src/routes/api/[...path]/+server.ts`) - Catch-all route that matches `/api/*` - Forwards requests to backend - Supports GET, POST, DELETE methods - Handles errors gracefully **2. API Client** (`src/lib/api/client.ts`) - Browser: Uses `/api` base URL (routes to SvelteKit) - SSR: Uses `VITE_API_BASE_URL` directly (bypasses SvelteKit route) - Automatically detects environment with `import.meta.env.SSR` **3. Environment Variable** (`.env`) - `VITE_API_BASE_URL` controls which backend to use - Switch between local and production easily ## Configuration ### Environment Variables **`.env`**: ```env # Production API (default) VITE_API_BASE_URL=https://api.csgow.tf # Local backend (for development) # VITE_API_BASE_URL=http://localhost:8000 ``` **Switching Backends**: ```bash # Use production API echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env npm run dev # Use local backend echo "VITE_API_BASE_URL=http://localhost:8000" > .env npm run dev ``` ### Server Route Implementation **File**: `src/routes/api/[...path]/+server.ts` ```typescript import { error, json } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://api.csgow.tf'; export const GET: RequestHandler = async ({ params, url }) => { const path = params.path; // e.g., "matches" const queryString = url.search; // e.g., "?limit=20" const backendUrl = `${API_BASE_URL}/${path}${queryString}`; try { const response = await fetch(backendUrl); const data = await response.json(); return json(data); } catch (err) { throw error(503, 'Unable to connect to backend'); } }; ``` ### API Client Configuration **File**: `src/lib/api/client.ts` ```typescript // Simple, single configuration const API_BASE_URL = '/api'; // Always routes to SvelteKit server routes // No environment detection needed ``` ## Testing the Setup ### 1. Check Environment Variable ```bash cat .env # Should show: VITE_API_BASE_URL=https://api.csgow.tf # or VITE_API_BASE_URL=http://localhost:8000 ``` ### 2. Start Development Server ```bash npm run dev # Server starts on http://localhost:5173 ``` ### 3. Check Network Requests Open DevTools → Network tab: - ✅ Requests go to `/api/matches`, `/api/player/123`, etc. - ✅ Status should be `200 OK` - ✅ No CORS errors in console ### 4. Test Both Backends **Test Production API**: ```bash # Set production API echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env # Start dev server npm run dev # Visit http://localhost:5173/matches # Should load matches from production API ``` **Test Local Backend**: ```bash # Start local backend first cd ../csgowtfd go run main.go # In another terminal, set local API echo "VITE_API_BASE_URL=http://localhost:8000" > .env # Start dev server npm run dev # Visit http://localhost:5173/matches # Should load matches from local backend ``` ## Common Issues ### Issue 1: 503 Service Unavailable **Symptom**: API requests return 503 error **Possible Causes**: 1. Backend is not running 2. Wrong `VITE_API_BASE_URL` in `.env` 3. Network connectivity issues **Fix**: ```bash # Check .env file cat .env # If using local backend, make sure it's running curl http://localhost:8000/matches # If using production API, check connectivity curl https://api.csgow.tf/matches # Restart dev server after changing .env npm run dev ``` ### Issue 2: 404 Not Found **Symptom**: `/api/*` routes return 404 **Cause**: SvelteKit server route file missing or not loaded **Fix**: ```bash # Check file exists ls src/routes/api/[...path]/+server.ts # If missing, create it mkdir -p src/routes/api/'[...path]' # Then create +server.ts file # Restart dev server npm run dev ``` ### Issue 3: Environment Variable Not Loading **Symptom**: Server route uses wrong backend URL **Cause**: Changes to `.env` require server restart **Fix**: ```bash # Stop dev server (Ctrl+C) # Update .env echo "VITE_API_BASE_URL=http://localhost:8000" > .env # Start dev server again npm run dev ``` ### Issue 4: CORS Errors Still Appearing **Symptom**: Browser console shows CORS errors **Cause**: API client is not using `/api` prefix **Fix**: Check `src/lib/api/client.ts`: ```typescript // Should be: const API_BASE_URL = '/api'; // Not: const API_BASE_URL = 'https://api.csgow.tf'; // ❌ Wrong ``` ## How It Works Compared to Vite Proxy ### Old Approach (Vite Proxy) ``` Development: Browser → /api → Vite Proxy → Backend Production: Browser → Backend (direct, different code path) ``` **Problems**: - Two different code paths (dev vs prod) - Proxy only works in development - SSR has to bypass proxy - Complex configuration ### New Approach (SvelteKit Server Routes) ``` All Environments: Browser → /api → SvelteKit Route → Backend ``` **Benefits**: - Single code path - Works in dev, preview, and production - Consistent behavior everywhere - Simpler configuration ## Adding Features ### Add Request Caching **File**: `src/routes/api/[...path]/+server.ts` ```typescript const cache = new Map(); export const GET: RequestHandler = async ({ params, url }) => { const cacheKey = `${params.path}${url.search}`; // Check cache const cached = cache.get(cacheKey); if (cached && Date.now() < cached.expires) { return json(cached.data); } // Fetch from backend const data = await fetch(`${API_BASE_URL}/${params.path}${url.search}`).then((r) => r.json()); // Cache for 5 minutes cache.set(cacheKey, { data, expires: Date.now() + 5 * 60 * 1000 }); return json(data); }; ``` ### Add Rate Limiting ```typescript import { rateLimit } from '$lib/server/rateLimit'; export const GET: RequestHandler = async ({ request, params, url }) => { // Check rate limit await rateLimit(request); // Continue with normal flow... }; ``` ### Add Authentication ```typescript export const GET: RequestHandler = async ({ request, params, url }) => { // Get auth token from cookie const token = request.headers.get('cookie')?.includes('auth_token'); // Forward to backend with auth const response = await fetch(backendUrl, { headers: { Authorization: `Bearer ${token}` } }); // ... }; ``` ## Summary | Feature | Vite Proxy | SvelteKit Routes | | --------------------- | ---------- | ---------------- | | Works in dev | ✅ | ✅ | | Works in production | ❌ | ✅ | | Single code path | ❌ | ✅ | | Can add caching | ❌ | ✅ | | Can add rate limiting | ❌ | ✅ | | Can add auth | ❌ | ✅ | | SSR compatible | ❌ | ✅ | **SvelteKit server routes provide a production-ready, maintainable solution for API proxying that works in all environments.**