Assalamu‘alaikum wr. wb.
Hello guys, Kembali lagi Bersama Teknoblog oleh Inzaghi's Blog! Munculnya Piala Dunia 2026 masih berlangsung dan sudah di babak Semifinal (Menuju Final) saat ini. Mari kita coba menguji API Piala Dunia FIFA 2026 untuk di beberapa Aplikasi, seperti Web, Mobile, hingga Pemrograman biasa, meskipun bukan API Resmi dari FIFA dan sifatnya juga Open Source.
Sumber : ChatGPT dan Claude AI
Piala Dunia FIFA 2026 akan digelar mulai 11 Juni 2026 (25 Dzulhijjah 1447 H) di Amerika Serikat, Kanada, dan Meksiko, diikuti 48 negara yang terbagi dalam 12 grup, dengan total 104 pertandingan mulai dari fase grup hingga final. Untuk menampilkan data seperti jadwal pertandingan, skor, klasemen grup, atau daftar tim ke dalam aplikasi web maupun mobile, kita bisa memanfaatkan API pihak ketiga worldcup26.ir yang menyediakan data tersebut dalam format JSON secara gratis dan mudah diakses lewat endpoint sederhana seperti /get/games dan /get/groups. Dokumen ini merangkum cara mengintegrasikan API tersebut ke 24 Stack teknologi berbeda — mulai dari front-end (HTML/JS, React, Vue, Flutter, dsb), back-end (Laravel, Express, Django, Spring Boot, dsb), hingga bahasa native seperti C, C++, dan Go — lengkap dengan contoh kode siap pakai, cara autentikasi, penanganan error, serta cara mengujinya lewat Postman, sehingga developer di berbagai stack tinggal menyesuaikan dengan kebutuhan aplikasinya masing-masing.
A. Ringkasan Penting
Catatan penting sebelum mulai, yaitu :
- API ini adalah proyek open-source pihak ketiga (bukan API resmi FIFA), dibuat oleh rezarahiminia (Repositori GitHub), dihosting di worldcup26.ir.
- Endpoint GET /get/* bisa diakses tanpa token untuk demo, tapi dokumentasi resminya menyebutkan bahwa semua endpoint (kecuali auth) idealnya memakai JWT Bearer Token. Jadi kode di bawah dibuat "defensif": mengirim header Authorization jika kamu punya token, dan tetap jalan tanpa token untuk endpoint publik.
- Ada rate limit untuk endpoint publik (default ±120 request/menit) dan cache di sisi server (±30 detik), jadi jangan polling terlalu sering.
- Karena ini API komunitas (bukan resmi), untuk produksi/komersial sebaiknya tetap siapkan fallback/error handling yang baik, dan pertimbangkan cache di sisi aplikasi kamu sendiri.
Dokumen ini merangkum cara mengintegrasikan API worldcup26.ir ke 24 Stack, yaitu HTML/JS Vanilla, PHP Native, CodeIgniter 4, Laravel, Node.js, Express.js, React, Next.js (App & Pages Router), Vue 3, Nuxt 3, Python Native, Flask, Django, FastAPI, Flutter, React Native, Kotlin (Android), Java Native, Java Spring Boot, Swift (iOS), C# (.NET), Go (Golang), C++, dan C.
Daftar Bahasa dan Teknologi :
- HTML + Vanilla JavaScript
- PHP Native (cURL & file_get_contents)
- CodeIgniter 4
- Laravel
- Node.js
- Express.js
- React
- Next.js (App Router & Pages Router)
- Vue 3
- Nuxt 3
- Python Native
- Flask
- Django
- FastAPI
- Flutter
- React Native
- Kotlin (Android)
- Java (Native)
- Java Spring Boot
- Swift (iOS)
- C# (.NET)
- Go (Golang)
- C++
- C
B. Ringkasan Endpoint yang Digunakan
Berikut ini adalah Tabel Rinciannya :
|
Endpoint
|
Deskripsi
|
Metode
|
|
https://worldcup26.ir/get/games
|
Semua 104
pertandingan
|
GET
|
|
https://worldcup26.ir/get/game/{id}
|
Detail 1
pertandingan (ID 1–104)
|
GET
|
|
https://worldcup26.ir/get/groups
|
12 grup (A–L)
beserta klasemen
|
GET
|
|
https://worldcup26.ir/get/group/{id}
atau ?name=A
|
Detail 1 grup
|
GET
|
|
https://worldcup26.ir/get/teams
|
48 tim
peserta
|
GET
|
|
https://worldcup26.ir/get/team/{id}
atau ?name=Argentina
|
Detail 1 tim
|
GET
|
|
https://worldcup26.ir/get/stadiums
|
16 stadion
|
GET
|
|
https://worldcup26.ir/auth/register
|
Registrasi
(dapat token)
|
POST
|
|
https://worldcup26.ir/auth/authenticate
|
Login (dapat
token)
|
POST
|
|
https://worldcup26.ir/health
|
Status
kesehatan API
|
GET
|
Contoh struktur respons GET /get/games :
{
"games": [
{
"id": "1",
"home_team_id": "1",
"away_team_id": "2",
"home_score": "0",
"away_score": "0",
"group": "A",
"matchday": "1",
"local_date": "06/11/2026 13:00",
"stadium_id": "1",
"finished": "FALSE",
"time_elapsed": "notstarted",
"type": "group",
"home_team_name_en": "Mexico",
"away_team_name_en": "South Africa"
}
]
}Contoh struktur respons GET /get/groups :
{
"group": "G",
"teams": [
{ "team_id": "25", "pts": "0", "gf": "0", "ga": "0" },
{ "team_id": "26", "pts": "0", "gf": "0", "ga": "0" }
]
}Ini biasanya berupa array dari objek di atas, satu objek per grup (A–L). Selalu console.log/print respons mentah dulu sebelum ngoding parsing, karena struktur API pihak ketiga bisa berubah sewaktu-waktu — jangan langsung percaya 100% pada skema di atas.
C. Testing API dengan Postman
Berikut langkah lengkap testing endpoint worldcup26.ir pakai Postman — dari setup environment, autentikasi, sampai automated testing dengan script.
1. Setup Environment
Pertama, buatlah Environment baru (ikon mata di kanan atas Postman) bernama WorldCup2026, isi variable berikut :
Keuntungan pakai environment variable, yaitu semua request tinggal panggil {{base_url}}/get/games dan {{token}}, jadi mudah dan ganti base URL (misal untuk testing di local/staging) tanpa edit satu-satu request.
2. Request Dasar — GET Data Pertandingan & Grup
Request 1: Get All Matches
- Method: GET
- URL: {{base_url}}/get/games
- Headers: Accept: application/json
- (Opsional) Authorization: Bearer {{token}}
Request 2: Get All Groups
- Method: GET
- URL: {{base_url}}/get/groups
Request 3: Get Match Detail
- Method: GET
- URL: {{base_url}}/get/game/1
Request 4: Get Group Detail by Name
- Method: GET
- URL: {{base_url}}/get/group?name=A
Klik Send di masing-masing, lalu cek tab Body (respons JSON) dan status code di kanan atas (harus 200 OK).
3. Autentikasi — Auto-Save Token dengan Script
Request: Login
- Method: POST
- URL: {{base_url}}/auth/authenticate
- Headers: Content-Type: application/json
Body (raw → JSON) :
{
"username": "your_username",
"password": "your_password"
}Buka tab Scripts → Post-response (di versi lama disebut "Tests"), lalu isi script berikut supaya token otomatis tersimpan ke environment setiap kali login berhasil :
pm.test("Login berhasil (status 200)", function () {pm.response.to.have.status(200);});const response = pm.response.json();if (response.token) {pm.environment.set("token", response.token);console.log("Token tersimpan:", response.token);} else {console.error("Login gagal, tidak ada token di respons:", response);}
Setelah ini, semua request lain tinggal pakai header Authorization: Bearer {{token}} tanpa perlu copy-paste token manual.
4. Automated Testing dengan Script (tab "Tests" / "Post-response")
Contoh Script validasi untuk Request 1 (Get All Matches) — taruh di tab Scripts → Post-response :
pm.test("Status code adalah 200", function () {pm.response.to.have.status(200);});pm.test("Response time di bawah 2000ms", function () {pm.expect(pm.response.responseTime).to.be.below(2000);});pm.test("Content-Type adalah JSON", function () {pm.response.to.have.header("Content-Type");pm.expect(pm.response.headers.get("Content-Type")).to.include("application/json");});pm.test("Response punya properti 'games' berupa array", function () {const data = pm.response.json();const games = data.games ?? data;pm.expect(games).to.be.an("array");});pm.test("Total pertandingan sesuai (104 pertandingan)", function () {const data = pm.response.json();const games = data.games ?? data;pm.expect(games.length).to.eql(104);});pm.test("Setiap pertandingan punya field wajib", function () {const data = pm.response.json();const games = data.games ?? data;const sample = games[0];pm.expect(sample).to.have.all.keys("id", "home_team_id", "away_team_id", "home_score", "away_score","group", "matchday", "local_date", "stadium_id", "finished","time_elapsed", "type", "home_team_name_en", "away_team_name_en");});
Untuk Request 2 (Get All Groups) :
pm.test("Status code adalah 200", function () {pm.response.to.have.status(200);});pm.test("Total grup ada 12 (A-L)", function () {const groups = pm.response.json();pm.expect(groups).to.be.an("array");pm.expect(groups.length).to.eql(12);});pm.test("Setiap grup punya array 'teams'", function () {const groups = pm.response.json();groups.forEach((g) => {pm.expect(g).to.have.property("group");pm.expect(g).to.have.property("teams").that.is.an("array");});});
Catatan :
Jika respons ternyata beda dari yang diasumsikan (misal games langsung array tanpa wrapper, atau field tambahan), sesuaikan assertion di atas — jalankan dulu request tanpa script untuk lihat struktur JSON asli di tab Body.
5. Menjalankan Semua Request Sekaligus — Collection Runner
Berikut ini adalah Langkah-langkahnya :
- Kumpulkan semua request di atas ke dalam satu Collection, misalnya WorldCup2026 API Tests.
- Klik collection → Run (ikon ▶ di sebelah nama collection).
- Pilih environment WorldCup2026, atur Iterations (misal 1x), lalu klik Run WorldCup2026 API Tests.
- Postman akan menjalankan semua request berurutan dan menampilkan ringkasan pass/fail dari semua pm.test() yang sudah dibuat.
6. Otomasi via CLI dengan Newman (opsional, untuk CI/CD)
Export collection & environment dari Postman (ikon ... → Export), lalu jalankan lewat command line pakai Newman — berguna jika ingin dimasukkan ke pipeline CI/CD (GitHub Actions, GitLab CI, dsb.) :
npm install -g newman newman run WorldCup2026-API-Tests.postman_collection.json \ -e WorldCup2026.postman_environment.json \ --reporters cli,json \ --reporter-json-export newman-report.json
Contoh Step di GitHub Actions (.github/workflows/api-test.yml) :
name: API Testson: [push]jobs:test:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v4- run: npm install -g newman- run: newman run WorldCup2026-API-Tests.postman_collection.json -e WorldCup2026.postman_environment.json
Tips Testing Khusus API Ini :
- Cek dulu tanpa token. Karena endpoint /get/* bisa diakses publik untuk demo, uji dulu tanpa Authorization header — kalau tetap 200, berarti auth belum wajib untuk endpoint itu saat ini.
- Uji rate limit dengan sengaja. Kirim request berkali-kali cepat (misal pakai Collection Runner dengan iterations tinggi + delay kecil) untuk lihat kapan API membalas 429 Too Many Requests, supaya kamu tahu batas amannya sebelum dipakai di aplikasi production.
- Uji endpoint yang butuh parameter tidak valid, misal /get/game/9999 (ID di luar 1–104) atau /get/group?name=Z (grup tidak ada), untuk memastikan aplikasi kamu bisa handle error/404 dari API ini dengan baik.
- Simpan contoh respons sebagai mock. Kalau API pihak ketiga ini down/berubah, kamu bisa pakai Postman Mock Server dari contoh respons yang sudah disimpan, supaya development front-end/mobile kamu tidak terganggu.
D. Kode Program API
Berikut ini adalah beberapa cara mengintegrasikan API worldcup26.ir ke 24 Stack, yaitu HTML/JS Vanilla, PHP Native, CodeIgniter 4, Laravel, Node.js, Express.js, React, Next.js (App & Pages Router), Vue 3, Nuxt 3, Python Native, Flask, Django, FastAPI, Flutter, React Native, Kotlin (Android), Java Native, Java Spring Boot, Swift (iOS), C# (.NET), Go (Golang), C++, dan C.
1. HTML + JavaScript Murni (Vanilla JavaScript)
<!DOCTYPE html><html lang="id"><head><meta charset="UTF-8"><title>Piala Dunia 2026 - Jadwal Pertandingan</title></head><body><h1>Jadwal Pertandingan Piala Dunia 2026</h1><div id="matches">Memuat data...</div><script>const API_BASE = "https://worldcup26.ir";// Kalau kamu sudah login dan punya token, isi di sini. Kosongkan jika ingin coba tanpa token.const TOKEN = "";async function fetchMatches() {try {const headers = {};if (TOKEN) headers["Authorization"] = `Bearer ${TOKEN}`;const res = await fetch(`${API_BASE}/get/games`, { headers });if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();renderMatches(data.games || data);} catch (err) {document.getElementById("matches").innerText = "Gagal memuat data: " + err.message;console.error(err);}}function renderMatches(games) {const container = document.getElementById("matches");container.innerHTML = "";games.slice(0, 10).forEach(g => {const el = document.createElement("div");el.innerHTML = `<strong>${g.home_team_name_en ?? g.home_team_id}</strong> vs<strong>${g.away_team_name_en ?? g.away_team_id}</strong>— Grup ${g.group}, ${g.local_date}<br>Skor: ${g.home_score} - ${g.away_score} (${g.finished === "TRUE" ? "Selesai" : "Belum main"})<hr>`;container.appendChild(el);});}fetchMatches();</script></body></html>
Hasil Output :
A
2. PHP Native
Nama File : matches.php
<?php// matches.phpfunction fetchWorldCupData(string $endpoint, ?string $token = null): array {$url = "https://worldcup26.ir" . $endpoint;$ch = curl_init($url);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_TIMEOUT, 10);$headers = ["Accept: application/json"];if ($token) {$headers[] = "Authorization: Bearer {$token}";}curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);$response = curl_exec($ch);$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);$error = curl_error($ch);curl_close($ch);if ($error) {throw new Exception("cURL error: {$error}");}if ($httpCode !== 200) {throw new Exception("HTTP error {$httpCode}: {$response}");}return json_decode($response, true);}try {// Ambil data pertandingan$matchesData = fetchWorldCupData("/get/games");$games = $matchesData['games'] ?? $matchesData;// Ambil data grup$groups = fetchWorldCupData("/get/groups");} catch (Exception $e) {die("Gagal mengambil data: " . $e->getMessage());}?><!DOCTYPE html><html lang="id"><head><meta charset="UTF-8"><title>Piala Dunia 2026</title></head><body><h1>Jadwal Pertandingan</h1><table border="1" cellpadding="6"><tr><th>Tuan Rumah</th><th>Tamu</th><th>Grup</th><th>Tanggal</th><th>Skor</th></tr><?php foreach (array_slice($games, 0, 10) as $g): ?><tr><td><?= htmlspecialchars($g['home_team_name_en'] ?? $g['home_team_id']) ?></td><td><?= htmlspecialchars($g['away_team_name_en'] ?? $g['away_team_id']) ?></td><td><?= htmlspecialchars($g['group']) ?></td><td><?= htmlspecialchars($g['local_date']) ?></td><td><?= htmlspecialchars($g['home_score'] . ' - ' . $g['away_score']) ?></td></tr><?php endforeach; ?></table><h1>Grup Peserta</h1><?php foreach ($groups as $grp): ?><h3>Grup <?= htmlspecialchars($grp['group']) ?></h3><ul><?php foreach ($grp['teams'] as $t): ?><li>Team ID <?= htmlspecialchars($t['team_id']) ?> — Poin: <?= htmlspecialchars($t['pts']) ?></li><?php endforeach; ?></ul><?php endforeach; ?></body></html>
?>
Hasil Output :
3. PHP Native — versi file_get_contents (alternatif tanpa cURL)
Nama File : matches.php
<?phpfunction fetchWorldCupDataSimple(string $endpoint, ?string $token = null): array {$url = "https://worldcup26.ir" . $endpoint;$headers = ["Accept: application/json"];if ($token) {$headers[] = "Authorization: Bearer {$token}";}$context = stream_context_create(["http" => ["method" => "GET","header" => implode("\r\n", $headers),"timeout" => 10,"ignore_errors" => true, // supaya tetap bisa baca body walau status bukan 200],]);$response = @file_get_contents($url, false, $context);if ($response === false) {throw new Exception("Gagal menghubungi API (kemungkinan timeout atau host tidak bisa diakses).");}// Cek status code dari header respons$statusLine = $http_response_header[0] ?? '';preg_match('{HTTP/\S*\s(\d{3})}', $statusLine, $match);$statusCode = (int) ($match[1] ?? 0);if ($statusCode !== 200) {throw new Exception("HTTP error {$statusCode}: {$response}");}return json_decode($response, true);}// --- Ambil data & render sebagai halaman HTML (sama seperti contoh cURL di atas) ---try {$matchesData = fetchWorldCupDataSimple("/get/games");$games = $matchesData['games'] ?? $matchesData;$groups = fetchWorldCupDataSimple("/get/groups");} catch (Exception $e) {die("Gagal mengambil data: " . $e->getMessage());}?><!DOCTYPE html><html lang="id"><head><meta charset="UTF-8"><title>Piala Dunia 2026 (file_get_contents)</title></head><body><h1>Jadwal Pertandingan</h1><table border="1" cellpadding="6"><tr><th>Tuan Rumah</th><th>Tamu</th><th>Grup</th><th>Tanggal</th><th>Skor</th></tr><?php foreach (array_slice($games, 0, 10) as $g): ?><tr><td><?= htmlspecialchars($g['home_team_name_en'] ?? $g['home_team_id']) ?></td><td><?= htmlspecialchars($g['away_team_name_en'] ?? $g['away_team_id']) ?></td><td><?= htmlspecialchars($g['group']) ?></td><td><?= htmlspecialchars($g['local_date']) ?></td><td><?= htmlspecialchars($g['home_score'] . ' - ' . $g['away_score']) ?></td></tr><?php endforeach; ?></table><h1>Grup Peserta</h1><?php foreach ($groups as $grp): ?><h3>Grup <?= htmlspecialchars($grp['group']) ?></h3><ul><?php foreach ($grp['teams'] as $t): ?><li>Team ID <?= htmlspecialchars($t['team_id']) ?> — Poin: <?= htmlspecialchars($t['pts']) ?></li><?php endforeach; ?></ul><?php endforeach; ?></body></html>
?>
Hasil Output :
4. CodeIgniter (V. 4)
Untuk CodeIgniter (CI), tersedia dalam beberapa File yang terdiri dari :
- Service (app/Libraries/WorldCupService.php)
- Controller (app/Controllers/WorldCup.php)
- Route (app/Config/Routes.php)
- View (app/Views/worldcup/matches.php)
1.) Buat Service (app/Libraries/WorldCupService.php)
<?phpnamespace App\Libraries;use Config\Services;class WorldCupService{protected string $baseUrl = 'https://worldcup26.ir';protected ?string $token;public function __construct(){$this->token = getenv('WORLDCUP26_TOKEN') ?: null;}protected function client(){return Services::curlrequest(['base_uri' => $this->baseUrl,'timeout' => 10,]);}protected function headers(): array{$headers = ['Accept' => 'application/json'];if ($this->token) {$headers['Authorization'] = "Bearer {$this->token}";}return $headers;}public function getMatches(): array{$response = $this->client()->get('/get/games', ['headers' => $this->headers()]);$data = json_decode($response->getBody(), true);return $data['games'] ?? $data;}public function getGroups(): array{$response = $this->client()->get('/get/groups', ['headers' => $this->headers()]);return json_decode($response->getBody(), true);}}?>
2.) Controller (app/Controllers/WorldCup.php)
<?phpnamespace App\Controllers;use App\Libraries\WorldCupService;class WorldCup extends BaseController{public function matches(){$service = new WorldCupService();$games = $service->getMatches();return view('worldcup/matches', ['games' => array_slice($games, 0, 10)]);}public function groups(){$service = new WorldCupService();$groups = $service->getGroups();return view('worldcup/groups', ['groups' => $groups]);}}?>
3.) Route (app/Config/Routes.php)
<?php$routes->get('matches', 'WorldCup::matches');$routes->get('groups', 'WorldCup::groups');?>
4.) View (app/Views/worldcup/matches.php)
<h1>Jadwal Pertandingan</h1><table border="1" cellpadding="6"><tr><th>Tuan Rumah</th><th>Tamu</th><th>Grup</th><th>Skor</th></tr><?php foreach ($games as $g): ?><tr><td><?= esc($g['home_team_name_en'] ?? $g['home_team_id']) ?></td><td><?= esc($g['away_team_name_en'] ?? $g['away_team_id']) ?></td><td><?= esc($g['group']) ?></td><td><?= esc($g['home_score'] . ' - ' . $g['away_score']) ?></td></tr><?php endforeach; ?></table>
Hasil Output :
5. Laravel
Untuk Laravel, juga tersedia dalam beberapa File yang terdiri dari :
- Service Class (app/Services/WorldCupService.php)
- Config (config/services.php)
- Controller (app/Http/Controllers/WorldCupController.php)
- Route (routes/web.php)
- Blade View (resources/views/worldcup/matches.blade.php)
1.) Buat Service Class (app/Services/WorldCupService.php)
<?phpnamespace App\Services;use Illuminate\Support\Facades\Http;use Illuminate\Support\Facades\Cache;class WorldCupService{protected string $baseUrl = 'https://worldcup26.ir';public function getMatches(): array{return Cache::remember('wc26_matches', now()->addSeconds(30), function () {$response = Http::timeout(10)->withHeaders($this->authHeader())->get("{$this->baseUrl}/get/games");$response->throw(); // lempar exception kalau bukan 2xxreturn $response->json('games') ?? $response->json();});}public function getGroups(): array{return Cache::remember('wc26_groups', now()->addSeconds(30), function () {$response = Http::timeout(10)->withHeaders($this->authHeader())->get("{$this->baseUrl}/get/groups");$response->throw();return $response->json();});}protected function authHeader(): array{$token = config('services.worldcup26.token'); // set di config/services.php + .envreturn $token ? ['Authorization' => "Bearer {$token}"] : [];}}?>
2.) Tambahkan Config (config/services.php)
<?php'worldcup26' => ['token' => env('WORLDCUP26_TOKEN'),],?>
3.) Controller (app/Http/Controllers/WorldCupController.php)
<?phpnamespace App\Http\Controllers;use App\Services\WorldCupService;class WorldCupController extends Controller{public function __construct(protected WorldCupService $worldCup) {}public function matches(){$games = $this->worldCup->getMatches();return view('worldcup.matches', compact('games'));}public function groups(){$groups = $this->worldCup->getGroups();return view('worldcup.groups', compact('groups'));}}?>
4.) Route (routes/web.php)
<?phpuse App\Http\Controllers\WorldCupController;Route::get('/matches', [WorldCupController::class, 'matches']);Route::get('/groups', [WorldCupController::class, 'groups']);?>
5.) Blade View (resources/views/worldcup/matches.blade.php)
<h1>Jadwal Pertandingan Piala Dunia 2026</h1><table class="table"><thead><tr><th>Tuan Rumah</th><th>Tamu</th><th>Grup</th><th>Tanggal</th><th>Skor</th></tr></thead><tbody>@foreach($games as $g)<tr><td>{{ $g['home_team_name_en'] ?? $g['home_team_id'] }}</td><td>{{ $g['away_team_name_en'] ?? $g['away_team_id'] }}</td><td>{{ $g['group'] }}</td><td>{{ $g['local_date'] }}</td><td>{{ $g['home_score'] }} - {{ $g['away_score'] }}</td></tr>@endforeach</tbody></table>
Hasil Output :
6. Node.js (Native, tanpa framework)
Node.js 18+ sudah punya fetch bawaan, jadi tidak perlu install axios/node-fetch lagi.
Nama File : worldcup.js
// worldcup.jsconst API_BASE = "https://worldcup26.ir";const TOKEN = process.env.WORLDCUP26_TOKEN || "";async function fetchData(endpoint) {const headers = { Accept: "application/json" };if (TOKEN) headers.Authorization = `Bearer ${TOKEN}`;const res = await fetch(`${API_BASE}${endpoint}`, { headers, signal: AbortSignal.timeout(10000) });if (!res.ok) {throw new Error(`HTTP ${res.status}: ${await res.text()}`);}return res.json();}async function main() {try {const matchesData = await fetchData("/get/games");const games = matchesData.games ?? matchesData;games.slice(0, 5).forEach((g) => {console.log(`${g.home_team_name_en ?? g.home_team_id} vs ${g.away_team_name_en ?? g.away_team_id} — Grup ${g.group}`);});const groups = await fetchData("/get/groups");console.log(groups);} catch (err) {console.error("Gagal mengambil data:", err.message);}}main();
Jalankan dengan :
node worldcup.js
Hasil Output :
7. Express.js
Nama File : server.js
// server.jsconst express = require("express");const app = express();const API_BASE = "https://worldcup26.ir";const TOKEN = process.env.WORLDCUP26_TOKEN || "";// Cache sederhana in-memory (30 detik)let cache = { matches: null, groups: null, ts: 0 };const CACHE_TTL = 30_000;async function fetchFromWorldCup(endpoint) {const headers = { Accept: "application/json" };if (TOKEN) headers.Authorization = `Bearer ${TOKEN}`;const res = await fetch(`${API_BASE}${endpoint}`, { headers });if (!res.ok) throw new Error(`HTTP ${res.status}`);return res.json();}app.get("/api/matches", async (req, res) => {try {const now = Date.now();if (!cache.matches || now - cache.ts > CACHE_TTL) {const data = await fetchFromWorldCup("/get/games");cache.matches = data.games ?? data;cache.ts = now;}res.json(cache.matches);} catch (err) {res.status(502).json({ error: "Gagal mengambil data pertandingan", detail: err.message });}});app.get("/api/groups", async (req, res) => {try {if (!cache.groups) {cache.groups = await fetchFromWorldCup("/get/groups");}res.json(cache.groups);} catch (err) {res.status(502).json({ error: "Gagal mengambil data grup", detail: err.message });}});app.listen(3000, () => console.log("Server jalan di http://localhost:3000"));
Frontend pada Express.js ini tinggal fetch("/api/matches") — token tetap aman di server, tidak terekspos ke browser.
8. React.js (Client-Side dengan fetch)
import { useEffect, useState } from "react";const API_BASE = "https://worldcup26.ir";const TOKEN = ""; // isi kalau punya tokenfunction useWorldCupData(endpoint) {const [data, setData] = useState(null);const [error, setError] = useState(null);const [loading, setLoading] = useState(true);useEffect(() => {let cancelled = false;async function load() {try {setLoading(true);const headers = TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {};const res = await fetch(`${API_BASE}${endpoint}`, { headers });if (!res.ok) throw new Error(`HTTP ${res.status}`);const json = await res.json();if (!cancelled) setData(json);} catch (err) {if (!cancelled) setError(err.message);} finally {if (!cancelled) setLoading(false);}}load();return () => { cancelled = true; };}, [endpoint]);return { data, error, loading };}export default function MatchesList() {const { data, error, loading } = useWorldCupData("/get/games");if (loading) return <p>Memuat jadwal pertandingan...</p>;if (error) return <p>Gagal memuat data: {error}</p>;const games = data.games ?? data;return (<div><h1>Jadwal Pertandingan Piala Dunia 2026</h1><ul>{games.slice(0, 10).map((g) => (<li key={g.id}>{g.home_team_name_en ?? g.home_team_id} vs {g.away_team_name_en ?? g.away_team_id}{" — Grup "}{g.group}{" — "}{g.local_date}{" — Skor: "}{g.home_score}-{g.away_score}</li>))}</ul></div>);}
Hasil Output :
9. Vue.js (V. 3)
Contoh pakai Composition API dengan <script setup> pada File MatchesList.vue :
<!-- MatchesList.vue --><script setup>import { ref, onMounted } from "vue";const API_BASE = "https://worldcup26.ir";const TOKEN = ""; // isi kalau punya tokenconst games = ref([]);const loading = ref(true);const error = ref(null);async function fetchMatches() {try {loading.value = true;const headers = TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {};const res = await fetch(`${API_BASE}/get/games`, { headers });if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();games.value = (data.games ?? data).slice(0, 10);} catch (err) {error.value = err.message;} finally {loading.value = false;}}onMounted(fetchMatches);</script><template><div><h1>Jadwal Pertandingan Piala Dunia 2026</h1><p v-if="loading">Memuat data...</p><p v-else-if="error">Gagal memuat data: {{ error }}</p><ul v-else><li v-for="g in games" :key="g.id">{{ g.home_team_name_en ?? g.home_team_id }} vs {{ g.away_team_name_en ?? g.away_team_id }}— Grup {{ g.group }} — {{ g.local_date }}— Skor: {{ g.home_score }}-{{ g.away_score }}</li></ul></div></template>
Bisa juga diringkas dengan composable (useWorldCup.js) kalau dipakai di banyak komponen :
// composables/useWorldCup.jsimport { ref } from "vue";export function useWorldCupData(endpoint) {const data = ref(null);const error = ref(null);const loading = ref(true);async function load() {try {loading.value = true;const res = await fetch(`https://worldcup26.ir${endpoint}`);if (!res.ok) throw new Error(`HTTP ${res.status}`);data.value = await res.json();} catch (err) {error.value = err.message;} finally {loading.value = false;}}load();return { data, error, loading };}
Hasil Output :
10. Next.js (App Router & Pages Router)
Untuk di Next.js, juga tersedia dalam beberapa File yang terdiri dari :
- Server Component langsung fetch untuk Data Pertandingan (app/matches/page.tsx)
- Server Component lewat Route Handler sebagai proxy (app/api/groups/route.ts)
- Pages Router — getServerSideProps / getStaticProps + API Route (pages/matches.tsx)
- API Route sebagai proxy (pages/api/groups.ts)
- Pages Router — getServerSideProps / getStaticProps + API Route (pages/matches.tsx)
- API Route sebagai proxy (pages/api/groups.ts)
1.) App Router — Server Component + Route Handler
Server Component langsung fetch (data pertandingan) :
// app/matches/page.tsxexport const revalidate = 30; // ISR: cache 30 detik, samain dgn cache server APIasync function getMatches() {const res = await fetch("https://worldcup26.ir/get/games", {headers: process.env.WORLDCUP26_TOKEN? { Authorization: `Bearer ${process.env.WORLDCUP26_TOKEN}` }: {},next: { revalidate: 30 },});if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();return data.games ?? data;}export default async function MatchesPage() {const games = await getMatches();return (<main><h1>Jadwal Pertandingan Piala Dunia 2026</h1><ul>{games.slice(0, 10).map((g: any) => (<li key={g.id}>{g.home_team_name_en ?? g.home_team_id} vs {g.away_team_name_en ?? g.away_team_id} — Grup {g.group}</li>))}</ul></main>);}
Atau lewat Route Handler sebagai proxy (agar token tidak terekspos ke client) :
// app/api/groups/route.tsimport { NextResponse } from "next/server";export async function GET() {const res = await fetch("https://worldcup26.ir/get/groups", {headers: process.env.WORLDCUP26_TOKEN? { Authorization: `Bearer ${process.env.WORLDCUP26_TOKEN}` }: {},next: { revalidate: 30 },});if (!res.ok) {return NextResponse.json({ error: "Gagal mengambil data grup" }, { status: res.status });}const groups = await res.json();return NextResponse.json(groups);}
Lalu di client, komponen kamu cukup fetch("/api/groups").
2.) Pages Router — getServerSideProps / getStaticProps + API Route
// pages/matches.tsximport { GetServerSideProps } from "next";interface Match {id: string;home_team_name_en?: string;away_team_name_en?: string;home_team_id: string;away_team_id: string;group: string;local_date: string;home_score: string;away_score: string;}interface Props {games: Match[];error?: string;}export const getServerSideProps: GetServerSideProps<Props> = async () => {try {const res = await fetch("https://worldcup26.ir/get/games", {headers: process.env.WORLDCUP26_TOKEN? { Authorization: `Bearer ${process.env.WORLDCUP26_TOKEN}` }: {},});if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();return { props: { games: (data.games ?? data).slice(0, 10) } };} catch (err: any) {return { props: { games: [], error: err.message } };}};export default function MatchesPage({ games, error }: Props) {if (error) return <p>Gagal memuat data: {error}</p>;return (<main><h1>Jadwal Pertandingan Piala Dunia 2026</h1><ul>{games.map((g) => (<li key={g.id}>{g.home_team_name_en ?? g.home_team_id} vs {g.away_team_name_en ?? g.away_team_id} — Grup {g.group}</li>))}</ul></main>);}
API Route sebagai proxy (pages/api/groups.ts) :
import type { NextApiRequest, NextApiResponse } from "next";export default async function handler(req: NextApiRequest, res: NextApiResponse) {try {const apiRes = await fetch("https://worldcup26.ir/get/groups", {headers: process.env.WORLDCUP26_TOKEN? { Authorization: `Bearer ${process.env.WORLDCUP26_TOKEN}` }: {},});if (!apiRes.ok) throw new Error(`HTTP ${apiRes.status}`);const groups = await apiRes.json();res.status(200).json(groups);} catch (err: any) {res.status(502).json({ error: "Gagal mengambil data grup", detail: err.message });}}
Kalau butuh data selalu fresh saat request masuk gunakan getServerSideProps; kalau data cukup di-build ulang berkala (misal setiap 30 detik) gunakan getStaticProps dengan opsi revalidate: 30 (Incremental Static Regeneration).
Hasil Output :
11. Nuxt JS (V. 3)
Nuxt 3 punya useFetch/$fetch bawaan yang otomatis SSR-friendly :
Nama File : matches.vue
<!-- pages/matches.vue --><script setup>const { data, error, pending } = await useFetch("https://worldcup26.ir/get/games", {key: "wc26-matches",server: true,headers: useRuntimeConfig().worldcup26Token? { Authorization: `Bearer ${useRuntimeConfig().worldcup26Token}` }: {},});const games = computed(() => (data.value?.games ?? data.value ?? []).slice(0, 10));</script><template><div><h1>Jadwal Pertandingan Piala Dunia 2026</h1><p v-if="pending">Memuat data...</p><p v-else-if="error">Gagal memuat data: {{ error.message }}</p><ul v-else><li v-for="g in games" :key="g.id">{{ g.home_team_name_en ?? g.home_team_id }} vs {{ g.away_team_name_en ?? g.away_team_id }} — Grup {{ g.group }}</li></ul></div></template>
Server route sebagai proxy (server/api/groups.get.ts) — supaya token tidak terekspos ke client :
export default defineEventHandler(async (event) => {const config = useRuntimeConfig();try {const groups = await $fetch("https://worldcup26.ir/get/groups", {headers: config.worldcup26Token? { Authorization: `Bearer ${config.worldcup26Token}` }: {},});return groups;} catch (err) {throw createError({ statusCode: 502, statusMessage: "Gagal mengambil data grup" });}});
Lalu daftarkan token di nuxt.config.ts :
export default defineNuxtConfig({runtimeConfig: {worldcup26Token: process.env.WORLDCUP26_TOKEN,},});
Dan di client cukup panggil useFetch("/api/groups").
Hasil Output :
12. Python Native (pakai requests)
import requestsAPI_BASE = "https://worldcup26.ir"TOKEN = "" # isi kalau punya tokendef fetch_data(endpoint: str) -> dict:headers = {"Accept": "application/json"}if TOKEN:headers["Authorization"] = f"Bearer {TOKEN}"response = requests.get(f"{API_BASE}{endpoint}", headers=headers, timeout=10)response.raise_for_status() # raise error kalau status bukan 2xxreturn response.json()def show_matches():data = fetch_data("/get/games")games = data.get("games", data)for g in games[:10]:home = g.get("home_team_name_en", g.get("home_team_id"))away = g.get("away_team_name_en", g.get("away_team_id"))print(f"{home} vs {away} — Grup {g['group']} — {g['local_date']} "f"— Skor {g['home_score']}-{g['away_score']}")def show_groups():groups = fetch_data("/get/groups")for grp in groups:print(f"Grup {grp['group']}:")for t in grp["teams"]:print(f" Team ID {t['team_id']} — Poin {t['pts']}")if __name__ == "__main__":show_matches()show_groups()
Output :
13. Flask
File utama (app.py) :
from flask import Flask, render_template, jsonifyimport requestsapp = Flask(__name__)API_BASE = "https://worldcup26.ir"TOKEN = "" # atau ambil dari os.environdef fetch_data(endpoint):headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}res = requests.get(f"{API_BASE}{endpoint}", headers=headers, timeout=10)res.raise_for_status()return res.json()@app.route("/matches")def matches():data = fetch_data("/get/games")games = data.get("games", data)return render_template("matches.html", games=games[:10])@app.route("/groups")def groups():groups = fetch_data("/get/groups")return render_template("groups.html", groups=groups)# Contoh endpoint JSON API sendiri (proxy)@app.route("/api/matches")def api_matches():return jsonify(fetch_data("/get/games"))if __name__ == "__main__":app.run(debug=True)
templates/matches.html :
<h1>Jadwal Pertandingan</h1><table border="1"><tr><th>Tuan Rumah</th><th>Tamu</th><th>Grup</th><th>Skor</th></tr>{% for g in games %}<tr><td>{{ g.get('home_team_name_en', g.home_team_id) }}</td><td>{{ g.get('away_team_name_en', g.away_team_id) }}</td><td>{{ g.group }}</td><td>{{ g.home_score }} - {{ g.away_score }}</td></tr>{% endfor %}</table>
14. Django
Di Django, juga tersedia dalam beberapa File yang terdiri dari :
- Config (worldcup/services.py)
- Route (worldcup/urls.py)
- View (worldcup/views.py)
1.) Config (worldcup/services.py)
import requestsfrom django.core.cache import cacheAPI_BASE = "https://worldcup26.ir"TOKEN = "" # sebaiknya dari settings.py / envdef fetch_data(endpoint: str, cache_key: str, ttl: int = 30):cached = cache.get(cache_key)if cached is not None:return cachedheaders = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}res = requests.get(f"{API_BASE}{endpoint}", headers=headers, timeout=10)res.raise_for_status()data = res.json()cache.set(cache_key, data, ttl)return data
2.) Route (worldcup/urls.py)
from django.shortcuts import renderfrom .services import fetch_datadef matches_view(request):data = fetch_data("/get/games", "wc26_matches")games = data.get("games", data)return render(request, "worldcup/matches.html", {"games": games[:10]})def groups_view(request):groups = fetch_data("/get/groups", "wc26_groups")return render(request, "worldcup/groups.html", {"groups": groups})
3.) View (worldcup/views.py)
from django.urls import pathfrom . import viewsurlpatterns = [path("matches/", views.matches_view, name="matches"),path("groups/", views.groups_view, name="groups"),]
Template Django mirip Jinja di Flask, seperti {{ g.group }}, {% for %}, dst.
Hasil Output :
15. FastAPI
from fastapi import FastAPI, HTTPExceptionfrom fastapi.responses import JSONResponseimport httpxapp = FastAPI(title="World Cup 2026 Proxy API")API_BASE = "https://worldcup26.ir"TOKEN = "" # sebaiknya dari environment variableasync def fetch_data(endpoint: str) -> dict:headers = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}async with httpx.AsyncClient(timeout=10) as client:res = await client.get(f"{API_BASE}{endpoint}", headers=headers)if res.status_code != 200:raise HTTPException(status_code=res.status_code, detail="Gagal mengambil data dari worldcup26.ir")return res.json()@app.get("/matches")async def get_matches():data = await fetch_data("/get/games")return data.get("games", data)@app.get("/groups")async def get_groups():return await fetch_data("/get/groups")@app.get("/matches/{match_id}")async def get_match_detail(match_id: int):data = await fetch_data(f"/get/game/{match_id}")return data.get("game", data)
Jalankan dengan :
uvicorn main:app --reload
Hasil Output :
16. Flutter (Dart)
Untuk di Flutter, juga tersedia dalam beberapa File yang terdiri dari :
- File Dependency (pubspec.yaml)
- Model sederhana (lib/models/match_model.dart)
- Service (lib/services/worldcup_service.dart)
- UI Widget (lib/screens/matches_screen.dart)
1.) File Dependency (pubspec.yaml)
Tambahkan dependency di pubspec.yaml :
dependencies:http: ^1.2.0
2.) Model Sederhana (lib/models/match_model.dart)
class WorldCupMatch {final String id;final String homeTeamName;final String awayTeamName;final String homeScore;final String awayScore;final String group;final String localDate;WorldCupMatch({required this.id,required this.homeTeamName,required this.awayTeamName,required this.homeScore,required this.awayScore,required this.group,required this.localDate,});factory WorldCupMatch.fromJson(Map<String, dynamic> json) {return WorldCupMatch(id: json['id'].toString(),homeTeamName: json['home_team_name_en'] ?? json['home_team_id'].toString(),awayTeamName: json['away_team_name_en'] ?? json['away_team_id'].toString(),homeScore: json['home_score'].toString(),awayScore: json['away_score'].toString(),group: json['group'].toString(),localDate: json['local_date'].toString(),);}}
3.) Service (lib/services/worldcup_service.dart)
import 'dart:convert';import 'package:http/http.dart' as http;import '../models/match_model.dart';class WorldCupService {static const String baseUrl = "https://worldcup26.ir";static const String token = ""; // isi kalau punya tokenstatic Future<List<WorldCupMatch>> fetchMatches() async {final uri = Uri.parse("$baseUrl/get/games");final headers = <String, String>{"Accept": "application/json",if (token.isNotEmpty) "Authorization": "Bearer $token",};final response = await http.get(uri, headers: headers).timeout(const Duration(seconds: 10));if (response.statusCode != 200) {throw Exception("Gagal memuat data (status ${response.statusCode})");}final decoded = jsonDecode(response.body);final List games = decoded is Map ? decoded['games'] : decoded;return games.map((g) => WorldCupMatch.fromJson(g)).toList();}static Future<List<dynamic>> fetchGroups() async {final uri = Uri.parse("$baseUrl/get/groups");final headers = <String, String>{if (token.isNotEmpty) "Authorization": "Bearer $token",};final response = await http.get(uri, headers: headers).timeout(const Duration(seconds: 10));if (response.statusCode != 200) {throw Exception("Gagal memuat data grup");}return jsonDecode(response.body);}}
4.) UI Widget (lib/screens/matches_screen.dart)
import 'package:flutter/material.dart';import '../services/worldcup_service.dart';import '../models/match_model.dart';class MatchesScreen extends StatefulWidget {const MatchesScreen({super.key});@overrideState<MatchesScreen> createState() => _MatchesScreenState();}class _MatchesScreenState extends State<MatchesScreen> {late Future<List<WorldCupMatch>> _futureMatches;@overridevoid initState() {super.initState();_futureMatches = WorldCupService.fetchMatches();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text("Jadwal Piala Dunia 2026")),body: FutureBuilder<List<WorldCupMatch>>(future: _futureMatches,builder: (context, snapshot) {if (snapshot.connectionState == ConnectionState.waiting) {return const Center(child: CircularProgressIndicator());}if (snapshot.hasError) {return Center(child: Text("Error: ${snapshot.error}"));}final matches = snapshot.data!;return ListView.builder(itemCount: matches.length,itemBuilder: (context, index) {final m = matches[index];return ListTile(title: Text("${m.homeTeamName} vs ${m.awayTeamName}"),subtitle: Text("Grup ${m.group} — ${m.localDate}"),trailing: Text("${m.homeScore} - ${m.awayScore}"),);},);},),);}}
Hasil Output :
17. React Native
Struktur mirip React biasa, hanya UI-nya menggunakan komponen native (View, Text, FlatList).
Nama File : MatchesScreen.js
// MatchesScreen.jsimport { useEffect, useState } from "react";import { View, Text, FlatList, ActivityIndicator, StyleSheet } from "react-native";const API_BASE = "https://worldcup26.ir";const TOKEN = ""; // isi kalau punya token, sebaiknya ambil dari secure storage / .envexport default function MatchesScreen() {const [games, setGames] = useState([]);const [loading, setLoading] = useState(true);const [error, setError] = useState(null);useEffect(() => {async function fetchMatches() {try {const headers = TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {};const controller = new AbortController();const timeout = setTimeout(() => controller.abort(), 10000);const res = await fetch(`${API_BASE}/get/games`, { headers, signal: controller.signal });clearTimeout(timeout);if (!res.ok) throw new Error(`HTTP ${res.status}`);const data = await res.json();setGames((data.games ?? data).slice(0, 20));} catch (err) {setError(err.message);} finally {setLoading(false);}}fetchMatches();}, []);if (loading) return <ActivityIndicator style={{ flex: 1 }} size="large" />;if (error) return <Text style={styles.error}>Gagal memuat data: {error}</Text>;return (<FlatListdata={games}keyExtractor={(item) => item.id}renderItem={({ item }) => (<View style={styles.row}><Text style={styles.teams}>{item.home_team_name_en ?? item.home_team_id} vs {item.away_team_name_en ?? item.away_team_id}</Text><Text style={styles.meta}>Grup {item.group} — {item.local_date}</Text><Text style={styles.score}>{item.home_score} - {item.away_score}</Text></View>)}/>);}const styles = StyleSheet.create({row: { padding: 12, borderBottomWidth: 1, borderBottomColor: "#eee" },teams: { fontWeight: "bold" },meta: { color: "#666" },score: { marginTop: 4 },error: { padding: 16, color: "red" },});
Hasil Output :
18. Kotlin (Android)
Contoh pakai Retrofit + Coroutines (pendekatan paling umum di Android modern), yang terdiri dari beberapa File, seperti :
1.) Dependency (build.gradle.kts)
dependencies {implementation("com.squareup.retrofit2:retrofit:2.11.0")implementation("com.squareup.retrofit2:converter-gson:2.11.0")implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")}
2.) Data class & API interface (Match.kt dan WorldCupApi.kt)
Match.kt :
data class Match(val id: String,val home_team_id: String,val away_team_id: String,val home_team_name_en: String?,val away_team_name_en: String?,val home_score: String,val away_score: String,val group: String,val local_date: String)
WorldCupApi.kt :
data class MatchesResponse(val games: List<Match>)interface WorldCupApi {@GET("get/games")suspend fun getMatches(@Header("Authorization") token: String? = null): MatchesResponse@GET("get/groups")suspend fun getGroups(@Header("Authorization") token: String? = null): List<Map<String, Any>>}
3.) Retrofit client (WorldCupClient.kt)
data class MatchesResponse(val games: List<Match>)interface WorldCupApi {@GET("get/games")suspend fun getMatches(@Header("Authorization") token: String? = null): MatchesResponse@GET("get/groups")suspend fun getGroups(@Header("Authorization") token: String? = null): List<Map<String, Any>>}
4.) Pemakaian di ViewModel (MatchesViewModel.kt)
import androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScopeimport kotlinx.coroutines.launchimport kotlinx.coroutines.flow.MutableStateFlowimport kotlinx.coroutines.flow.StateFlowclass MatchesViewModel : ViewModel() {private val _matches = MutableStateFlow<List<Match>>(emptyList())val matches: StateFlow<List<Match>> = _matchesprivate val _error = MutableStateFlow<String?>(null)val error: StateFlow<String?> = _errorfun loadMatches(token: String? = null) {viewModelScope.launch {try {val authHeader = token?.let { "Bearer $it" }val response = WorldCupClient.api.getMatches(authHeader)_matches.value = response.games.take(10)} catch (e: Exception) {_error.value = "Gagal memuat data: ${e.message}"}}}}
Hasil Output :
21. Swift (iOS)
Contoh pakai URLSession + async/await (Swift 5.5+, tanpa dependency tambahan) :
1.) Model & Service (WorldCupService.swift)
import Foundationstruct Match: Decodable, Identifiable {let id: Stringlet home_team_id: Stringlet away_team_id: Stringlet home_team_name_en: String?let away_team_name_en: String?let home_score: Stringlet away_score: Stringlet group: Stringlet local_date: String}struct MatchesResponse: Decodable {let games: [Match]}enum WorldCupError: Error {case badStatus(Int)}final class WorldCupService {static let baseUrl = "https://worldcup26.ir"static let token: String? = nil // isi kalau punya tokenstatic func fetchMatches() async throws -> [Match] {var request = URLRequest(url: URL(string: "\(baseUrl)/get/games")!)request.timeoutInterval = 10if let token {request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")}let (data, response) = try await URLSession.shared.data(for: request)guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {let status = (response as? HTTPURLResponse)?.statusCode ?? -1throw WorldCupError.badStatus(status)}let decoded = try JSONDecoder().decode(MatchesResponse.self, from: data)return decoded.games}}
Hasil Output :
20. Java (Native — HttpClient)
Java 11+ sudah punya java.net.http.HttpClient bawaan, jadi tidak perlu library HTTP tambahan. Untuk parsing JSON, contoh ini pakai Jackson (paling umum dipakai di ekosistem Java/Spring).
1.) Dependency (Maven, pom.xml)
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.17.0</version></dependency>
2.) Model Class (Match.java)
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import java.util.List;@JsonIgnoreProperties(ignoreUnknown = true)public class Match {public String id;public String home_team_id;public String away_team_id;public String home_team_name_en;public String away_team_name_en;public String home_score;public String away_score;public String group;public String local_date;public String finished;}@JsonIgnoreProperties(ignoreUnknown = true)class MatchesResponse {public List<Match> games;}
3.) Service untuk fetch data (WorldCupService.java)
import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.time.Duration;import com.fasterxml.jackson.databind.ObjectMapper;public class WorldCupService {private static final String BASE_URL = "https://worldcup26.ir";private final HttpClient client;private final ObjectMapper mapper = new ObjectMapper();private final String token; // isi kalau punya token, boleh nullpublic WorldCupService(String token) {this.token = token;this.client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build();}public MatchesResponse getMatches() throws Exception {HttpRequest.Builder builder = HttpRequest.newBuilder().uri(URI.create(BASE_URL + "/get/games")).header("Accept", "application/json").timeout(Duration.ofSeconds(10)).GET();if (token != null && !token.isBlank()) {builder.header("Authorization", "Bearer " + token);}HttpResponse<String> response = client.send(builder.build(), HttpResponse.BodyHandlers.ofString());if (response.statusCode() != 200) {throw new RuntimeException("HTTP error " + response.statusCode() + ": " + response.body());}return mapper.readValue(response.body(), MatchesResponse.class);}public static void main(String[] args) throws Exception {WorldCupService service = new WorldCupService(null);MatchesResponse data = service.getMatches();data.games.stream().limit(10).forEach(g -> {String home = g.home_team_name_en != null ? g.home_team_name_en : g.home_team_id;String away = g.away_team_name_en != null ? g.away_team_name_en : g.away_team_id;System.out.printf("%s vs %s — Grup %s — Skor %s-%s%n", home, away, g.group, g.home_score, g.away_score);});}}
Jalankan dengan :
javac WorldCupService.java && java WorldCupService
Pastikan Jackson ada di classpath, atau gunakan Maven/Gradle.
Hasil Output :
21. Java Spring Boot
Pakai RestClient (Spring 6.1+/Boot 3.2+, pengganti modern RestTemplate) — kalau versi Spring Boot Anda lebih lama, RestTemplate juga bisa dipakai dengan pola yang mirip.
1.) Dependency (pom.xml) — cukup spring-boot-starter-web, sudah termasuk Jackson & RestClient
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
2.) Konfigurasi property (application.yml)
worldcup26:base-url: https://worldcup26.irtoken: ${WORLDCUP26_TOKEN:}
3.) Data Transfer Object (DTO) (Match.java)
package com.example.worldcup.dto;import com.fasterxml.jackson.annotation.JsonIgnoreProperties;import java.util.List;@JsonIgnoreProperties(ignoreUnknown = true)public record Match(String id,String home_team_id,String away_team_id,String home_team_name_en,String away_team_name_en,String home_score,String away_score,String group,String local_date,String finished) {}@JsonIgnoreProperties(ignoreUnknown = true)record MatchesResponse(List<Match> games) {}
4.) Service class (WorldCupService.java)
package com.example.worldcup.service;import com.example.worldcup.dto.Match;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import org.springframework.web.client.RestClient;import org.springframework.cache.annotation.Cacheable;import java.util.List;import java.util.Map;@Servicepublic class WorldCupService {private final RestClient restClient;private final String token;public WorldCupService(@Value("${worldcup26.base-url}") String baseUrl,@Value("${worldcup26.token:}") String token) {this.token = token;this.restClient = RestClient.builder().baseUrl(baseUrl).build();}@Cacheable("wc26-matches") // aktifkan @EnableCaching di main class untuk pakai inipublic List<Match> getMatches() {var response = restClient.get().uri("/get/games").headers(headers -> {if (token != null && !token.isBlank()) {headers.setBearerAuth(token);}}).retrieve().body(Map.class);List<?> rawGames = (List<?>) response.get("games");return rawGames.stream().map(g -> new com.fasterxml.jackson.databind.ObjectMapper().convertValue(g, Match.class)).toList();}@Cacheable("wc26-groups")public Object getGroups() {return restClient.get().uri("/get/groups").headers(headers -> {if (token != null && !token.isBlank()) {headers.setBearerAuth(token);}}).retrieve().body(Object.class);}}
5.) REST Controller (WorldCupController.java)
Jadi, Proxy API sendiri dan sekaligus menyembunyikan Token dari Client.
package com.example.worldcup.controller;import com.example.worldcup.dto.Match;import com.example.worldcup.service.WorldCupService;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestControllerpublic class WorldCupController {private final WorldCupService worldCupService;public WorldCupController(WorldCupService worldCupService) {this.worldCupService = worldCupService;}@GetMapping("/api/matches")public List<Match> matches() {return worldCupService.getMatches().stream().limit(10).toList();}@GetMapping("/api/groups")public Object groups() {return worldCupService.getGroups();}}
4.) Aktifkan Cache (WorldCupApplication.java) [Opsional]
Opsional, tapi disarankan supaya tidak spam request ke API pihak ketiga) di main class :
package com.example.worldcup;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication@EnableCachingpublic class WorldCupApplication {public static void main(String[] args) {SpringApplication.run(WorldCupApplication.class, args);}}
5.) Error handling global (GlobalExceptionHandler.java, opsional, @ControllerAdvice)
package com.example.worldcup.controller;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.RestControllerAdvice;import org.springframework.web.client.RestClientException;@RestControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(RestClientException.class)public ResponseEntity<String> handleRestClientException(RestClientException ex) {return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body("Gagal mengambil data dari worldcup26.ir: " + ex.getMessage());}}
Jalankan dengan :
mvn spring-boot:run
Lalu, akses http://localhost:8080/api/matches — frontend (React/Vue/dsb.) Anda tinggal fetch Endpoint ini; token tetap aman di sisi server Spring Boot.
Hasil Output :
22. C# (.NET)
Contoh pakai HttpClient (ASP.NET Core / Console App), dengan System.Text.Json :
1.) Model (Match.cs)
public class Match{public string Id { get; set; } = "";public string Home_team_id { get; set; } = "";public string Away_team_id { get; set; } = "";public string? Home_team_name_en { get; set; }public string? Away_team_name_en { get; set; }public string Home_score { get; set; } = "";public string Away_score { get; set; } = "";public string Group { get; set; } = "";public string Local_date { get; set; } = "";}public class MatchesResponse{public List<Match> Games { get; set; } = new();}
Catatan :
Match dan MatchesResponse di sini ditaruh dalam satu file yang sama karena keduanya kecil dan berkaitan erat (response wrapper). Kalau proyekmu punya banyak model, biasa juga dipisah jadi Match.cs dan MatchesResponse.cs sendiri-sendiri.
2.) Service (WorldCupService.cs)
using System.Net.Http.Json;using System.Text.Json.Serialization;public class WorldCupService{private readonly HttpClient _client;private readonly string? _token;public WorldCupService(HttpClient client, string? token = null){_client = client;_client.BaseAddress = new Uri("https://worldcup26.ir");_client.Timeout = TimeSpan.FromSeconds(10);_token = token;}public async Task<List<Match>> GetMatchesAsync(){var request = new HttpRequestMessage(HttpMethod.Get, "/get/games");if (!string.IsNullOrEmpty(_token)){request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", _token);}var response = await _client.SendAsync(request);if (!response.IsSuccessStatusCode){throw new Exception($"HTTP error {(int)response.StatusCode}");}var data = await response.Content.ReadFromJsonAsync<MatchesResponse>();return data?.Games ?? new List<Match>();}}
3. Pemakaian sebagai Console App (Program.cs)
var httpClient = new HttpClient();var service = new WorldCupService(httpClient);var matches = await service.GetMatchesAsync();foreach (var m in matches.Take(10)){Console.WriteLine($"{m.Home_team_name_en ?? m.Home_team_id} vs {m.Away_team_name_en ?? m.Away_team_id} — Grup {m.Group}");}
Hasil Output :
23. Go (Golang)
package mainimport ("encoding/json""fmt""io""net/http""time")const apiBase = "https://worldcup26.ir"type Match struct {ID string `json:"id"`HomeTeamID string `json:"home_team_id"`AwayTeamID string `json:"away_team_id"`HomeTeamNameEn string `json:"home_team_name_en"`AwayTeamNameEn string `json:"away_team_name_en"`HomeScore string `json:"home_score"`AwayScore string `json:"away_score"`Group string `json:"group"`LocalDate string `json:"local_date"`}type MatchesResponse struct {Games []Match `json:"games"`}func fetchJSON(endpoint string, token string, target interface{}) error {client := &http.Client{Timeout: 10 * time.Second}req, err := http.NewRequest("GET", apiBase+endpoint, nil)if err != nil {return err}req.Header.Set("Accept", "application/json")if token != "" {req.Header.Set("Authorization", "Bearer "+token)}res, err := client.Do(req)if err != nil {return err}defer res.Body.Close()body, err := io.ReadAll(res.Body)if err != nil {return err}if res.StatusCode != http.StatusOK {return fmt.Errorf("HTTP error %d: %s", res.StatusCode, string(body))}return json.Unmarshal(body, target)}func main() {var matchesData MatchesResponseif err := fetchJSON("/get/games", "", &matchesData); err != nil {fmt.Println("Gagal mengambil data pertandingan:", err)return}limit := 10if len(matchesData.Games) < limit {limit = len(matchesData.Games)}for _, g := range matchesData.Games[:limit] {home := g.HomeTeamNameEnif home == "" {home = g.HomeTeamID}away := g.AwayTeamNameEnif away == "" {away = g.AwayTeamID}fmt.Printf("%s vs %s — Grup %s — %s\n", home, away, g.Group, g.LocalDate)}var groups []map[string]interface{}if err := fetchJSON("/get/groups", "", &groups); err != nil {fmt.Println("Gagal mengambil data grup:", err)return}fmt.Println(groups)}
Hasil Output :
24. C++
Contoh pakai library libcurl (https://curl.se/libcurl) untuk request HTTP dan GitHub nlohmann/json untuk parsing JSON — kombinasi paling umum di C++ modern.
Instalasi (Linux Ubuntu/Debian) :
sudo apt-get install libcurl4-openssl-dev nlohmann-json3-dev
Nama File : worldcup_client.cpp
#include <iostream>#include <string>#include <curl/curl.h>#include <nlohmann/json.hpp>using json = nlohmann::json;// Callback untuk menampung response body dari libcurlstatic size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {size_t totalSize = size * nmemb;output->append(static_cast<char*>(contents), totalSize);return totalSize;}std::string fetchData(const std::string& endpoint, const std::string& token = "") {CURL* curl = curl_easy_init();std::string responseBody;long httpCode = 0;if (!curl) {throw std::runtime_error("Gagal inisialisasi curl");}std::string url = "https://worldcup26.ir" + endpoint;struct curl_slist* headers = nullptr;headers = curl_slist_append(headers, "Accept: application/json");if (!token.empty()) {std::string authHeader = "Authorization: Bearer " + token;headers = curl_slist_append(headers, authHeader.c_str());}curl_easy_setopt(curl, CURLOPT_URL, url.c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBody);curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);CURLcode res = curl_easy_perform(curl);curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);curl_slist_free_all(headers);curl_easy_cleanup(curl);if (res != CURLE_OK) {throw std::runtime_error(std::string("cURL error: ") + curl_easy_strerror(res));}if (httpCode != 200) {throw std::runtime_error("HTTP error " + std::to_string(httpCode) + ": " + responseBody);}return responseBody;}int main() {curl_global_init(CURL_GLOBAL_DEFAULT);try {// Ambil data pertandinganstd::string matchesRaw = fetchData("/get/games");json matchesData = json::parse(matchesRaw);json games = matchesData.contains("games") ? matchesData["games"] : matchesData;int limit = std::min<int>(10, games.size());for (int i = 0; i < limit; ++i) {auto& g = games[i];std::string home = g.value("home_team_name_en", g.value("home_team_id", ""));std::string away = g.value("away_team_name_en", g.value("away_team_id", ""));std::cout << home << " vs " << away<< " — Grup " << g.value("group", "")<< " — " << g.value("local_date", "") << "\n";}// Ambil data grupstd::string groupsRaw = fetchData("/get/groups");json groups = json::parse(groupsRaw);std::cout << groups.dump(2) << std::endl;} catch (const std::exception& e) {std::cerr << "Gagal mengambil data: " << e.what() << std::endl;}curl_global_cleanup();return 0;}
Atau jika ingin menggunakan CMake, tambahkan di CMakeLists.txt [Opsional] :
find_package(CURL REQUIRED)find_package(nlohmann_json REQUIRED)add_executable(worldcup_client worldcup_client.cpp)target_link_libraries(worldcup_client PRIVATE CURL::libcurl nlohmann_json::nlohmann_json)
Compile :
g++ worldcup_client.cpp -o worldcup_client -lcurl ./worldcup_client
Hasil Output :
25. C
Contoh pakai libcurl murni (tanpa C++ wrapper) untuk request HTTP, dan cJSON — library JSON parser yang ringan dan populer untuk C.
Instalasi (Linux Ubuntu/Debian) :
sudo apt-get install libcurl4-openssl-dev # cJSON: clone & build dari https://github.com/DaveGamble/cJSON, atau install via package manager kalau tersedia sudo apt-get install libcjson-dev
Nama File : worldcup_client.c
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <curl/curl.h>#include <cjson/cJSON.h>struct MemoryBuffer {char* data;size_t size;};// Callback untuk menampung response body dari libcurlstatic size_t write_callback(void* contents, size_t size, size_t nmemb, void* userp) {size_t realSize = size * nmemb;struct MemoryBuffer* mem = (struct MemoryBuffer*)userp;char* ptr = realloc(mem->data, mem->size + realSize + 1);if (!ptr) {fprintf(stderr, "Gagal alokasi memori\n");return 0;}mem->data = ptr;memcpy(&(mem->data[mem->size]), contents, realSize);mem->size += realSize;mem->data[mem->size] = 0;return realSize;}char* fetch_data(const char* endpoint, const char* token, long* http_code_out) {CURL* curl = curl_easy_init();if (!curl) {fprintf(stderr, "Gagal inisialisasi curl\n");return NULL;}char url[256];snprintf(url, sizeof(url), "https://worldcup26.ir%s", endpoint);struct MemoryBuffer chunk = { .data = malloc(1), .size = 0 };struct curl_slist* headers = NULL;headers = curl_slist_append(headers, "Accept: application/json");char authHeader[512];if (token != NULL && strlen(token) > 0) {snprintf(authHeader, sizeof(authHeader), "Authorization: Bearer %s", token);headers = curl_slist_append(headers, authHeader);}curl_easy_setopt(curl, CURLOPT_URL, url);curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&chunk);curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);CURLcode res = curl_easy_perform(curl);curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, http_code_out);curl_slist_free_all(headers);curl_easy_cleanup(curl);if (res != CURLE_OK) {fprintf(stderr, "cURL error: %s\n", curl_easy_strerror(res));free(chunk.data);return NULL;}return chunk.data; // caller wajib free() setelah selesai pakai}int main(void) {curl_global_init(CURL_GLOBAL_DEFAULT);long httpCode = 0;char* matchesRaw = fetch_data("/get/games", NULL, &httpCode);if (matchesRaw == NULL || httpCode != 200) {fprintf(stderr, "Gagal mengambil data pertandingan (HTTP %ld)\n", httpCode);free(matchesRaw);curl_global_cleanup();return 1;}cJSON* root = cJSON_Parse(matchesRaw);cJSON* games = cJSON_GetObjectItemCaseSensitive(root, "games");if (!cJSON_IsArray(games)) {games = root; // fallback kalau respons langsung array}int limit = cJSON_GetArraySize(games) < 10 ? cJSON_GetArraySize(games) : 10;for (int i = 0; i < limit; i++) {cJSON* g = cJSON_GetArrayItem(games, i);cJSON* homeName = cJSON_GetObjectItemCaseSensitive(g, "home_team_name_en");cJSON* awayName = cJSON_GetObjectItemCaseSensitive(g, "away_team_name_en");cJSON* group = cJSON_GetObjectItemCaseSensitive(g, "group");cJSON* date = cJSON_GetObjectItemCaseSensitive(g, "local_date");printf("%s vs %s — Grup %s — %s\n",cJSON_IsString(homeName) ? homeName->valuestring : "?",cJSON_IsString(awayName) ? awayName->valuestring : "?",cJSON_IsString(group) ? group->valuestring : "?",cJSON_IsString(date) ? date->valuestring : "?");}cJSON_Delete(root);free(matchesRaw);curl_global_cleanup();return 0;}
Compile :
gcc worldcup_client.c -o worldcup_client -lcurl -lcjson ./worldcup_client
Hasil Output :
E. Tips Umum untuk Semua Stack
Berikut ini adalah Tips Umum untuk Semua Stack yang harus kalian ketahui :
1. Selalu handle error & timeout
API pihak ketiga bisa down, lambat, atau kena rate limit (kode 429).
2. Cache di sisi aplikasi Anda (30–60 detik)
Untuk mengurangi beban ke API dan menghindari rate limit, terutama untuk trafik tinggi.
3. Jangan Hardcode token di kode frontend (JS/React/Flutter)
Yang bisa dilihat publik — simpan token di server (Laravel .env, Next.js Route Handler, Flask/Django/FastAPI backend) lalu buat endpoint proxy sendiri.
4. Cek dulu struktur JSON Asli
Dengan curl https://worldcup26.ir/get/games atau Postman sebelum menulis parsing logic, karena field bisa bertambah/berubah seiring waktu (misal saat turnamen berlangsung field skor & status akan ter-update).
5. Field type pada data pertandingan menunjukkan tahap turnamen: group, r32, r16, qf, sf, third, final — berguna untuk memfilter tampilan bracket knockout.
6. Untuk pertandingan knockout yang timnya belum ditentukan, field home_team_id/away_team_id bisa bernilai "0" dan tim ditampilkan lewat home_team_label/away_team_label (misal "Winner Group A").
Untuk lebih lengkap dan jelasnya mengenai Piala Dunia 2026, silahkan lihat di sini. Atau jika ingin melihat Jadwal Piala Dunia FIFA 2026 di Inzaghi's Blog, silakan lihat di sini.
Itulah Integrasi API Piala Dunia FIFA 2026 Untuk di Beberapa Aplikasi, seperti Web, Mobile, hingga Pemrograman biasa. Mohon maaf apabila ada kesalahan apapun.
Terima Kasih 😄😘👌👍 :)
Wassalamu‘alaikum wr. wb.
