tutorial

61 Nested Resolver dan Resolver Berantai

61 Nested Resolver dan Resolver Berantai: Memahami Mekanisme dan Praktik Terbaik di GraphQL (Lengkap dengan Contoh & Diagram)


GraphQL telah merevolusi cara kita mendesain API—salah satu kekuatan utamanya adalah kemampuan untuk menulis query kompleks yang bisa menarik data berlapis-lapis dalam satu permintaan. Namun, di balik keindahan sintaks ini, ada sebuah konsep krusial yang harus dipahami setiap engineer: resolver dan cara mereka bekerja, terutama dalam konteks nested resolver dan resolver berantai.

Kali ini, kita akan membedah secara mendalam tentang nested resolver dan resolver berantai di GraphQL—mulai dari konsep dasar, simulasi kode, diagram alur, hingga best practice. Artikel ini cocok untuk kamu yang ingin menguatkan fundamental hingga menyusun API yang efisien dan scalable.


Resolver 101: Pondasi Pengetahuan

Sebelum melangkah jauh ke “nested” dan “berantai”, kamu harus memahami akar permasalahan: Apa itu resolver?

Secara sederhana, resolver adalah fungsi yang bertanggung jawab untuk “mengisi” nilai dari field yang diminta dalam GraphQL Query. Ketika GraphQL menerima permintaan, ia akan menelusuri query berdasarkan schema, lalu memanggil resolver sesuai struktur tersebut.

Contoh Resolver Sederhana

Misal, skema berikut:

type Book {
  id: ID!
  title: String!
  author: Author!
}

type Author {
  id: ID!
  name: String!
}

type Query {
  book(id: ID!): Book
}

Query:

{
  book(id: 1) {
    title
    author {
      name
    }
  }
}

Implementasi resolver:

const books = [
  { id: "1", title: "GraphQL in Depth", authorId: "2" }
];
const authors = [
  { id: "2", name: "Adi Nugroho" }
];

const resolvers = {
  Query: {
    book: (_, { id }) => books.find(b => b.id === id),
  },
  Book: {
    author: (parent) => authors.find(a => a.id === parent.authorId)
  }
}

Lihat bagaimana author diresolusi oleh fungsi terpisah pada tipe Book—itulah nested resolver.

1. Nested Resolver: Resolver Bersarang

Definisi

Nested resolver adalah fungsi resolver yang dideklarasikan pada level field yang bersarang dalam tipe objek.

Saat query meminta data berlapis, GraphQL akan memanggil resolver:

  • Dimulai dari level root (misal, book di Query),
  • Melewati setiap field yang diminta,
  • Untuk field dengan tipe objek (bukan tipe primitif), akan memanggil resolver pada field type berikutnya,
  • Dan seterusnya, secara rekursif.

Ilustrasi Flow

Mari lihat dengan mermaid diagram berikut:

graph TD
    Start([Query: book(id:1)]) --> B[Resolver: Query.book]
    B --> T[Return Book object]
    T --> C[Resolver: Book.title (primitive)]
    T --> D[Resolver: Book.author (object)]
    D --> E[Resolver: Author.name (primitive)]

Penjelasan:

  • Query.book mencari buku sesuai ID (return JSON objek).
  • Untuk field title, karena primitif, ambil langsung dari objek.
  • Untuk author, karena tipenya objek Author, resolver pada field author di objek Book akan dipanggil, kemudian mengulangi proses ke dalam.

Simulasi Request & Process Table

StepFieldCurrent Parent ObjectResolver dipanggilOutput
1Query.booknullQuery.book{id,title,authorId}
2Book.title{id,title,authorId}Default/simple"GraphQL in Depth"
3Book.author{id,title,authorId}Book.author{id, name}
4Author.name{id, name}Default/simple"Adi Nugroho"

2. Resolver Berantai: Serialisasi antara Resolver

Sering timbul pertanyaan: Apakah hasil satu resolver dipasok ke resolver berikutnya?

Jawabannya: YA, tapi terbatas pada parent field yang sedang diproses. Resolver berantai berarti setiap resolver menunggu hasil (object) dari resolver di atasnya. Inilah yang memungkinkan data dinamis dan computed field.

Studi Kasus: Chaining Resolver

Misalkan kita ingin menghitung jumlah buku yang dimiliki seorang Author pada field baru bookCount:

Schema

type Author {
  id: ID!
  name: String!
  bookCount: Int!
}

Resolver Chain

const resolvers = {
  Query: {
    author: (_, { id }) => authors.find(a => a.id === id),
  },
  Author: {
    bookCount: (parent) => books.filter(b => b.authorId === parent.id).length,
    // bisa juga buat resolver 'name' kalau diperlukan
  }
}

Diagram Alur Serialisasi Resolver

graph TD
    Q([Query: author(id:2)]) --> R1[Resolver: Query.author]
    R1 --> AO[Return Author object]
    AO --> R2[Resolver: Author.bookCount]
    R2 --> R3[Count & Return]

Catatan: Nilai return dari resolver Query.author menjadi input (parent) pada resolver Author.bookCount—inilah “rantai” antar resolver.


3. Tantangan: N+1 Problem dan Optimasi

Nested resolver sangat powerful, tapi bisa menyebabkan masalah klasik: N+1 Problem.

Contoh Kasus

Query: Semua buku, lengkap dengan nama author:

{
  books {
    title
    author {
      name
    }
  }
}
  • Resolver books ambil semua buku (N buah).
  • Untuk setiap buku, ambil author (menghasilkan N query lagi).

Problem? Jika datanya besar, API akan kena “flood” ke database!

Simulasi dengan DataLoader

Solusi: Gunakan DataLoader (batching dan caching).

const DataLoader = require('dataloader');
const authorLoader = new DataLoader(async (ids) => {
  // ids: array of authorId
  const result = await db.authors.find({ id: { $in: ids } });
  return ids.map(id => result.find(a => a.id === id));
});

const resolvers = {
  Query: {
    books: () => books,
  },
  Book: {
    author: (parent) => authorLoader.load(parent.authorId)
  }
}

4. Best Practice Mendefinisikan Resolver Bersarang dan Berantai

  • Minimalkan Fetching di setiap resolver child. “Delegasikan” data fetching sebanyak mungkin ke resolver parent/root.
  • Optimalkan dengan Batching/Caching (misal, DataLoader di Node.js).
  • Buat resolver stateless dan idempotent.
  • Modularisasi resolver: Pisahkan logic fetching, transformation, dan aggrigation.

5. Tabel Ringkasan Perbandingan

AspekNested ResolverResolver Berantai
DefinisiResolver pada field yang bersarangResolver yang serial (bergantung parent)
ContohBook.authorAuthor.bookCount
InputParent object, argsParent object hasil resolver sebelumnya
RisikoN+1 Query problemData redundancy jika chain dalam-dalam
SolusiBatching, DataLoader, eager loadingOptimasi pada root fetching

Kesimpulan

Memahami mekanisme nested resolver dan resolver berantai adalah wajib bagi backend engineer yang ingin membangun GraphQL API yang efisien & scalable. Dengan memahami arsitektur pemanggilan resolver yang bertingkat dan berseri, kita dapat mengoptimalisasi fetching data, menghindari N+1 problem, dan membuat kode lebih modular.

GraphQL memang powerful, tapi ia menuntut disiplin dalam menyusun resolver—nested atau berantai. Dengan simulasi di atas, kamu diharapkan siap menyusun API yang cerdas! Jangan ragu gunakan batching, instrumentasi, serta diagram dan flow untuk membantu tim memahami alur data di seluruh resolver.

Salam optimasi, dan semoga kode GraphQL-mu makin efektif!

comments powered by Disqus

Topik Terhangat

programming
299
tutorial
166
tips-and-trick
44
jaringan
28
hardware
11
linux
4
kubernetes
1