Memperkenalkan API Berbasis Komponen
Diterbitkan: 2022-03-10Artikel ini diperbarui pada 31 Januari 2019 untuk menanggapi tanggapan pembaca. Penulis telah menambahkan kemampuan kueri kustom ke API berbasis komponen dan menjelaskan cara kerjanya .
API adalah saluran komunikasi aplikasi untuk memuat data dari server. Di dunia API, REST telah menjadi metodologi yang lebih mapan, tetapi akhir-akhir ini dibayangi oleh GraphQL, yang menawarkan keunggulan penting dibandingkan REST. Sementara REST memerlukan beberapa permintaan HTTP untuk mengambil sekumpulan data untuk merender komponen, GraphQL dapat melakukan kueri dan mengambil data tersebut dalam satu permintaan, dan responsnya akan persis seperti yang diperlukan, tanpa pengambilan data yang berlebihan atau kurang seperti yang biasanya terjadi di ISTIRAHAT.
Dalam artikel ini, saya akan menjelaskan cara lain untuk mengambil data yang telah saya rancang dan sebut "PoP" (dan bersumber terbuka di sini), yang memperluas gagasan mengambil data untuk beberapa entitas dalam satu permintaan yang diperkenalkan oleh GraphQL dan mengambilnya melangkah lebih jauh, yaitu saat REST mengambil data untuk satu sumber daya, dan GraphQL mengambil data untuk semua sumber daya dalam satu komponen, API berbasis komponen dapat mengambil data untuk semua sumber daya dari semua komponen dalam satu halaman.
Menggunakan API berbasis komponen paling masuk akal ketika situs web itu sendiri dibangun menggunakan komponen, yaitu ketika halaman web secara iteratif terdiri dari komponen yang membungkus komponen lain sampai, di bagian paling atas, kita mendapatkan satu komponen yang mewakili halaman. Misalnya, halaman web yang ditunjukkan pada gambar di bawah ini dibuat dengan komponen, yang digariskan dengan kotak:
API berbasis komponen mampu membuat permintaan tunggal ke server dengan meminta data untuk semua sumber daya di setiap komponen (serta untuk semua komponen di halaman) yang dilakukan dengan menjaga hubungan antar komponen di struktur API itu sendiri.
Antara lain, struktur ini menawarkan beberapa manfaat berikut:
- Halaman dengan banyak komponen hanya akan memicu satu permintaan, bukan banyak;
- Data yang dibagikan di seluruh komponen hanya dapat diambil sekali dari DB dan dicetak hanya sekali dalam respons;
- Ini dapat sangat mengurangi — bahkan menghapus sepenuhnya — kebutuhan akan penyimpanan data.
Kita akan menjelajahi ini secara mendetail di seluruh artikel, tetapi pertama-tama, mari kita jelajahi apa sebenarnya komponen itu dan bagaimana kita bisa membangun situs berdasarkan komponen tersebut, dan terakhir, jelajahi cara kerja API berbasis komponen.
Bacaan yang direkomendasikan : A GraphQL Primer: Mengapa Kami Membutuhkan Jenis API Baru
Membangun Situs Melalui Komponen
Komponen hanyalah sekumpulan potongan kode HTML, JavaScript, dan CSS yang disatukan untuk membuat entitas otonom. Ini kemudian dapat membungkus komponen lain untuk membuat struktur yang lebih kompleks, dan itu sendiri dibungkus oleh komponen lain juga. Komponen memiliki tujuan, yang dapat berkisar dari sesuatu yang sangat mendasar (seperti tautan atau tombol) hingga sesuatu yang sangat rumit (seperti korsel atau pengunggah gambar seret-dan-lepas). Komponen paling berguna ketika mereka generik dan memungkinkan penyesuaian melalui properti yang disuntikkan (atau "alat peraga"), sehingga mereka dapat melayani beragam kasus penggunaan. Dalam kasus terbaik, situs itu sendiri menjadi sebuah komponen.
Istilah "komponen" sering digunakan untuk merujuk pada fungsionalitas dan desain. Misalnya, mengenai fungsionalitas, kerangka kerja JavaScript seperti React atau Vue memungkinkan untuk membuat komponen sisi klien, yang dapat merender sendiri (misalnya, setelah API mengambil data yang diperlukan), dan menggunakan props untuk menetapkan nilai konfigurasi pada komponen yang dibungkus, memungkinkan penggunaan kembali kode. Mengenai desain, Bootstrap telah menstandarisasi bagaimana tampilan dan nuansa situs web melalui pustaka komponen front-endnya, dan telah menjadi tren yang sehat bagi tim untuk membuat sistem desain untuk memelihara situs web mereka, yang memungkinkan anggota tim yang berbeda (desainer dan pengembang, tetapi juga pemasar dan penjual) untuk berbicara dalam satu bahasa dan mengekspresikan identitas yang konsisten.
Komponen situs kemudian adalah cara yang sangat masuk akal untuk membuat situs web menjadi lebih terawat. Situs yang menggunakan kerangka kerja JavaScript seperti React dan Vue sudah berbasis komponen (setidaknya di sisi klien). Menggunakan pustaka komponen seperti Bootstrap tidak serta merta membuat situs menjadi berbasis komponen (bisa berupa gumpalan besar HTML), namun, ia menggabungkan konsep elemen yang dapat digunakan kembali untuk antarmuka pengguna.
Jika situs adalah gumpalan besar HTML, bagi kita untuk mengkomponenkannya, kita harus memecah tata letak menjadi serangkaian pola berulang, yang untuk itu kita harus mengidentifikasi dan membuat katalog bagian pada halaman berdasarkan kesamaan fungsionalitas dan gaya, dan memecahnya bagian-bagian menjadi lapisan-lapisan, sedetail mungkin, berusaha agar setiap lapisan difokuskan pada satu tujuan atau tindakan, dan juga mencoba mencocokkan lapisan-lapisan umum di seluruh bagian yang berbeda.
Catatan : "Desain Atom" Brad Frost adalah metodologi yang bagus untuk mengidentifikasi pola-pola umum ini dan membangun sistem desain yang dapat digunakan kembali.
Oleh karena itu, membangun situs melalui komponen mirip dengan bermain dengan LEGO. Setiap komponen dapat berupa fungsionalitas atomik, komposisi komponen lain, atau kombinasi keduanya.
Seperti yang ditunjukkan di bawah ini, komponen dasar (avatar) secara iteratif disusun oleh komponen lain hingga mendapatkan halaman web di bagian atas:
Spesifikasi API Berbasis Komponen
Untuk API berbasis komponen yang saya rancang, komponen disebut "modul", jadi mulai sekarang istilah "komponen" dan "modul" digunakan secara bergantian.
Hubungan semua modul yang membungkus satu sama lain, dari modul paling atas sampai ke tingkat terakhir, disebut "hierarki komponen". Hubungan ini dapat diekspresikan melalui array asosiatif (array properti kunci =>) di sisi server, di mana setiap modul menyatakan namanya sebagai atribut kunci dan modul dalamnya di bawah properti modules
. API kemudian cukup mengkodekan array ini sebagai objek JSON untuk konsumsi:
// Component hierarchy on server-side, eg through PHP: [ "top-module" => [ "modules" => [ "module-level1" => [ "modules" => [ "module-level11" => [ "modules" => [...] ], "module-level12" => [ "modules" => [ "module-level121" => [ "modules" => [...] ] ] ] ] ], "module-level2" => [ "modules" => [ "module-level21" => [ "modules" => [...] ] ] ] ] ] ] // Component hierarchy encoded as JSON: { "top-module": { modules: { "module-level1": { modules: { "module-level11": { ... }, "module-level12": { modules: { "module-level121": { ... } } } } }, "module-level2": { modules: { "module-level21": { ... } } } } } }
Hubungan antar modul didefinisikan secara ketat dari atas ke bawah: modul membungkus modul lain dan mengetahui siapa mereka, tetapi tidak tahu — dan tidak peduli — modul mana yang membungkusnya.
Misalnya, dalam kode JSON di atas, module module-level1
mengetahui bahwa ia membungkus modules module-level11
dan module-level12
, dan, secara transitif, ia juga mengetahui bahwa ia membungkus module-level121
; tetapi module module-level11
tidak peduli siapa yang membungkusnya, akibatnya tidak mengetahui module-level1
.
Memiliki struktur berbasis komponen, sekarang kita dapat menambahkan informasi aktual yang diperlukan oleh setiap modul, yang dikategorikan ke dalam pengaturan (seperti nilai konfigurasi dan properti lainnya) dan data (seperti ID objek database yang ditanyakan dan properti lainnya) , dan ditempatkan sesuai di bawah entri modulesettings
dan moduledata
:
{ modulesettings: { "top-module": { configuration: {...}, ..., modules: { "module-level1": { configuration: {...}, ..., modules: { "module-level11": { repeat... }, "module-level12": { configuration: {...}, ..., modules: { "module-level121": { repeat... } } } } }, "module-level2": { configuration: {...}, ..., modules: { "module-level21": { repeat... } } } } } }, moduledata: { "top-module": { dbobjectids: [...], ..., modules: { "module-level1": { dbobjectids: [...], ..., modules: { "module-level11": { repeat... }, "module-level12": { dbobjectids: [...], ..., modules: { "module-level121": { repeat... } } } } }, "module-level2": { dbobjectids: [...], ..., modules: { "module-level21": { repeat... } } } } } } }
Selanjutnya, API akan menambahkan data objek database. Informasi ini tidak ditempatkan di bawah setiap modul, tetapi di bawah bagian bersama yang disebut databases
, untuk menghindari duplikasi informasi ketika dua atau lebih modul yang berbeda mengambil objek yang sama dari database.
Selain itu, API mewakili data objek database secara relasional, untuk menghindari duplikasi informasi ketika dua atau lebih objek database yang berbeda terkait dengan objek yang sama (seperti dua posting yang memiliki penulis yang sama). Dengan kata lain, data objek database dinormalisasi.
Bacaan yang disarankan : Membuat Formulir Kontak Tanpa Server Untuk Situs Statis Anda
Strukturnya adalah kamus, diatur di bawah setiap jenis objek pertama dan ID objek kedua, dari mana kita dapat memperoleh properti objek:
{ databases: { primary: { dbobject_type: { dbobject_id: { property: ..., ... }, ... }, ... } } }
Objek JSON ini sudah menjadi respons dari API berbasis komponen. Formatnya adalah spesifikasi dengan sendirinya: Selama server mengembalikan respons JSON dalam format yang diperlukan, klien dapat menggunakan API secara independen dari cara penerapannya. Oleh karena itu, API dapat diimplementasikan pada bahasa apa pun (yang merupakan salah satu keindahan GraphQL: menjadi spesifikasi dan bukan implementasi aktual telah memungkinkannya tersedia dalam berbagai bahasa.)
Catatan : Dalam artikel yang akan datang, saya akan menjelaskan implementasi saya dari API berbasis komponen di PHP (yang tersedia di repo).
Contoh respons API
Misalnya, respons API di bawah ini berisi hierarki komponen dengan dua modul, page
=> post-feed
, di mana modul post-feed
mengambil entri blog. Harap perhatikan hal-hal berikut:
- Setiap modul mengetahui objek yang ditanyakan dari properti
dbobjectids
(ID4
dan9
untuk posting blog) - Setiap modul mengetahui jenis objek untuk objek yang ditanyakannya dari properti
dbkeys
(data setiap posting ditemukan di bawahposts
, dan data penulis posting, sesuai dengan penulis dengan ID yang diberikan di bawahauthor
properti posting, ditemukan di bawahusers
) - Karena data objek database bersifat relasional,
author
properti berisi ID ke objek penulis alih-alih mencetak data penulis secara langsung.
{ moduledata: { "page": { modules: { "post-feed": { dbobjectids: [4, 9] } } } }, modulesettings: { "page": { modules: { "post-feed": { dbkeys: { id: "posts", author: "users" } } } } }, databases: { primary: { posts: { 4: { title: "Hello World!", author: 7 }, 9: { title: "Everything fine?", author: 7 } }, users: { 7: { name: "Leo" } } } } }
Perbedaan Mengambil Data Dari API Berbasis Sumber Daya, Berbasis Skema, dan Berbasis Komponen
Mari kita lihat bagaimana API berbasis komponen seperti PoP dibandingkan, saat mengambil data, dengan API berbasis sumber daya seperti REST, dan dengan API berbasis skema seperti GraphQL.
Katakanlah IMDB memiliki halaman dengan dua komponen yang perlu mengambil data: "Sutradara unggulan" (menampilkan deskripsi George Lucas dan daftar filmnya) dan "Film yang direkomendasikan untuk Anda" (menampilkan film seperti Star Wars: Episode I — Ancaman Phantom dan Terminator ). Ini bisa terlihat seperti ini:
Mari kita lihat berapa banyak permintaan yang diperlukan untuk mengambil data melalui setiap metode API. Untuk contoh ini, komponen "Sutradara unggulan" membawa satu hasil ("George Lucas"), dari mana komponen tersebut mengambil dua film ( Star Wars: Episode I — The Phantom Menace dan Star Wars: Episode II — Attack of the Clones ), dan untuk setiap film dua aktor ("Ewan McGregor" dan "Natalie Portman" untuk film pertama, dan "Natalie Portman" dan "Hayden Christensen" untuk film kedua). Komponen "Film yang direkomendasikan untuk Anda" membawa dua hasil ( Star Wars: Episode I — The Phantom Menace dan The Terminator ), dan kemudian mengambil sutradara mereka ("George Lucas" dan "James Cameron" masing-masing).
Menggunakan REST untuk merender component featured-director
, kita mungkin memerlukan 7 permintaan berikut (jumlah ini dapat bervariasi tergantung pada seberapa banyak data yang disediakan oleh setiap endpoint, yaitu seberapa banyak over-fetching yang telah diterapkan):
GET - /featured-director GET - /directors/george-lucas GET - /films/the-phantom-menace GET - /films/attack-of-the-clones GET - /actors/ewan-mcgregor GET - /actors/natalie-portman GET - /actors/hayden-christensen
GraphQL memungkinkan, melalui skema yang diketik dengan kuat, untuk mengambil semua data yang diperlukan dalam satu permintaan tunggal per komponen. Kueri untuk mengambil data melalui featuredDirector
untuk komponen featuresDirector terlihat seperti ini (setelah kami mengimplementasikan skema yang sesuai):
query { featuredDirector { name country avatar films { title thumbnail actors { name avatar } } } }
Dan itu menghasilkan respons berikut:
{ data: { featuredDirector: { name: "George Lucas", country: "USA", avatar: "...", films: [ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "...", actors: [ { name: "Ewan McGregor", avatar: "...", }, { name: "Natalie Portman", avatar: "...", } ] }, { title: "Star Wars: Episode II - Attack of the Clones", thumbnail: "...", actors: [ { name: "Natalie Portman", avatar: "...", }, { name: "Hayden Christensen", avatar: "...", } ] } ] } } }
Dan menanyakan komponen "Film yang direkomendasikan untuk Anda" menghasilkan respons berikut:
{ data: { films: [ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "...", director: { name: "George Lucas", avatar: "...", } }, { title: "The Terminator", thumbnail: "...", director: { name: "James Cameron", avatar: "...", } } ] } }
PoP hanya akan mengeluarkan satu permintaan untuk mengambil semua data untuk semua komponen di halaman, dan menormalkan hasilnya. Titik akhir yang akan dipanggil sama dengan URL yang kita perlukan untuk mendapatkan datanya, cukup tambahkan parameter tambahan output=json
untuk menunjukkan membawa data dalam format JSON alih-alih mencetaknya sebagai HTML:
GET - /url-of-the-page/?output=json
Dengan asumsi bahwa struktur modul memiliki page
bernama modul teratas yang berisi modul-modul featured-director
dan films-recommended-for-you
, dan ini juga memiliki submodul, seperti ini:
"page" modules "featured-director" modules "director-films" modules "film-actors" "films-recommended-for-you" modules "film-director"
Respons JSON tunggal yang dikembalikan akan terlihat seperti ini:
{ modulesettings: { "page": { modules: { "featured-director": { dbkeys: { id: "people", }, modules: { "director-films": { dbkeys: { films: "films" }, modules: { "film-actors": { dbkeys: { actors: "people" }, } } } } }, "films-recommended-for-you": { dbkeys: { id: "films", }, modules: { "film-director": { dbkeys: { director: "people" }, } } } } } }, moduledata: { "page": { modules: { "featured-director": { dbobjectids: [1] }, "films-recommended-for-you": { dbobjectids: [1, 3] } } } }, databases: { primary: { people { 1: { name: "George Lucas", country: "USA", avatar: "..." films: [1, 2] }, 2: { name: "Ewan McGregor", avatar: "..." }, 3: { name: "Natalie Portman", avatar: "..." }, 4: { name: "Hayden Christensen", avatar: "..." }, 5: { name: "James Cameron", avatar: "..." }, }, films: { 1: { title: "Star Wars: Episode I - The Phantom Menace", actors: [2, 3], director: 1, thumbnail: "..." }, 2: { title: "Star Wars: Episode II - Attack of the Clones", actors: [3, 4], thumbnail: "..." }, 3: { title: "The Terminator", director: 5, thumbnail: "..." }, } } } }
Mari kita analisis bagaimana ketiga metode ini dibandingkan satu sama lain, dalam hal kecepatan dan jumlah data yang diambil.
Kecepatan
Melalui REST, harus mengambil 7 permintaan hanya untuk merender satu komponen bisa sangat lambat, sebagian besar pada koneksi data seluler dan goyah. Oleh karena itu, lompatan dari REST ke GraphQL mewakili banyak kecepatan, karena kami dapat merender komponen hanya dengan satu permintaan.
PoP, karena dapat mengambil semua data untuk banyak komponen dalam satu permintaan, akan lebih cepat untuk merender banyak komponen sekaligus; Namun, kemungkinan besar tidak perlu untuk ini. Membuat komponen dirender secara berurutan (seperti yang muncul di halaman), sudah merupakan praktik yang baik, dan untuk komponen yang muncul di bawah flip tentu tidak perlu terburu-buru untuk merendernya. Oleh karena itu, baik API berbasis skema dan berbasis komponen sudah cukup bagus dan jelas lebih unggul daripada API berbasis sumber daya.
Jumlah Data
Pada setiap permintaan, data dalam respons GraphQL dapat diduplikasi: aktris "Natalie Portman" diambil dua kali dalam respons dari komponen pertama, dan ketika mempertimbangkan keluaran gabungan untuk dua komponen, kami juga dapat menemukan data bersama, seperti film Star Wars: Episode I — Ancaman Hantu .
PoP, di sisi lain, menormalkan data database dan mencetaknya hanya sekali, namun, ia membawa overhead pencetakan struktur modul. Oleh karena itu, tergantung pada permintaan tertentu yang memiliki data duplikat atau tidak, baik API berbasis skema atau API berbasis komponen akan memiliki ukuran yang lebih kecil.
Kesimpulannya, API berbasis skema seperti GraphQL dan API berbasis komponen seperti PoP sama baiknya dalam hal kinerja, dan lebih unggul dari API berbasis sumber daya seperti REST.
Bacaan yang disarankan : Memahami Dan Menggunakan REST API
Properti Khusus API Berbasis Komponen
Jika API berbasis komponen belum tentu lebih baik dalam hal kinerja daripada API berbasis skema, Anda mungkin bertanya-tanya, lalu apa yang saya coba capai dengan artikel ini?
Di bagian ini, saya akan mencoba meyakinkan Anda bahwa API semacam itu memiliki potensi luar biasa, menyediakan beberapa fitur yang sangat diinginkan, menjadikannya pesaing serius di dunia API. Saya menjelaskan dan mendemonstrasikan setiap fitur hebatnya yang unik di bawah ini.
Data Yang Akan Diambil Dari Basis Data Dapat Disimpulkan Dari Hirarki Komponen
Ketika modul menampilkan properti dari objek DB, modul mungkin tidak tahu, atau peduli, objek apa itu; yang penting adalah mendefinisikan properti apa dari objek yang dimuat yang diperlukan.
Sebagai contoh, perhatikan gambar di bawah ini. Modul memuat objek dari database (dalam hal ini, satu posting), dan kemudian modul turunannya akan menampilkan properti tertentu dari objek, seperti title
dan content
:
Oleh karena itu, di sepanjang hierarki komponen, modul "pemuatan data" akan bertanggung jawab untuk memuat objek yang ditanyakan (modul memuat satu posting, dalam hal ini), dan modul turunannya akan menentukan properti apa dari objek DB yang diperlukan ( title
dan content
, dalam hal ini).
Mengambil semua properti yang diperlukan untuk objek DB dapat dilakukan secara otomatis dengan melintasi hierarki komponen: mulai dari modul pemuatan data, kami mengulangi semua modul turunannya hingga mencapai modul pemuatan data baru, atau hingga akhir hierarki; di setiap level kami memperoleh semua properti yang diperlukan, lalu menggabungkan semua properti bersama-sama dan menanyakannya dari database, semuanya hanya sekali.
Dalam struktur di bawah ini, modul single-post
mengambil hasil dari DB (postingan dengan ID 37), dan submodul post-title
dan post-content
mendefinisikan properti yang akan dimuat untuk objek DB yang ditanyakan (masing-masing title
dan content
); submodul post-layout
dan fetch-next-post-button
tidak memerlukan bidang data apa pun.
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "fetch-next-post-button"
Kueri yang akan dieksekusi dihitung secara otomatis dari hierarki komponen dan bidang data yang diperlukan, yang berisi semua properti yang diperlukan oleh semua modul dan submodulnya:
SELECT title, content FROM posts WHERE id = 37
Dengan mengambil properti untuk diambil langsung dari modul, kueri akan diperbarui secara otomatis setiap kali hierarki komponen berubah. Jika, misalnya, kami kemudian menambahkan submodule post-thumbnail
, yang membutuhkan data field thumbnail
:
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "post-thumbnail" => Load property "thumbnail" "fetch-next-post-button"
Kemudian kueri diperbarui secara otomatis untuk mengambil properti tambahan:
SELECT title, content, thumbnail FROM posts WHERE id = 37
Karena kami telah menetapkan data objek database untuk diambil secara relasional, kami juga dapat menerapkan strategi ini di antara hubungan antara objek database itu sendiri.
Perhatikan gambar di bawah ini: Mulai dari post
jenis objek dan turun ke hierarki komponen, kita perlu menggeser jenis objek DB ke user
dan comment
, sesuai dengan penulis pos dan masing-masing komentar pos, dan kemudian, untuk masing-masing komentar, itu harus mengubah jenis objek sekali lagi menjadi user
yang sesuai dengan penulis komentar.
Pindah dari objek database ke objek relasional (mungkin mengubah jenis objek, seperti pada post
=> author
beralih dari post
ke user
, atau tidak, seperti pada author
=> followers beralih dari user
ke user
) adalah apa yang saya sebut “switching domains ”.
Setelah beralih ke domain baru, dari tingkat itu di hierarki komponen ke bawah, semua properti yang diperlukan akan tunduk pada domain baru:
-
name
diambil dari objekuser
(mewakili penulis posting), -
content
diambil dari objekcomment
(mewakili setiap komentar kiriman), -
name
diambil dari objekuser
(mewakili penulis setiap komentar).
Melintasi hierarki komponen, API mengetahui saat beralih ke domain baru dan, dengan tepat, memperbarui kueri untuk mengambil objek relasional.
Misalnya, jika kita perlu menampilkan data dari penulis postingan, susun submodule post-author
akan mengubah domain pada level tersebut dari post
ke user
yang sesuai, dan dari level ini ke bawah objek DB yang dimuat ke dalam konteks yang diteruskan ke modul adalah pengguna. Kemudian, submodul user-name
user-avatar
di bawah post-author
akan memuat name
properti dan avatar
di bawah objek user
:
"single-post" => Load objects with object type "post" and ID 37 modules "post-layout" modules "post-title" => Load property "title" "post-content" => Load property "content" "post-author" => Switch domain from "post" to "user", based on property "author" modules "user-layout" modules "user-name" => Load property "name" "user-avatar" => Load property "avatar" "fetch-next-post-button"
Menghasilkan kueri berikut:
SELECT p.title, p.content, p.author, u.name, u.avatar FROM posts p INNER JOIN users u WHERE p.id = 37 AND p.author = u.id
Singkatnya, dengan mengonfigurasi setiap modul dengan tepat, tidak perlu menulis kueri untuk mengambil data untuk API berbasis komponen. Kueri secara otomatis dihasilkan dari struktur hierarki komponen itu sendiri, memperoleh objek apa yang harus dimuat oleh modul pemuatan data, bidang yang akan diambil untuk setiap objek yang dimuat yang ditentukan pada setiap modul turunan, dan pengalihan domain yang ditentukan pada setiap modul turunan.
Menambah, menghapus, mengganti, atau mengubah modul apa pun akan secara otomatis memperbarui kueri. Setelah menjalankan kueri, data yang diambil akan persis seperti yang diperlukan — tidak lebih atau kurang.
Mengamati Data Dan Menghitung Properti Tambahan
Mulai dari modul pemuatan data hingga hierarki komponen, modul apa pun dapat mengamati hasil yang dikembalikan dan menghitung item data tambahan berdasarkan itu, atau nilai feedback
, yang ditempatkan di bawah entri moduledata
.
Misalnya, modul fetch-next-post-button
dapat menambahkan properti yang menunjukkan apakah ada lebih banyak hasil untuk diambil atau tidak (berdasarkan nilai umpan balik ini, jika tidak ada lebih banyak hasil, tombol akan dinonaktifkan atau disembunyikan):
{ moduledata: { "page": { modules: { "single-post": { modules: { "fetch-next-post-button": { feedback: { hasMoreResults: true } } } } } } } }
Pengetahuan Tersirat Tentang Data yang Diperlukan Mengurangi Kompleksitas Dan Membuat Konsep "Titik Akhir" Menjadi Usang
Seperti yang ditunjukkan di atas, API berbasis komponen dapat mengambil dengan tepat data yang diperlukan, karena memiliki model semua komponen di server dan bidang data apa yang diperlukan oleh setiap komponen. Kemudian, itu dapat membuat pengetahuan tentang bidang data yang diperlukan implisit.
Keuntungannya adalah mendefinisikan data apa yang diperlukan oleh komponen dapat diperbarui hanya di sisi server, tanpa harus menyebarkan kembali file JavaScript, dan klien dapat dibuat bodoh, hanya meminta server untuk menyediakan data apa pun yang dibutuhkannya. , sehingga mengurangi kompleksitas aplikasi sisi klien.
Selain itu, memanggil API untuk mengambil data untuk semua komponen untuk URL tertentu dapat dilakukan hanya dengan menanyakan URL tersebut plus menambahkan parameter tambahan output=json
untuk menunjukkan data API yang dikembalikan alih-alih mencetak halaman. Oleh karena itu, URL menjadi titik akhirnya sendiri atau, dianggap dengan cara yang berbeda, konsep "titik akhir" menjadi usang.
Mengambil Subset Data: Data Dapat Diambil Untuk Modul Tertentu, Ditemukan Pada Setiap Tingkat Hirarki Komponen
Apa yang terjadi jika kita tidak perlu mengambil data untuk semua modul dalam satu halaman, tetapi hanya data untuk modul tertentu yang dimulai dari tingkat hierarki komponen mana pun? Misalnya, jika sebuah modul mengimplementasikan gulir tak terbatas, saat menggulir ke bawah, kita harus mengambil hanya data baru untuk modul ini, dan bukan untuk modul lain di halaman.
Ini dapat dicapai dengan memfilter cabang hierarki komponen yang akan disertakan dalam respons, untuk menyertakan properti hanya mulai dari modul yang ditentukan dan mengabaikan semua yang ada di atas level ini. Dalam implementasi saya (yang akan saya jelaskan di artikel mendatang), pemfilteran diaktifkan dengan menambahkan parameter modulefilter=modulepaths
ke URL, dan modul yang dipilih (atau modul) ditunjukkan melalui parameter modulepaths[]
, di mana "jalur modul ” adalah daftar modul mulai dari modul paling atas hingga modul tertentu (mis. module1
=> module2
=> module3
memiliki jalur modul [ module1
, module2
, module3
] dan diteruskan sebagai parameter URL sebagai module1.module2.module3
) .
Misalnya, dalam hierarki komponen di bawah setiap modul memiliki entri dbobjectids
:
"module1" dbobjectids: [...] modules "module2" dbobjectids: [...] modules "module3" dbobjectids: [...] "module4" dbobjectids: [...] "module5" dbobjectids: [...] modules "module6" dbobjectids: [...]
Kemudian meminta URL halaman web menambahkan parameter modulefilter=modulepaths
dan modulepaths[]=module1.module2.module5
akan menghasilkan respons berikut:
"module1" modules "module2" modules "module5" dbobjectids: [...] modules "module6" dbobjectids: [...]
Intinya, API mulai memuat data mulai dari module1
=> module2
=> module5
. Itu sebabnya module6
, yang berada di bawah module5
, juga membawa datanya sementara module3
dan module4
tidak.
Selain itu, kita dapat membuat filter modul khusus untuk menyertakan set modul yang telah diatur sebelumnya. Misalnya, memanggil halaman dengan modulefilter=userstate
hanya dapat mencetak modul yang memerlukan status pengguna untuk merendernya di klien, seperti modules module3
dan module6
:
"module1" modules "module2" modules "module3" dbobjectids: [...] "module5" modules "module6" dbobjectids: [...]
Informasi yang merupakan modul awal berada di bawah bagian requestmeta
, di bawah entri filteredmodules
, sebagai larik jalur modul:
requestmeta: { filteredmodules: [ ["module1", "module2", "module3"], ["module1", "module2", "module5", "module6"] ] }
Fitur ini memungkinkan untuk mengimplementasikan Aplikasi Satu Halaman yang tidak rumit, di mana bingkai situs dimuat pada permintaan awal:
"page" modules "navigation-top" dbobjectids: [...] "navigation-side" dbobjectids: [...] "page-content" dbobjectids: [...]
Namun, dari sana, kita dapat menambahkan parameter modulefilter=page
ke semua URL yang diminta, memfilter bingkai dan hanya membawa konten halaman:
"page" modules "navigation-top" "navigation-side" "page-content" dbobjectids: [...]
Mirip dengan filter modul userstate
dan page
yang dijelaskan di atas, kami dapat menerapkan filter modul khusus apa pun dan menciptakan pengalaman pengguna yang kaya.
Modul Adalah API Sendiri
Seperti yang ditunjukkan di atas, kita dapat memfilter respons API untuk mengambil data mulai dari modul apa pun. Akibatnya, setiap modul dapat berinteraksi dengan dirinya sendiri dari klien ke server hanya dengan menambahkan jalur modulnya ke URL halaman web yang telah disertakan.
Saya harap Anda akan memaafkan kegembiraan saya yang berlebihan, tetapi saya benar-benar tidak dapat cukup menekankan betapa indahnya fitur ini. Saat membuat komponen, kita tidak perlu membuat API untuk mengikutinya mengambil data (REST, GraphQL, atau apa pun), karena komponen sudah dapat berbicara dengan dirinya sendiri di server dan memuatnya sendiri data — sepenuhnya otonom dan melayani diri sendiri .
Setiap modul pemuatan data mengekspor URL untuk berinteraksi dengannya di bawah entri dataloadsource
dari bawah bagian datasetmodulemeta
:
{ datasetmodulemeta: { "module1": { modules: { "module2": { modules: { "module5": { meta: { dataloadsource: "https://page-url/?modulefilter=modulepaths&modulepaths[]=module1.module2.module5" }, modules: { "module6": { meta: { dataloadsource: "https://page-url/?modulefilter=modulepaths&modulepaths[]=module1.module2.module5.module6" } } } } } } } } } }
Pengambilan Data Dipisahkan di Seluruh Modul Dan KERING
Untuk membuat poin saya bahwa mengambil data dalam API berbasis komponen sangat dipisahkan dan KERING ( Jangan ulangi sendiri), pertama-tama saya harus menunjukkan bagaimana dalam API berbasis skema seperti GraphQL itu kurang dipisahkan dan tidak kering.
Di GraphQL, kueri untuk mengambil data harus menunjukkan bidang data untuk komponen, yang mungkin menyertakan subkomponen, dan ini mungkin juga menyertakan subkomponen, dan seterusnya. Kemudian, komponen paling atas perlu mengetahui data apa yang dibutuhkan oleh setiap subkomponennya juga, untuk mengambil data tersebut.
Misalnya, merender komponen <FeaturedDirector>
mungkin memerlukan subkomponen berikut:
Render <FeaturedDirector>: <div> Country: {country} {foreach films as film} <Film film={film} /> {/foreach} </div> Render <Film>: <div> Title: {title} Pic: {thumbnail} {foreach actors as actor} <Actor actor={actor} /> {/foreach} </div> Render <Actor>: <div> Name: {name} Photo: {avatar} </div>
Dalam skenario ini, kueri GraphQL diterapkan pada tingkat <FeaturedDirector>
. Kemudian, jika subkomponen <Film>
diperbarui, meminta judul melalui properti filmTitle
alih-alih title
, kueri dari komponen <FeaturedDirector>
juga perlu diperbarui, untuk mencerminkan informasi baru ini (GraphQL memiliki mekanisme versi yang dapat menangani dengan masalah ini, tetapi cepat atau lambat kami harus tetap memperbarui informasi). Ini menghasilkan kerumitan pemeliharaan, yang mungkin sulit ditangani ketika komponen dalam sering berubah atau diproduksi oleh pengembang pihak ketiga. Oleh karena itu, komponen tidak sepenuhnya dipisahkan satu sama lain.
Demikian pula, kita mungkin ingin merender secara langsung komponen <Film>
untuk beberapa film tertentu, yang kemudian kita juga harus mengimplementasikan kueri GraphQL pada tingkat ini, untuk mengambil data untuk film dan aktornya, yang menambahkan kode redundan: bagian dari kueri yang sama akan hidup pada tingkat yang berbeda dari struktur komponen. Jadi GraphQL tidak KERING .
Karena API berbasis komponen sudah mengetahui bagaimana komponennya membungkus satu sama lain dalam strukturnya sendiri, maka masalah ini dapat dihindari sepenuhnya. Pertama, klien dapat dengan mudah meminta data yang diperlukan yang dibutuhkannya, apa pun datanya; if a subcomponent data field changes, the overall model already knows and adapts immediately, without having to modify the query for the parent component in the client. Therefore, the modules are highly decoupled from each other.
For another, we can fetch data starting from any module path, and it will always return the exact required data starting from that level; there are no duplicated queries whatsoever, or even queries to start with. Hence, a component-based API is fully DRY . (This is another feature that really excites me and makes me get wet.)
(Yes, pun fully intended. Sorry about that.)
Retrieving Configuration Values In Addition To Database Data
Let's revisit the example of the featured-director
component for the IMDB site described above, which was created — you guessed it! — with Bootstrap. Instead of hardcoding the Bootstrap classnames or other properties such as the title's HTML tag or the avatar max width inside of JavaScript files (whether they are fixed inside the component, or set through props by parent components), each module can set these as configuration values through the API, so that then these can be directly updated on the server and without the need to redeploy JavaScript files. Similarly, we can pass strings (such as the title Featured director
) which can be already translated/internationalized on the server-side, avoiding the need to deploy locale configuration files to the front-end.
Similar to fetching data, by traversing the component hierarchy, the API is able to deliver the required configuration values for each module and nothing more or less.
The configuration values for the featured-director
component might look like this:
{ modulesettings: { "page": { modules: { "featured-director": { configuration: { class: "alert alert-info", title: "Featured director", titletag: "h3" }, modules: { "director-films": { configuration: { classes: { wrapper: "media", avatar: "mr-3", body: "media-body", films: "row", film: "col-sm-6" }, avatarmaxsize: "100px" }, modules: { "film-actors": { configuration: { classes: { wrapper: "card", image: "card-img-top", body: "card-body", title: "card-title", avatar: "img-thumbnail" } } } } } } } } } } }
Please notice how — because the configuration properties for different modules are nested under each module's level — these will never collide with each other if having the same name (eg property classes
from one module will not override property classes
from another module), avoiding having to add namespaces for modules.
Higher Degree Of Modularity Achieved In The Application
According to Wikipedia, modularity means:
The degree to which a system's components may be separated and recombined, often with the benefit of flexibility and variety in use. The concept of modularity is used primarily to reduce complexity by breaking a system into varying degrees of interdependence and independence across and 'hide the complexity of each part behind an abstraction and interface'.
Being able to update a component just from the server-side, without the need to redeploy JavaScript files, has the consequence of better reusability and maintenance of components. I will demonstrate this by re-imagining how this example coded for React would fare in a component-based API.
Let's say that we have a <ShareOnSocialMedia>
component, currently with two items: <FacebookShare>
and <TwitterShare>
, like this:
Render <ShareOnSocialMedia>: <ul> <li>Share on Facebook: <FacebookShare url={window.location.href} /></li> <li>Share on Twitter: <TwitterShare url={window.location.href} /></li> </ul>
But then Instagram got kind of cool, so we need to add an item <InstagramShare>
to our <ShareOnSocialMedia>
component, too:
Render <ShareOnSocialMedia>: <ul> <li>Share on Facebook: <FacebookShare url={window.location.href} /></li> <li>Share on Twitter: <TwitterShare url={window.location.href} /></li> <li>Share on Instagram: <InstagramShare url={window.location.href} /></li> </ul>
In the React implementation, as it can be seen in the linked code, adding a new component <InstagramShare>
under component <ShareOnSocialMedia>
forces to redeploy the JavaScript file for the latter one, so then these two modules are not as decoupled as they could be.
Namun, dalam API berbasis komponen, kita dapat dengan mudah menggunakan hubungan antar modul yang telah dijelaskan di API untuk menggabungkan modul bersama-sama. Meskipun awalnya kami akan memiliki respons ini:
{ modulesettings: { "share-on-social-media": { modules: { "facebook-share": { configuration: {...} }, "twitter-share": { configuration: {...} } } } } }
Setelah menambahkan Instagram, kami akan mendapatkan respons yang ditingkatkan:
{ modulesettings: { "share-on-social-media": { modules: { "facebook-share": { configuration: {...} }, "twitter-share": { configuration: {...} }, "instagram-share": { configuration: {...} } } } } }
Dan hanya dengan mengulangi semua nilai di bawah modulesettings["share-on-social-media"].modules
, komponen <ShareOnSocialMedia>
dapat ditingkatkan untuk menampilkan komponen <InstagramShare>
tanpa perlu menerapkan ulang file JavaScript apa pun. Oleh karena itu, API mendukung penambahan dan penghapusan modul tanpa mengorbankan kode dari modul lain, sehingga mencapai tingkat modularitas yang lebih tinggi.
Cache/Penyimpanan Data Sisi Klien Asli
Data database yang diambil dinormalisasi dalam struktur kamus, dan distandarisasi sehingga, mulai dari nilai pada dbobjectids
, setiap bagian data di bawah databases
dapat dicapai hanya dengan mengikuti jalurnya seperti yang ditunjukkan melalui entri dbkeys
, dengan cara apa pun itu terstruktur . Oleh karena itu, logika untuk mengatur data sudah bawaan dari API itu sendiri.
Kita bisa mendapatkan keuntungan dari situasi ini dalam beberapa cara. Misalnya, data yang dikembalikan untuk setiap permintaan dapat ditambahkan ke cache sisi klien yang berisi semua data yang diminta oleh pengguna sepanjang sesi. Oleh karena itu, adalah mungkin untuk menghindari penambahan penyimpanan data eksternal seperti Redux ke aplikasi (maksud saya mengenai penanganan data, bukan mengenai fitur lain seperti Undo/Redo, lingkungan kolaboratif atau debugging perjalanan waktu).
Selain itu, struktur berbasis komponen mempromosikan caching: hierarki komponen tidak bergantung pada URL, tetapi pada komponen apa yang diperlukan dalam URL tersebut. Dengan cara ini, dua peristiwa di bawah /events/1/
dan /events/2/
akan berbagi hierarki komponen yang sama, dan informasi tentang modul apa yang diperlukan dapat digunakan kembali di antara mereka. Akibatnya, semua properti (selain data basis data) dapat di-cache pada klien setelah mengambil peristiwa pertama dan digunakan kembali sejak saat itu, sehingga hanya data basis data untuk setiap peristiwa berikutnya yang harus diambil dan tidak ada yang lain.
Ekstensibilitas Dan Tujuan Ulang
Bagian databases
dari API dapat diperluas, memungkinkan untuk mengkategorikan informasinya ke dalam subbagian yang disesuaikan. Secara default, semua data objek database ditempatkan di bawah entri primary
, namun, kami juga dapat membuat entri khusus tempat menempatkan properti objek DB tertentu.
Misalnya, jika komponen "Film yang direkomendasikan untuk Anda" yang dijelaskan sebelumnya pada menunjukkan daftar teman pengguna yang masuk yang telah menonton film ini di bawah properti friendsWhoWatchedFilm
pada objek DB film
, karena nilai ini akan berubah tergantung pada log-in pengguna kemudian kami menyimpan properti ini di bawah entri status userstate
, jadi ketika pengguna keluar, kami hanya menghapus cabang ini dari database yang di-cache pada klien, tetapi semua data primary
tetap ada:
{ databases: { userstate: { films: { 5: { friendsWhoWatchedFilm: [22, 45] }, } }, primary: { films: { 5: { title: "The Terminator" }, } "people": { 22: { name: "Peter", }, 45: { name: "John", }, }, } } }
Selain itu, hingga titik tertentu, struktur respons API dapat digunakan kembali. Secara khusus, hasil database dapat dicetak dalam struktur data yang berbeda, seperti array alih-alih kamus default.
Misalnya, jika jenis objek hanya satu (misalnya films
), itu dapat diformat sebagai larik untuk dimasukkan langsung ke komponen typeahead:
[ { title: "Star Wars: Episode I - The Phantom Menace", thumbnail: "..." }, { title: "Star Wars: Episode II - Attack of the Clones", thumbnail: "..." }, { title: "The Terminator", thumbnail: "..." }, ]
Dukungan Untuk Pemrograman Berorientasi Aspek
Selain mengambil data, API berbasis komponen juga dapat memposting data, seperti untuk membuat postingan atau menambahkan komentar, dan menjalankan segala jenis operasi, seperti login atau keluar pengguna, mengirim email, login, analitik, dan seterusnya. Tidak ada batasan: fungsionalitas apa pun yang disediakan oleh CMS yang mendasarinya dapat dipanggil melalui modul — di level mana pun.
Sepanjang hierarki komponen, kita dapat menambahkan sejumlah modul, dan setiap modul dapat menjalankan operasinya sendiri. Oleh karena itu, tidak semua operasi harus selalu terkait dengan tindakan yang diharapkan dari permintaan, seperti ketika melakukan operasi POST, PUT atau DELETE di REST atau mengirim mutasi di GraphQL, tetapi dapat ditambahkan untuk menyediakan fungsionalitas tambahan, seperti mengirim email ke admin ketika pengguna membuat posting baru.
Jadi, dengan mendefinisikan hierarki komponen melalui injeksi dependensi atau file konfigurasi, API dapat dikatakan mendukung pemrograman berorientasi Aspek, “paradigma pemrograman yang bertujuan untuk meningkatkan modularitas dengan memungkinkan pemisahan masalah lintas sektoral.”
Bacaan yang disarankan : Melindungi Situs Anda Dengan Kebijakan Fitur
Keamanan yang Ditingkatkan
Nama-nama modul tidak harus tetap ketika dicetak dalam output, tetapi dapat dipersingkat, dihancurkan, diubah secara acak atau (singkatnya) dibuat variabel dengan cara apa pun yang dimaksudkan. Meskipun awalnya berpikir untuk memperpendek output API (sehingga nama modul carousel-featured-posts
atau drag-and-drop-user-images
dapat disingkat menjadi notasi 64 dasar, seperti a1
, a2
dan seterusnya, untuk lingkungan produksi ), fitur ini memungkinkan untuk sering mengubah nama modul dalam respons dari API untuk alasan keamanan.
Misalnya, nama input secara default dinamai sebagai modul yang sesuai; kemudian, modul yang disebut username
dan password
, yang akan dirender di klien sebagai <input type="text" name="{input_name}">
dan <input type="password" name="{input_name}">
masing-masing, dapat disetel berbagai nilai acak untuk nama inputnya (seperti zwH8DSeG
dan QBG7m6EF
hari ini, dan c3oMLBjo
dan c46oVgN6
besok) sehingga mempersulit spammer dan bot untuk menargetkan situs.
Keserbagunaan Melalui Model Alternatif
Penyusunan modul memungkinkan untuk bercabang ke modul lain untuk menambahkan kompatibilitas untuk media atau teknologi tertentu, atau mengubah beberapa gaya atau fungsionalitas, dan kemudian kembali ke cabang asli.
Misalnya, katakanlah halaman web memiliki struktur berikut:
"module1" modules "module2" modules "module3" "module4" modules "module5" modules "module6"
Dalam hal ini, kami ingin membuat situs web juga berfungsi untuk AMP, namun modul module2
, module4
dan module5
tidak kompatibel dengan AMP. Kami dapat membagi modul ini menjadi modul serupa yang kompatibel dengan AMP module2AMP
, module4AMP
dan module5AMP
, setelah itu kami terus memuat hierarki komponen asli, jadi hanya tiga modul ini yang diganti (dan tidak ada yang lain):
"module1" modules "module2AMP" modules "module3" "module4AMP" modules "module5AMP" modules "module6"
Ini membuatnya cukup mudah untuk menghasilkan keluaran yang berbeda dari basis kode tunggal, menambahkan garpu hanya di sana-sini sesuai kebutuhan, dan selalu dicakup dan dibatasi ke modul individual.
Waktu Demonstrasi
Kode yang mengimplementasikan API seperti yang dijelaskan dalam artikel ini tersedia di repositori sumber terbuka ini.
Saya telah menggunakan PoP API di bawah https://nextapi.getpop.org
untuk tujuan demonstrasi. Situs web berjalan di WordPress, jadi permalink URL adalah khas WordPress. Seperti disebutkan sebelumnya, dengan menambahkan parameter output=json
ke dalamnya, URL ini menjadi titik akhir API mereka sendiri.
Situs ini didukung oleh database yang sama dari situs web Demo PoP, sehingga visualisasi hierarki komponen dan data yang diambil dapat dilakukan dengan menanyakan URL yang sama di situs web lain ini (misalnya mengunjungi https://demo.getpop.org/u/leo/
menjelaskan data dari https://nextapi.getpop.org/u/leo/?output=json
).
Tautan di bawah menunjukkan API untuk kasus yang dijelaskan sebelumnya tentang:
- Beranda, satu posting, seorang penulis, daftar posting dan daftar pengguna.
- Sebuah acara, penyaringan dari modul tertentu.
- Sebuah tag, modul pemfilteran yang memerlukan status pengguna dan pemfilteran untuk membawa hanya satu halaman dari Aplikasi Satu Halaman.
- Array lokasi, untuk dimasukkan ke dalam typeahead.
- Model alternatif untuk halaman “Siapa kita”: Normal, Dapat Dicetak, Dapat Disematkan.
- Mengubah nama modul: asli vs hancur.
- Penyaringan informasi: hanya pengaturan modul, data modul ditambah data database.
Kesimpulan
API yang baik adalah batu loncatan untuk membuat aplikasi yang andal, mudah dirawat, dan kuat. Dalam artikel ini, saya telah menjelaskan konsep yang mendukung API berbasis komponen yang, menurut saya, adalah API yang cukup bagus, dan saya harap saya juga meyakinkan Anda.
Sejauh ini, desain dan implementasi API telah melibatkan beberapa iterasi dan memakan waktu lebih dari lima tahun — dan itu belum sepenuhnya siap. Namun, itu dalam kondisi yang cukup baik, tidak siap untuk produksi tetapi sebagai alpha yang stabil. Hari-hari ini, saya masih mengerjakannya; bekerja untuk mendefinisikan spesifikasi terbuka, mengimplementasikan lapisan tambahan (seperti rendering) dan menulis dokumentasi.
Dalam artikel yang akan datang, saya akan menjelaskan cara kerja implementasi API saya. Sampai saat itu, jika Anda memiliki pemikiran tentang itu — terlepas dari apakah positif atau negatif — saya akan senang membaca komentar Anda di bawah.
Pembaruan (31 Jan): Kemampuan Kueri Kustom
Alain Schlesser berkomentar bahwa API yang tidak dapat dibuat kueri khusus dari klien tidak berharga, membawa kita kembali ke SOAP, karena itu tidak dapat bersaing dengan REST atau GraphQL. Setelah memberikan komentarnya selama beberapa hari, saya harus mengakui bahwa dia benar. Namun, alih-alih mengabaikan API berbasis Komponen sebagai upaya yang bermaksud baik-tapi-tidak-cukup-ada-belum, saya melakukan sesuatu yang jauh lebih baik: saya harus menerapkan kemampuan kueri khusus untuknya. Dan itu bekerja seperti pesona!
Di tautan berikut, data untuk sumber daya atau kumpulan sumber daya diambil seperti yang biasanya dilakukan melalui REST. Namun, melalui fields
parameter, kami juga dapat menentukan data spesifik apa yang akan diambil untuk setiap sumber daya, menghindari pengambilan data yang berlebihan atau kurang:
- Satu posting dan kumpulan posting menambahkan
fields=title,content,datetime
- Pengguna dan kumpulan pengguna menambahkan
fields=name,username,description
Tautan di atas menunjukkan pengambilan data hanya untuk sumber daya yang ditanyakan. Bagaimana dengan hubungan mereka? Misalnya, katakanlah kita ingin mengambil daftar posting dengan bidang "title"
dan "content"
, komentar setiap pos dengan bidang "content"
dan "date"
, dan penulis setiap komentar dengan bidang "name"
dan "url"
. Untuk mencapai ini di GraphQL kami akan menerapkan kueri berikut:
query { post { title content comments { content date author { name url } } } }
Untuk implementasi API berbasis komponen, saya telah menerjemahkan kueri ke dalam ekspresi "sintaks titik" yang sesuai, yang kemudian dapat diberikan melalui fields
parameter . Menanyakan pada sumber daya "posting", nilai ini adalah:
fields=title,content,comments.content,comments.date,comments.author.name,comments.author.url
Atau bisa disederhanakan, menggunakan |
untuk mengelompokkan semua bidang yang diterapkan ke sumber daya yang sama:
fields=title|content,comments.content|date,comments.author.name|url
Saat menjalankan kueri ini pada satu pos, kami memperoleh data yang diperlukan secara tepat untuk semua sumber daya yang terlibat:
{ "datasetmodulesettings": { "dataload-dataquery-singlepost-fields": { "dbkeys": { "id": "posts", "comments": "comments", "comments.author": "users" } } }, "datasetmoduledata": { "dataload-dataquery-singlepost-fields": { "dbobjectids": [ 23691 ] } }, "databases": { "posts": { "23691": { "id": 23691, "title": "A lovely tango", "content": "<div class=\"responsiveembed-container\"><iframe loading="lazy" width=\"480\" height=\"270\" src=\"https:\\/\\/www.youtube.com\\/embed\\/sxm3Xyutc1s?feature=oembed\" frameborder=\"0\" allowfullscreen><\\/iframe><\\/div>\n", "comments": [ "25094", "25164" ] } }, "comments": { "25094": { "id": "25094", "content": "<p><a class=\"hashtagger-tag\" href=\"https:\\/\\/newapi.getpop.org\\/tags\\/videos\\/\">#videos<\\/a>\\u00a0<a class=\"hashtagger-tag\" href=\"https:\\/\\/newapi.getpop.org\\/tags\\/tango\\/\">#tango<\\/a><\\/p>\n", "date": "4 Aug 2016", "author": "851" }, "25164": { "id": "25164", "content": "<p>fjlasdjf;dlsfjdfsj<\\/p>\n", "date": "19 Jun 2017", "author": "1924" } }, "users": { "851": { "id": 851, "name": "Leonardo Losoviz", "url": "https:\\/\\/newapi.getpop.org\\/u\\/leo\\/" }, "1924": { "id": 1924, "name": "leo2", "url": "https:\\/\\/newapi.getpop.org\\/u\\/leo2\\/" } } } }
Oleh karena itu, kita dapat melakukan kueri sumber daya dengan cara REST, dan menentukan kueri berbasis skema dalam mode GraphQL, dan kita akan memperoleh persis apa yang diperlukan, tanpa data yang berlebihan atau kurang, dan menormalkan data dalam database sehingga tidak ada data yang diduplikasi. Menguntungkan, kueri dapat menyertakan sejumlah hubungan, bersarang jauh di lubuk hati, dan ini diselesaikan dengan waktu kompleksitas linier: kasus terburuk O(n+m), di mana n adalah jumlah node yang beralih domain (dalam kasus ini 2: comments
dan comments.author
) dan m adalah jumlah hasil yang diambil (dalam hal ini 5: 1 posting + 2 komentar + 2 pengguna), dan kasus rata-rata O(n). (Ini lebih efisien daripada GraphQL, yang memiliki waktu kompleksitas polinomial O(n^c) dan mengalami peningkatan waktu eksekusi saat kedalaman level meningkat).
Terakhir, API ini juga dapat menerapkan pengubah saat meminta data, misalnya untuk memfilter sumber daya apa yang diambil, seperti yang dapat dilakukan melalui GraphQL. Untuk mencapai ini, API hanya duduk di atas aplikasi dan dapat dengan mudah menggunakan fungsinya, sehingga tidak perlu menemukan kembali roda. Misalnya, menambahkan parameter filter=posts&searchfor=internet
akan memfilter semua posting yang berisi "internet"
dari kumpulan posting.
Implementasi fitur baru ini akan dijelaskan pada artikel yang akan datang.