Mengonsumsi API

curl atau Guzzle serta penanganan error respons.

Daftar isi

Mengonsumsi API berarti memanggil layanan pihak lain dari kode PHP sendiri, membaca status dan isi responsnya, lalu menangani kegagalannya secara eksplisit. Topik ini membangun client dengan ekstensi curl yang tersedia di PHP dan mengujinya terhadap server API jadwal piket yang ikut dibuat. Tiga kegagalan wajib ditangani, yaitu koneksi gagal, kode status bukan 200, dan body yang bukan JSON. Alternatif pustaka Guzzle dibahas singkat pada bagian akhir.

Server API jadwal untuk latihan#

Server latihan berikut melayani data jadwal piket dalam format JSON. Parameter mode=salah disediakan untuk menyimulasikan keadaan buruk, yaitu kode status 500 dengan isi yang bukan JSON, sehingga penanganan error client dapat diuji tanpa layanan luar.

php api-jadwal.php
<?php

declare(strict_types=1);

header('Content-Type: application/json; charset=utf-8');

$jadwal = [
    ['hari' => 'Senin', 'petugas' => 'Ali'],
    ['hari' => 'Rabu', 'petugas' => 'Citra'],
];

// mode=salah menyimulasikan API yang menjawab 500 dengan isi bukan JSON
if (($_GET['mode'] ?? '') === 'salah') {
    http_response_code(500);
    echo 'Terjadi kegagalan internal yang tidak dapat dipahami';
    return;
}

echo json_encode($jadwal, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), "\n";

Menjalankan berkas ini tanpa parameter mencetak jadwal sebagai JSON, dan isi itulah yang nantinya diminta client.

text Keluaran
[
    {
        "hari": "Senin",
        "petugas": "Ali"
    },
    {
        "hari": "Rabu",
        "petugas": "Citra"
    }
]

Pada bagian Menjalankan kedua jalur, berkas ini disajikan lewat server bawaan. Strukturnya sengaja sederhana karena fokus topik ini berada di sisi client, sedangkan pembangunan API lengkap sudah dibahas pada topik Membangun API JSON.

Client dengan curl#

Ekstensi curl bekerja melalui objek handle yang menampung satu koneksi beserta opsinya. Opsi CURLOPT_RETURNTRANSFER wajib disetel, karena tanpanya curl_exec() mencetak isi respons langsung ke keluaran dan mengembalikan true atau false saja. Dua opsi batas waktu disertakan sejak awal sebagai kebiasaan yang baik, dan penjelasan mendalamnya menjadi isi topik Disiplin Client HTTP. Client ini dijalankan oleh blok bash pada bagian Menjalankan kedua jalur, bukan langsung dengan php, karena permintaannya ditujukan ke server yang harus dinyalakan lebih dulu.

OpsiNilai pada contohPeran
CURLOPT_RETURNTRANSFERtrueIsi respons dikembalikan sebagai string, bukan dicetak langsung
CURLOPT_CONNECTTIMEOUT2Batas detik membuka koneksi
CURLOPT_TIMEOUT5Batas detik keseluruhan permintaan

Alamat layanan ditulis langsung pada kode demi kesederhanaan contoh. Pada proyek nyata, alamat dasar (base URL) dan kredensialnya ditempatkan pada variabel lingkungan atau berkas konfigurasi, sehingga alamat server uji dan server produksi dapat ditukar tanpa mengubah kode client.

php Berkas klien-jadwal.php
// tidak-dijalankan
// client dimuat lewat blok bash setelah server dinyalakan pada port 8150
<?php

declare(strict_types=1);

// alamat API: jalur normal, atau mode=salah untuk menunjukkan penanganan error
$mode = ($argv[1] ?? '') === 'salah' ? '?mode=salah' : '';
$alamat = 'http://localhost:8150/' . $mode;

$ch = curl_init($alamat);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 2,
    CURLOPT_TIMEOUT => 5,
]);
$isi = curl_exec($ch);
$kode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$nomorError = curl_errno($ch);
$pesanError = curl_error($ch);
curl_close($ch);

if ($isi === false) {
    echo "Permintaan gagal (error $nomorError): $pesanError", "\n";
    return;
}

if ($kode !== 200) {
    echo "API menjawab dengan kode status $kode", "\n";
    return;
}

try {
    $jadwal = json_decode($isi, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException) {
    echo "Isi respons bukan JSON yang valid", "\n";
    return;
}

foreach ($jadwal as $baris) {
    echo $baris['hari'], ': ', $baris['petugas'], "\n";
}

Pemeriksaan tersusun berlapis dari yang paling ringan. Kegagalan transport ditandai nilai kembalian false beserta curl_errno(), kegagalan logis ditandai kode status yang berbeda dari 200, dan isi yang tidak sah ditandai JsonException. Ketiga lapisan tersebut menyaring kegagalan yang berbeda sifatnya, sehingga satu if tunggal tidak cukup.

Data yang lolos ketiga lapisan tetap tidak boleh diperlakukan sebagai kebenaran. Respons dari layanan luar adalah masukan dari pihak ketiga, dan modul Keamanan menegaskan prinsip yang sama untuk data pengguna, yaitu tidak memercayai apa pun tanpa pemeriksaan. Struktur bidang yang diharapkan, misalnya keberadaan kunci hari dan petugas, sebaiknya divalidasi sebelum nilainya dipakai. Perubahan bentuk respons dari layanan luar tidak pernah diumumkan lebih dahulu.

Contoh pada topik ini memakai GET, dan permintaan yang mengirim data menambah dua opsi lagi, yaitu CURLOPT_POST bersama CURLOPT_POSTFIELDS untuk isi yang dikirim serta CURLOPT_HTTPHEADER untuk menandai Content-Type yang sesuai. Parameter pada URL dibangun dengan http_build_query() alih-alih disambung secara manual, karena fungsi itu sekaligus menangani pelolosan (escaping) karakter khusus pada setiap nilai.

Menjalankan kedua jalur#

Server dinyalakan sekali, lalu client dipanggil dua kali dengan argumen berbeda. Pemanggilan pertama mengambil jadwal, sedangkan pemanggilan kedua dengan argumen salah memicu respons 500 yang bukan JSON.

bash
# jalankan server API jadwal pada port 8150
php -S localhost:8150 api-jadwal.php >/dev/null 2>&1 &
sleep 1
# ambil jadwal melalui client
php klien-jadwal.php
# ulangi dengan mode=salah untuk melihat penanganan error
php klien-jadwal.php salah
kill %1
text Keluaran
Senin: Ali
Rabu: Citra
API menjawab dengan kode status 500

Pada jalur kedua, client berhenti pada lapisan status sebelum mencoba json_decode(), sehingga pesan yang muncul menjelaskan kode 500 dan bukan error parsing. Kombinasi ini menunjukkan urutan pemeriksaan yang tepat, yaitu transport lebih dulu, kemudian status, kemudian isi.

Guzzle sebagai alternatif#

Guzzle adalah pustaka HTTP yang paling banyak dipakai di ekosistem PHP dan biasanya dipasang lewat Composer. Client yang sama ditulis lebih ringkas, dan pengecualian RequestException menampung berbagai kegagalan sekaligus.

php
// tidak-dijalankan
// contoh membutuhkan guzzlehttp/guzzle yang dipasang lewat Composer, dan
// berkas vendor/ tidak dibawa oleh bundle topik ini
use GuzzleHttp\Client;

$client = new Client(['timeout' => 5]);
try {
    $respons = $client->get('http://localhost:8150/');
    $jadwal = json_decode((string) $respons->getBody(), true, 512, JSON_THROW_ON_ERROR);
} catch (RequestException $e) {
    echo "Permintaan gagal: ", $e->getMessage(), "\n";
}

Ekstensi curl dan Guzzle menyelesaikan pekerjaan yang sama, sehingga pemilihan biasanya ditentukan kebutuhan proyek. Proyek tanpa Composer cukup memakai curl yang sudah menyertai PHP, sedangkan proyek dengan Composer cenderung memilih Guzzle karena antarmukanya lebih ramah untuk deretan permintaan yang kompleks. Kedua pendekatan sama-sama menuntut disiplin yang dibahas pada topik Disiplin Client HTTP, yaitu batas waktu, percobaan ulang, dan validasi respons.

Kesalahan umum#

  • Lupa CURLOPT_RETURNTRANSFER => true. Isi respons tercetak langsung dan curl_exec() hanya mengembalikan boolean, sehingga data tidak pernah sampai ke variabel.
  • Membaca isi respons tanpa memeriksa kode status. Body dari respons 500 diproses seolah-olah data yang sah, dan kegagalan baru terlihat jauh kemudian dalam bentuk data kosong atau error parsing.
  • Memakai json_decode() tanpa pemeriksaan. Body HTML dari halaman error menghasilkan null tanpa pesan apa pun, dan perulangan atas null melempar error yang mengarahkan pencarian masalah ke tempat yang keliru.
  • Mengabaikan curl_errno() pada kegagalan transport. Pesan dari curl_error() menyebut penyebab persisnya, misalnya resolusi nama gagal atau batas waktu terlampaui, sehingga pencarian penyebab berlangsung cepat.

Ringkasan#

  • Client curl tersusun atas curl_init(), curl_setopt_array() dengan CURLOPT_RETURNTRANSFER, lalu pembacaan hasil melalui curl_exec(), curl_getinfo(), curl_errno(), dan curl_error().
  • Pemeriksaan berlapis mengurutkan kegagalan transport, kode status, dan validitas JSON, dan setiap lapisan menyaring jenis kegagalan yang berbeda.
  • Parameter mode=salah pada server latihan memungkinkan respons buruk diuji tanpa layanan luar, sehingga penanganan error terbukti lewat keluaran nyata.
  • Guzzle menyederhanakan kode client untuk proyek berbasis Composer, sedangkan curl tersedia tanpa pemasangan dan cukup untuk kebutuhan sehari-hari.

Referensi#