Menjalankan beberapa query secara bersamaan
Beberapa query dapat digabungkan dan dieksekusi sebagai satu operasi tunggal, dengan berbagi state dan datanya.
Ini berbeda dari query batching, di mana server GraphQL juga mengeksekusi beberapa query dalam satu permintaan, tetapi query-query tersebut hanya dieksekusi satu per satu secara berurutan, secara independen satu sama lain.
Fitur ini meningkatkan performa. Alih-alih mengeksekusi query secara independen dalam permintaan yang berbeda (sehingga kita pertama-tama mengeksekusi sebuah operasi terhadap server GraphQL, kemudian menunggu responsnya, dan kemudian menggunakan hasil tersebut untuk melakukan operasi lain), kita dapat mengeksekusinya bersama-sama, sehingga menghindari latensi dari beberapa permintaan.
Multiple Query Execution juga memungkinkan kita untuk mengorganisasi query GraphQL kita dengan lebih baik, memecahnya menjadi unit-unit logis yang saling bergantung, dan yang dieksekusi secara kondisional berdasarkan hasil dari operasi sebelumnya.
Cara menggunakan multiple query execution
Misalkan kita ingin mencari semua post yang menyebutkan nama pengguna yang sedang login. Biasanya, kita membutuhkan dua query untuk menyelesaikan ini:
Pertama kita mengambil name pengguna:
query GetLoggedInUserName {
me {
name
}
}...dan kemudian, setelah mengeksekusi query pertama, kita dapat meneruskan name pengguna yang diambil sebagai variabel $search untuk melakukan pencarian dalam query kedua:
query GetPostsContainingString($search: String!) {
posts(filter: { search: $search }) {
id
title
}
}Multiple Query Execution menyederhanakan proses ini, memungkinkan kita untuk mengambil semua data dan mengeksekusi semua logika yang diperlukan dalam satu permintaan:
query GetLoggedInUserName {
me {
name @export(as: "search")
}
}
query GetPostsContainingString @depends(on: "GetLoggedInUserName") {
posts(filter: { search: $search }) {
id
title
}
}Multiple Query Execution dicapai dengan penggunaan direktif-direktif khusus berikut:
@depends(direktif operasi): membuat sebuah operasi (baikquerymaupunmutation) menunjukkan operasi lain apa yang harus dieksekusi sebelumnya@export(direktif field): mengekspor nilai suatu field (atau kumpulan field) dari satu operasi, untuk diinjeksikan sebagai input ke suatu field di operasi lain@deferredExport(direktif field): Mirip dengan@exporttetapi digunakan dengan Multi-Field Directives.
Selain itu, direktif @include dan @skip juga tersedia sebagai direktif operasi (normalnya hanya sebagai direktif field), dan ini dapat digunakan untuk mengeksekusi operasi secara kondisional jika memenuhi suatu kondisi.
Server GraphQL akan membuat daftar operasi untuk dimuat dan dieksekusi, mengambilnya dari setiap @depends(on: ...), dan akan mengekspor nilai dari field mana pun yang mengandung @export sebagai variabel dinamis (dengan nama yang didefinisikan di bawah argumen as) untuk dijadikan input pada operasi berikutnya.
Dengan menggabungkan direktif-direktif ini, kita dapat memecah fungsionalitas kompleks apa pun menjadi langkah-langkah perantara, menyelang-nyeling operasi query dan mutation, menambahkan dependensinya dalam urutan yang diperlukan, dan mengeksekusi semuanya dalam satu permintaan dengan mendefinisikan operasi terluar di ?operationName=... (dalam contoh di atas, itu adalah ?operationName=GetPostsContainingString).
Mendefinisikan operasi yang dimuat dan dieksekusi melalui @depends
Ketika dokumen GraphQL mengandung beberapa operasi, kita menunjukkan ke server operasi mana yang akan dieksekusi melalui parameter URL ?operationName=...; jika tidak, operasi terakhir yang akan dieksekusi.
Dimulai dari operasi awal ini, server akan mengumpulkan semua operasi yang akan dieksekusi, yang didefinisikan dengan menambahkan direktif depends(on: [...]), dan mengeksekusinya dalam urutan yang sesuai dengan menghormati dependensinya.
Argumen direktif operations menerima sebuah array nama operasi ([String]), atau kita juga dapat memberikan satu nama operasi (String).
Dalam query ini, kita meneruskan ?operationName=Four, dan operasi yang dieksekusi (baik query maupun mutation) akan menjadi ["One", "Two", "Three", "Four"]:
mutation One {
# Do something ...
}
mutation Two {
# Do something ...
}
query Three @depends(on: ["One", "Two"]) {
# Do something ...
}
query Four @depends(on: "Three") {
# Do something ...
}Berbagi data antar query melalui @export
Direktif @export mengekspor nilai dari sebuah field (atau kumpulan field) ke dalam variabel dinamis, untuk digunakan sebagai input di suatu field dari query lain.
Misalnya, dalam query ini kita mengekspor nama pengguna yang sedang login, dan menggunakan nilai ini untuk mencari post yang mengandung string ini (perhatikan bahwa variabel $loggedInUserName, karena bersifat dinamis, tidak perlu didefinisikan dalam operasi FindPosts):
query GetLoggedInUserName {
me {
name @export(as: "loggedInUserName")
}
}
query FindPosts @depends(on: "GetLoggedInUserName") {
posts(filter: { search: $loggedInUserName }) {
id
}
}Output variabel dinamis
@export dapat menghasilkan 6 output yang berbeda, berdasarkan kombinasi dari:
- Nilai argumen
type(baikSINGLE,LISTmaupunDICTIONARY) - Apakah direktif diterapkan pada satu field, atau beberapa field (melalui modul Multi-Field Directives)
6 kemungkinan output tersebut adalah:
- Tipe
SINGLE:- Field tunggal
- Multi-field
- Tipe
LIST:- Field tunggal
- Multi-field
- Tipe
DICTIONARY:- Field tunggal
- Multi-field
Tipe SINGLE / Field tunggal
Output berupa nilai tunggal saat meneruskan parameter type: SINGLE (yang ditetapkan sebagai nilai default).
Dalam query ini:
query {
post(by: { id: 1 }) {
title @export(as: "postTitle", type: SINGLE)
}
}...variabel dinamis $postTitle akan memiliki nilai:
"Hello world!"Perhatikan bahwa jika SINGLE diterapkan pada sebuah array entitas, maka nilai untuk entitas terakhir adalah yang diekspor.
Dalam query ini:
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postTitle", type: SINGLE)
}
}...variabel dinamis $postTitle akan memiliki nilai untuk post dengan ID 5:
"Everything good?"Tipe SINGLE / Multi-field
Jika @export diterapkan pada beberapa field (dengan menambahkan parameter affectAdditionalFieldsUnderPos yang disediakan oleh modul Multi-Field Directives), maka nilai yang ditetapkan pada variabel dinamis adalah sebuah dictionary dari { key: alias field, value: nilai field } (bertipe JSONObject).
Query ini:
query {
post(by: { id: 1 }) {
title
content
@export(
as: "postData",
type: SINGLE,
affectAdditionalFieldsUnderPos: [1]
)
}
}...mengekspor variabel dinamis $postData dengan nilai:
{
"title": "Hello world!",
"content": "Lorem ipsum."
}Tipe LIST / Field tunggal
Variabel dinamis akan berisi sebuah array dengan nilai field dari semua entitas yang di-query (dari field yang melingkupinya), dengan meneruskan parameter type: LIST.
Saat menjalankan query ini (di mana entitas yang di-query adalah post dengan ID 1 dan 5):
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postTitles", type: LIST)
}
}...variabel dinamis $postTitles akan memiliki nilai:
[
"Hello world!",
"Everything good?"
]Tipe LIST / Multi-field
Kita mendapatkan sebuah array dictionary (bertipe JSONObject), masing-masing berisi nilai dari field-field tempat direktif diterapkan.
Query ini:
query {
posts(filter: { ids: [1, 5] }) {
title
content
@export(
as: "postsData",
type: LIST,
affectAdditionalFieldsUnderPos: [1]
)
}
}...mengekspor variabel dinamis $postsData dengan nilai:
[
{
"title": "Hello world!",
"content": "Lorem ipsum."
},
{
"title": "Everything good?",
"content": "Quisque convallis libero in sapien pharetra tincidunt."
}
]Tipe DICTIONARY / Field tunggal
Variabel dinamis akan berisi sebuah dictionary (bertipe JSONObject) dengan ID dari entitas yang di-query sebagai kunci, dan nilai field sebagai nilainya, dengan meneruskan parameter type: DICTIONARY.
Query ini:
query {
posts(filter: { ids: [1, 5] }) {
title @export(as: "postIDTitles", type: DICTIONARY)
}
}...mengekspor variabel dinamis $postIDTitles dengan nilai:
{
"1": "Hello world!",
"5": "Everything good?"
}Tipe DICTIONARY / Multi-field
Dalam kombinasi ini, kita mengekspor sebuah dictionary dari dictionary: { key: ID entitas, value: { key: alias field, value: nilai field } } (menggunakan tipe JSONObject yang akan berisi entri bertipe JSONObject).
Query ini:
query {
posts(filter: { ids: [1, 5] }) {
title
content
@export(
as: "postsIDProperties",
type: DICTIONARY,
affectAdditionalFieldsUnderPos: [1]
)
}
}...mengekspor variabel dinamis $postsIDProperties dengan nilai:
{
"1": {
"title": "Hello world!",
"content": "Lorem ipsum."
},
"5": {
"title": "Everything good?",
"content": "Quisque convallis libero in sapien pharetra tincidunt."
}
}Eksekusi kondisional operasi
Ketika Multiple Query Execution diaktifkan, direktif @include dan @skip juga tersedia sebagai direktif operasi, dan ini dapat digunakan untuk mengeksekusi operasi secara kondisional jika memenuhi suatu kondisi.
Misalnya, dalam query ini, operasi CheckIfPostExists mengekspor variabel dinamis $postExists dan, hanya jika nilainya true, mutation ExecuteOnlyIfPostExists akan dieksekusi:
query CheckIfPostExists($id: ID!) {
# Initialize the dynamic variable to `false`
postExists: _echo(value: false) @export(as: "postExists")
post(by: { id: $id }) {
# Found the Post => Set dynamic variable to `true`
postExists: _echo(value: true) @export(as: "postExists")
}
}
mutation ExecuteOnlyIfPostExists
@depends(on: "CheckIfPostExists")
@include(if: $postExists)
{
# Do something...
}Mengekspor nilai saat mengiterasi array atau objek JSON
@export menghormati kardinalitas dari meta-direktif mana pun yang melingkupinya.
Khususnya, setiap kali @export disarangkan di bawah meta-direktif yang mengiterasi item array atau properti objek JSON (yaitu @underEachArrayItem dan @underEachJSONObjectProperty), maka nilai yang diekspor akan berupa sebuah array.
Query ini:
{
post(by: { id: 19 }) {
coreContentAttributeBlocks: blockFlattenedDataItems(
filterBy: { include: "core/heading" }
)
@underEachArrayItem
@underJSONObjectProperty(
by: { path: "attributes.content" },
)
@export(
as: "contentAttributes",
)
}
}...menghasilkan $contentAttributes dengan nilai:
[
"List Block",
"Columns Block",
"Columns inside Columns (nested inner blocks)",
"Life is so rich",
"Life is so dynamic"
]Sebaliknya, query yang sama yang mengakses item tertentu dalam array alih-alih mengiterasi semuanya (dengan mengganti @underEachArrayItem dengan @underArrayItem(index: 0)) akan mengekspor nilai tunggal.
Query ini:
{
post(by: { id: 19 }) {
coreContentAttributeBlocks: blockFlattenedDataItems(
filterBy: { include: "core/heading" }
)
@underArrayItem(index: 0)
@underJSONObjectProperty(
by: { path: "attributes.content" },
)
@export(
as: "contentAttributes",
)
}
}...menghasilkan $contentAttributes dengan nilai:
"List Block"Urutan eksekusi direktif
Jika ada direktif lain sebelum @export, nilai yang diekspor akan mencerminkan modifikasi dari direktif-direktif sebelumnya tersebut.
Misalnya, dalam query ini, bergantung pada apakah @export terjadi sebelum atau sesudah @strUpperCase, hasilnya akan berbeda:
query One {
id
# First export "root", only then will be converted to "ROOT"
@export(as: "id")
@strUpperCase
again: id
# First convert to "ROOT" and then export this value
@strUpperCase
@export(as: "again")
}
query Two @depends(on: "One") {
mirrorID: _echo(value: $id)
mirrorAgain: _echo(value: $again)
}Menghasilkan:
{
"data": {
"id": "ROOT",
"again": "ROOT",
"mirrorID": "root",
"mirrorAgain": "ROOT"
}
}Multi-Field Directives
Ketika fitur Multi-Field Directives diaktifkan dan kita mengekspor nilai beberapa field ke dalam sebuah dictionary, gunakan @deferredExport alih-alih @export untuk memastikan bahwa semua direktif dari setiap field yang terlibat telah dieksekusi sebelum mengekspor nilai field.
Misalnya, dalam query ini, field pertama memiliki direktif @strUpperCase yang diterapkan padanya, dan yang kedua memiliki @titleCase. Saat mengeksekusi @deferredExport, nilai yang diekspor akan memiliki direktif-direktif ini diterapkan:
query One {
id @strUpperCase # Will be exported as "ROOT"
again: id @titleCase # Will be exported as "Root"
@deferredExport(as: "props", affectAdditionalFieldsUnderPos: [1])
}
query Two @depends(on: "One") {
mirrorProps: _echo(value: $props)
}Menghasilkan:
{
"data": {
"id": "ROOT",
"again": "Root",
"mirrorProps": {
"id": "ROOT",
"again": "Root"
}
}
}Spesifikasi GraphQL
Fungsionalitas ini saat ini belum menjadi bagian dari spesifikasi GraphQL, tetapi telah diminta: