Strategi Praktis untuk Rate Limiting API GraphQL
January 4, 2024
Menerapkan pembatasan kecepatan (rate limiting) pada REST API relatif mudah, karena kita biasanya menggunakan jalur URI untuk mewakili sumber daya API tertentu dan metode HTTP untuk menunjukkan operasi pada sumber daya tersebut. Lapisan proxy dapat dengan mudah menerapkan aturan pembatasan kecepatan yang telah ditentukan berdasarkan informasi ini.
Namun, skenario menjadi jauh lebih kompleks ketika menyangkut GraphQL API. Mari kita selami bagaimana mengatasi tantangan ini.
Skenario Sederhana
Berbeda dengan REST API, GraphQL menggunakan bahasa kueri khusus. Ia tidak lagi mengandalkan jalur dan metode HTTP untuk pengambilan dan manipulasi sumber daya, tetapi menyatukan kueri data dan operasi di bawah Query dan Mutation, di mana Query mengambil data, dan mutation melakukan manipulasi data seperti membuat, memperbarui, dan menghapus.
GET /users GET /users/1 POST /users PUT /users/1 DELETE /users/1 query { users { fullName } } query { user(id: 1) { fullName } } mutation { createUser(user: { lastName: "Jack" }) { id, fullName } } mutation { updateUser (id: 1, update: { lastName: "Marry" }) { fullName } } mutation { deleteUser (id: 1) }
Contoh di atas menunjukkan pergeseran dalam metode kueri API. Tidak seperti REST, GraphQL menyerupai pemanggilan fungsi pada sumber daya, dengan meneruskan parameter input yang diperlukan, dan respons yang berisi data yang diminta. Selain perbedaan dalam metode kueri, GraphQL biasanya mengekspos API melalui satu endpoint tunggal (misalnya, /graphql), dengan kueri dan parameter input dikirim melalui body POST.
Pertimbangkan contoh berikut:
query { users { fullName } photos { url uploadAt } }
Dalam skenario ini, yang mensimulasikan beranda album, panggilan API ke endpoint /graphql mengkueri daftar pengguna dan foto secara bersamaan. Sekarang, pertimbangkan apakah strategi reverse proxy tradisional, yang dijalankan pada tingkat permintaan, masih berlaku untuk GraphQL API.
Jawabannya adalah tidak. Server reverse proxy tradisional tidak dapat menangani panggilan API GraphQL yang berisi kueri itu sendiri secara efektif, sehingga tidak mungkin untuk menerapkan kebijakan seperti pembatasan kecepatan. Untuk GraphQL API, granularitas "permintaan HTTP" tampak terlalu kasar.
Namun, API gateway, Apache APISIX memiliki dukungan bawaan untuk kemampuan GraphQL ke HTTP. Administrator dapat mengonfigurasi pernyataan kueri terlebih dahulu, memungkinkan klien untuk memanggilnya langsung melalui HTTP POST tanpa memahami detail GraphQL, hanya memberikan parameter input yang diperlukan. Ini tidak hanya meningkatkan keamanan tetapi juga memungkinkan penerapan kebijakan API HTTP dalam konteks ini.

Secara efektif, ini mengubah kueri GraphQL dinamis menjadi kueri statis berbasis pengetahuan yang disediakan oleh penyedia API, yang memiliki kelebihan dan kekurangan. Terkadang, kita mungkin tidak ingin mengorbankan fitur kueri dinamis dari GraphQL. Mari kita lanjutkan diskusi tentang skenario lainnya.
Skenario Kompleks
GraphQL menggunakan bahasa khususnya untuk pemodelan data dan deskripsi API, memungkinkan struktur data bersarang. Memperluas contoh sebelumnya:
query { photos(first: 10) { url uploadAt publisher { fullName avatar } comments(first: 10) { content sender { fullName avatar } } } // users... }
Dalam kasus ini, yang mensimulasikan pengambilan 10 foto pertama, termasuk penerbit untuk setiap foto dan 10 komentar pertama dengan pengirimnya, layanan backend harus menangani kueri yang melibatkan beberapa tabel database atau panggilan ke layanan mikro. Dalam skenario kueri bersarang seperti ini, seiring dengan peningkatan jumlah data dan tingkat bersarang, tekanan komputasi pada layanan backend dan database meningkat secara eksponensial.
Untuk mencegah kueri kompleks membebani layanan, kita mungkin ingin memeriksa dan memblokir kueri tersebut di lapisan proxy. Untuk menerapkan strategi ini, komponen proxy harus mengurai pernyataan kueri menjadi data terstruktur, melintasinya untuk mendapatkan bidang bersarang di setiap lapisan, dan mengikuti praktik umum GraphQL dengan menetapkan nilai kompleksitas ke bidang sebagai biaya kueri. Batasan global pada kompleksitas kueri secara keseluruhan kemudian dapat diterapkan. Untuk kueri di atas, dengan asumsi biaya 1 untuk setiap bidang individu:
10 * photo (url + uploadAt + publisher.fullName + publisher.avatar + 10 * comment (content + sender.fullName + sender.avatar)) 10 * (1 + 1 + 1 + 1 + 10 * (1 + 1 + 1)) = 340
Dengan total biaya kueri 340, tampaknya dapat diterima, dan kita dapat mengonfigurasi batasan biaya kueri API berdasarkan aturan seperti ini. Namun, jika klien jahat mencoba mengambil data untuk 100 foto dalam satu kueri, biaya kueri akan melonjak menjadi 3400, melebihi batasan yang telah ditentukan, dan mengakibatkan permintaan ditolak.
Selain membatasi biaya maksimum per kueri klien, batasan tambahan pada interval waktu, seperti mengizinkan klien total 2000 kueri per menit dan menolak kueri berlebih, dapat menggagalkan crawler jahat.
Untuk mengimplementasikan kemampuan seperti ini, komponen proxy harus mengurai dan menghitung biaya kueri. API7 Enterprise mendukung fitur ini, memungkinkan penguraian dinamis kueri GraphQL dan penerapan batasan kecepatan pengguna berdasarkan konfigurasi.
GraphQL API menghadapi tantangan di lapisan proxy, di mana reverse proxy tradisional kesulitan untuk secara efektif memahami dan menangani kompleksitas dan hubungan bersarang dalam pernyataan kueri GraphQL. Sebaliknya, teknologi API gateway terbukti sangat berharga dalam mengatasi tantangan ini, di mana API7 Enterprise dapat menjadi pilihan yang bagus.