Skip to content

Codeforces Adapter

Platform adapter for fetching contest data from Codeforces REST API.

Overview

The Codeforces adapter integrates with the official Codeforces API to fetch contest information including divisions, phases, and metadata.

Status

Enabled - Fully implemented and active

API Details

PropertyValue
API TypeREST API
Endpointhttps://codeforces.com/api/contest.list
MethodGET
AuthenticationNone (public API)
Timeout15000ms
Retry Attempts3

Location

Server/src/integrations/platforms/codeforces/codeforces.adapter.ts

Implementation

typescript
@Injectable()
export class CodeforcesAdapter extends BasePlatformAdapter {
  readonly platformName = ContestPlatform.CODEFORCES;
  private readonly apiBaseUrl = PLATFORM_URLS.CODEFORCES;

  constructor(private readonly configService: ConfigService) {
    const config: PlatformConfig = {
      enabled: true,
      apiUrl: PLATFORM_URLS.CODEFORCES,
      timeout: PLATFORM_TIMEOUTS.CODEFORCES,
      retryAttempts: HTTP_CONFIG.DEFAULT_RETRY_ATTEMPTS,
      retryDelay: HTTP_CONFIG.DEFAULT_RETRY_DELAY,
    };
    super(config);
  }
}

API Response Format

Codeforces Contest Object

typescript
interface CodeforcesContest {
  id: number;                    // Contest ID
  name: string;                  // Contest name
  type: string;                  // CF, ICPC, IOI
  phase: string;                 // BEFORE, CODING, FINISHED, etc.
  frozen: boolean;               // Is scoreboard frozen
  durationSeconds: number;       // Contest duration
  startTimeSeconds?: number;     // Unix timestamp
  relativeTimeSeconds?: number;  // Relative time
}

API Response

typescript
interface CodeforcesApiResponse {
  status: string;                // "OK" or "FAILED"
  result: CodeforcesContest[];   // Array of contests
}

Contest Types

TypeDescriptionExample
CFCodeforces RoundCodeforces Round #900 (Div. 2)
IOIIOI-style contestIOI-style contest
ICPCICPC-style contestICPC Regional Contest

Contest Phases

PhaseDescription
BEFOREContest not started
CODINGContest in progress
PENDING_SYSTEM_TESTWaiting for system testing
SYSTEM_TESTSystem testing in progress
FINISHEDContest completed

Methods

fetchContests()

Fetch all contests from Codeforces API.

typescript
async fetchContests(): Promise<ContestData[]>

Process:

  1. Make GET request to /api/contest.list
  2. Validate response status is "OK"
  3. Transform each contest to internal format
  4. Return array of ContestData

Example Response:

json
{
  "status": "OK",
  "result": [
    {
      "id": 1900,
      "name": "Codeforces Round #900 (Div. 2)",
      "type": "CF",
      "phase": "BEFORE",
      "frozen": false,
      "durationSeconds": 7200,
      "startTimeSeconds": 1707998400
    }
  ]
}

fetchUpcomingContests()

Fetch only upcoming contests (phase = BEFORE).

typescript
async fetchUpcomingContests(): Promise<ContestData[]>

fetchRunningContests()

Fetch only running contests (phase = CODING).

typescript
async fetchRunningContests(): Promise<ContestData[]>

transformToInternalFormat()

Transform Codeforces contest to unified format.

typescript
transformToInternalFormat(cfContest: CodeforcesContest): ContestData

Transformation Logic:

typescript
{
  platformId: cfContest.id.toString(),
  platform: ContestPlatform.CODEFORCES,
  name: cfContest.name,
  type: typeMap[cfContest.type] || ContestType.CF,
  phase: phaseMap[cfContest.phase] || ContestPhase.BEFORE,
  startTime: new Date(cfContest.startTimeSeconds * 1000),
  endTime: new Date((cfContest.startTimeSeconds + cfContest.durationSeconds) * 1000),
  durationMinutes: Math.floor(cfContest.durationSeconds / 60),
  isActive: cfContest.phase === 'BEFORE' || cfContest.phase === 'CODING',
  platformMetadata: {
    frozen: cfContest.frozen,
    relativeTimeSeconds: cfContest.relativeTimeSeconds,
  },
  lastSyncedAt: new Date(),
}

Platform Metadata

Codeforces-specific metadata stored in platformMetadata:

typescript
{
  frozen: boolean;              // Is scoreboard frozen
  relativeTimeSeconds: number;  // Time relative to now
}

Phase Mapping

typescript
const phaseMap: Record<string, ContestPhase> = {
  BEFORE: ContestPhase.BEFORE,
  CODING: ContestPhase.CODING,
  PENDING_SYSTEM_TEST: ContestPhase.PENDING_SYSTEM_TEST,
  SYSTEM_TEST: ContestPhase.SYSTEM_TEST,
  FINISHED: ContestPhase.FINISHED,
};

Type Mapping

typescript
const typeMap: Record<string, ContestType> = {
  CF: ContestType.CF,
  IOI: ContestType.IOI,
  ICPC: ContestType.ICPC,
};

Example Usage

Sync Codeforces Contests

bash
# Manual sync (admin only)
curl -X POST http://localhost:3000/contests/sync/codeforces \
  -H "Authorization: Bearer <admin_token>"

Get Codeforces Contests

bash
# Get all Codeforces contests
curl http://localhost:3000/contests/platform/codeforces \
  -H "Authorization: Bearer <token>"

Error Handling

API Errors

typescript
if (data.status !== 'OK') {
  throw new Error('Codeforces API returned error status');
}

Network Errors

Handled by BasePlatformAdapter with automatic retry (3 attempts).

Timeout

15-second timeout enforced using AbortController.

Testing

typescript
describe('CodeforcesAdapter', () => {
  let adapter: CodeforcesAdapter;

  beforeEach(() => {
    adapter = new CodeforcesAdapter(configService);
  });

  it('should fetch contests', async () => {
    const contests = await adapter.fetchContests();
    expect(contests).toBeDefined();
    expect(Array.isArray(contests)).toBe(true);
  });

  it('should transform contest correctly', () => {
    const cfContest = {
      id: 1900,
      name: 'Codeforces Round #900 (Div. 2)',
      type: 'CF',
      phase: 'BEFORE',
      frozen: false,
      durationSeconds: 7200,
      startTimeSeconds: 1707998400,
    };

    const result = adapter.transformToInternalFormat(cfContest);
    
    expect(result.platformId).toBe('1900');
    expect(result.platform).toBe(ContestPlatform.CODEFORCES);
    expect(result.type).toBe(ContestType.CF);
    expect(result.durationMinutes).toBe(120);
  });

  it('should filter upcoming contests', async () => {
    const contests = await adapter.fetchUpcomingContests();
    contests.forEach(contest => {
      expect(contest.startTime.getTime()).toBeGreaterThan(Date.now());
    });
  });
});

Configuration

Environment Variables

bash
# Codeforces API URL (optional, uses default)
CODEFORCES_API_URL=https://codeforces.com/api

# Timeout in milliseconds
CODEFORCES_TIMEOUT=15000

# Enable/disable adapter
CODEFORCES_ENABLED=true

Rate Limiting

Codeforces API has rate limits:

  • Limit: ~1 request per second
  • Handling: Built-in retry with exponential backoff
  • Recommendation: Sync every 6 hours (scheduled)

Best Practices

✅ Do

  1. Respect rate limits - Don't sync too frequently
  2. Handle frozen contests - Check frozen metadata
  3. Validate phase - Use phase mapping for consistency
  4. Log operations - Track sync success/failure
  5. Cache results - Store in MongoDB to reduce API calls

❌ Don't

  1. Don't ignore status - Always check status === "OK"
  2. Don't sync continuously - Use scheduled jobs (6 hours)
  3. Don't hardcode URLs - Use constants
  4. Don't skip error handling - API can fail
  5. Don't ignore phases - Different phases need different handling

Monitoring

Health Check

bash
curl http://localhost:3000/contests/health

Sync Logs

[CodeforcesAdapter] Fetching contests from Codeforces API
[CodeforcesAdapter] Successfully fetched 150 contests from Codeforces
[ContestsService] Syncing codeforces: 5 new, 12 updated, 0 failed

Built with ❤️ for competitive programmers worldwide.