D
P
0

Next.js

Fear & Greed Index Nyangkut di 72 Padahal API Bilang 26: Placeholder Hardcoded Menutupi Outage

16 Juli 2026·3 menit baca
Fear & Greed Index Nyangkut di 72 Padahal API Bilang 26: Placeholder Hardcoded Menutupi Outage

Klien mengirim satu screenshot ke saya. Widget Fear & Greed Index di situs berita crypto yang saya kerjakan untuknya menunjukkan 72, label "Greed", jarum gauge duduk manis di zona serakah. Di bawahnya ada screenshot kedua, langsung dari alternative.me: 26, "Fear". Itu bukan selisih tipis. Satu angka bilang pasar sedang serakah, angka satunya bilang pasar sedang ketakutan. Dan angka yang salah itu sudah tayang berjam-jam, mungkin berhari-hari, tanpa ada satu pun yang sadar. Tidak ada error di halaman, tidak ada alert, tidak ada log merah. Situsnya terlihat sehat. Justru itu bagian paling buruknya.

Jejak yang menyesatkan

Langkah pertama saya standar: verifikasi hulunya dulu. Saya panggil endpoint-nya langsung dari terminal:

curl "https://api.alternative.me/fng/?limit=1"

API-nya hidup dan menjawab 26. Jadi data benar di hulu, salah di hilir. Kecurigaan kedua jatuh ke cache. Widget ini membaca dari Upstash Redis dengan TTL satu jam, jadi wajar kalau angkanya telat sedikit. Tapi telat satu jam tidak menjelaskan 72 lawan 26 yang bertahan jauh lebih lama dari itu. Cache basi hanya bisa menyajikan angka lama yang dulunya pernah benar. Masalahnya, 72 ini bukan angka yang pernah benar dalam rentang waktu itu.

Di situ rasa janggalnya mulai kebaca: 72 itu terlalu rapi. Bukan NaN, bukan 0, bukan widget kosong. Angka yang masuk akal, lengkap dengan label klasifikasi yang cocok. Kalau sebuah nilai salah tapi terlihat meyakinkan, kecurigaan saya pindah dari data ke kode.

Root cause: fallback yang mengarang angka

Saya buka lib/api/fear-greed.ts dan menemukannya dalam hitungan detik:

const PLACEHOLDER: FearGreedIndex = {
  value: 72,
  classification: "Greed",
  // ...field lain diisi seadanya
};

Fungsi fetch-nya mengembalikan PLACEHOLDER ini pada kegagalan apa pun, termasuk saat cache-nya basi. API timeout? 72. Response bentuknya aneh? 72. alternative.me sedang outage total? Tetap 72, label "Greed", tampil percaya diri seolah baru diambil detik itu juga.

Niat awalnya mungkin mulia: jangan biarkan widget kosong. Praktiknya: setiap alternative.me bermasalah, situs menampilkan angka fiksi yang plausible. Dan karena hasilnya ikut menempel di cache dengan TTL satu jam, angka fiksi itu bertahan lama bahkan setelah insiden di hulunya selesai.

Ini kelas bug yang paling saya hindari: bukan yang bikin crash, tapi yang gagal secara diam-diam. Error tertelan rapi, monitoring tidak melihat apa-apa karena secara teknis tidak ada yang gagal, dan satu-satunya sistem deteksi yang tersisa adalah klien yang kebetulan membandingkan situsnya sendiri dengan sumber aslinya.

Perbaikannya: jujur saat gagal

Perbaikannya tiga lapis. Pertama, kontrak fungsinya diubah. fetchFearGreedIndex() sekarang mengembalikan FearGreedIndex | null, dan null artinya gagal, titik:

export async function fetchFearGreedIndex(): Promise<FearGreedIndex | null> {
  const res = await fetch("https://api.alternative.me/fng/?limit=1")
    .catch(() => null);
  if (!res || !res.ok) return null; // gagal ya gagal, jangan mengarang
  // parse dan validasi seperti biasa
}

Kedua, UI-nya berhenti pura-pura. Kalau datanya null, yang tampil adalah keterangan eksplisit, bukan angka karangan:

{fng ? (
  <FearGreedGauge value={fng.value} bare />
) : (
  <div>Index temporarily unavailable.</div>
)}

Ketiga, lapisan cache-nya dibereskan. TTL turun dari satu jam ke lima menit supaya jendela data basinya mengecil. Lalu cache key-nya di-bump dari fearGreed:latest ke fearGreed:latest:v2, supaya nilai palsu yang terlanjur tersimpan langsung tersingkir saat deploy, tanpa menunggu TTL habis dan tanpa flush manual.

Setelah tiga perubahan itu naik, gauge kembali sinkron dengan alternative.me. Dan kalau suatu saat API-nya tumbang lagi, situs akan bilang jujur bahwa indeksnya sedang tidak tersedia, bukan mengarang cerita bahwa pasar sedang serakah.

Pelajaran

  • Fallback yang menampilkan data palsu lebih berbahaya daripada error state. Widget kosong itu jelek; angka bohong itu merusak kepercayaan.
  • Kembalikan null saat gagal dan biarkan UI menampilkan kondisi tidak tersedia secara eksplisit. Gagal yang terlihat bisa diperbaiki, gagal yang tersembunyi tidak.
  • Cache memperpanjang umur nilai salah. Saat memperbaiki data yang tercemar, turunkan TTL dan bump cache key supaya nilai lama langsung tersingkir.
  • Bug diam-diam hampir tidak mungkin ketahuan dari dalam. Sesekali bandingkan angka di situs dengan sumber aslinya, karena screenshot dari klien bukan sistem monitoring yang bisa diandalkan.