Skip to content
Santekno.com | Level Up Your Engineering Skills
ID
📖 0%
14 Sep 2025 · 4 mnt baca ·Artikel 76 / 125
Go

76 Optimasi Resolver agar Tidak Lambat

IH
Ihsan Arif
Penulis di Santekno · Backend Engineer

76 Optimasi Resolver agar Tidak Lambat: Panduan Lengkap untuk Engineer

Bayangkan sebuah sistem backend dengan GraphQL, puluhan query harian, dan traffic tinggi. Para engineer seringkali menjadikan resolver sebagai tempat utama proses fetching dan transformasi data. Namun, lambat laun, performa resolver menjadi masalah klasik—API terasa lambat, bahkan kadang membebani infrastruktur. Artikel ini membedah 76 optimasi yang bisa Anda lakukan agar resolver Anda tetap ringan, cepat, dan scalable.

Mengapa Resolver Sering Lambat?

Resolver ada di jantung ekosistem GraphQL. Ia memetakan query client ke resource backend (database, REST API, service lain). Permasalahan performa muncul karena beberapa faktor:

  • N+1 problems
  • Unoptimized queries
  • Overfetching/underfetching
  • Kurangnya caching
  • Pemrosesan data yang tidak efisien

Mari kita selami lapisan-lapisannya, dan saya akan tunjukkan 76 optimasi yang terbukti efektif.


1. Struktur Resolver yang Bersih

Struktur yang baik membuat debug, tracing, dan profiling lebih mudah.

js
 1// Resolver yang bagus: Modular, reusable, testable
 2module.exports = {
 3  Query: {
 4    user: async (parent, args, context) => {
 5      return context.dataSources.userAPI.getUserById(args.id);
 6    },
 7  },
 8  User: {
 9    posts: async (user, _, context) => {
10      return context.dataSources.postAPI.getPostsByUser(user.id);
11    },
12  },
13};
Danger
Hindari menulis semua logic langsung di resolver, apalagi query SQL/raw database di dalamnya.

2. Batasi Penggunaan Anonymous Function

Resolver yang inline dan tidak diberi nama rentan lupa ditest dan susah di-log. Biasakan pisahkan logic berat ke helper:

js
1const getDetailedUser = (userId, dataSources) => { ... }
2
3// Di resolver
4user: (parent, args, context) => getDetailedUser(args.id, context.dataSources)

3-10. Atasi N+1 Query Problem

Skenario N+1 muncul saat fetching relasi, contohnya: Ambil user lalu ambil post dari tiap user.

Menggunakan DataLoader

Install dulu:

bash
1npm install dataloader

Implementasi:

js
1const DataLoader = require('dataloader');
2
3const postLoader = new DataLoader(async (userIds) => {
4  // Query posts with IN(userIds)
5  const posts = await db.posts.findAll({ where: { userId: userIds } });
6  // Map result ke masing-masing owner id
7  return userIds.map(id => posts.filter(p => p.userId == id));
8});

Di resolver:

js
1User: {
2  posts: (user, args, { loaders }) => loaders.postLoader.load(user.id)
3}
Performa membaik drastis!
Simulasi benchmark kecil:

Jumlah UserTanpa Dataloader (ms)Dengan Dataloader (ms)
1015240
100142368

11-15. Limit dan Pagination sebagai Aturan Wajib

Jangan pernah kirim seluruh data tanpa batas (limit), gunakan teknik offset, cursor, atau keyset pagination:

js
1// Query dengan limit & offset
2db.users.find({ limit: 50, offset: 0 });

16-20. Gunakan Projection/Field Mask

Cek hanya field yang diminta GraphQL:

js
1function selectFields(info) {
2  // Extract requested fields, implementasi custom
3}
4
5const fields = selectFields(info); // ['id','name']
6const user = db.user.find({ select: fields });

21-30. Response Caching

Implement cache pada level dataSource atau resolver, contoh dengan Redis:

js
1async function cachedFindUser(id) {
2  const cachedUser = await redis.get("user:" + id);
3  if (cachedUser) return JSON.parse(cachedUser);
4
5  const user = await db.user.findById(id);
6  await redis.set("user:" + id, JSON.stringify(user), 'EX', 3600);
7  return user;
8}

31-35. Query Batching

Gabungkan beberapa query menjadi satu, terutama saat request beruntun dari user yang sama.


36-45. Async/Await All the Way

Jangan pernah block event loop! Gunakan Promise.all jika resolver membutuhkan beberapa resource:

js
1const [profile, posts] = await Promise.all([
2  db.profile.findById(userId),
3  db.posts.find({ userId })
4]);

46-55. Monitoring dan Logging Detail

Pasang tracing:

js
1const tracer = require('your-trace-lib');
2tracer.startSpan('getUserResolver');
3// resolve logic
4tracer.endSpan();

56-60. Debounce dan Throttle Internal Request

Resolver yang memanggil resource eksternal (API third party) rawan membebani server mereka.


61-65. Early Return dan Filtering

Saring sebanyak mungkin di database, jangan di memory JS:

js
1// Kurang optimal
2db.users.find().filter(user => user.isActive);
3
4// Lebih baik:
5db.users.find({ isActive: true });

66-70. Batasi Nested Resolver (Depth Limit)

Agar resolver tidak infinite loop dan resource boros:

js
1// graphql-depth-limit middleware usage
2graphqlServer.use(depthLimit(5));

71-75. Dokumentasi dan Review Kode

Optimasi resolver yang baik selalu didukung test dan dokumentasi beri contoh edge-casenya.


76. Gunakan Profiling untuk Menemukan Bottleneck

Instal profiler (mis: 0x, clinic.js, perf_hooks):

bash
1npx 0x app.js

Analisa timeline functional resolver, temukan function yang bottleneck dan refactor.


Diagram Alur Resolver

Mari gambarkan alur eksekusi resolver teroptimasi dengan mermaid:

MERMAID
flowchart TD
    A[Query Masuk] --> B[Middleware Validasi & Auth]
    B --> C[Resolve Parent]
    C --> D((Cek Cache?))
    D -- Ya --> E[Return dari Cache]
    D -- Tidak --> F[Fecth DB/API/DataSource]
    F --> G[Process, Mapping, Transform]
    G --> H[Simpan ke Cache (optional)]
    H --> I[Kirim Response]

Kesimpulan

Mengoptimasi resolver itu bukan tugas sekali jadi. Setiap tahap perlu pendekatan berbeda—mulai dari arsitektur, resource management, caching, hingga profiling. Lakukan 76 tips di atas secara progresif, prioritaskan dari yang memiliki impact Google-style: 80% masalah resolver lambat biasanya hanya di <10 optimasi teratas!

Ingat, tujuan kita bukan sekedar “cepat”, tapi juga “robust” dan maintainable. Selalu review, monitor, dan refactor resolver secara berkala.


Bonus file:
Download template resolver siap optimized dan mulai benchmarking hari ini!


Related Reading:

  • How to Avoid GraphQL N+1 Problem with DataLoader (Medium)
  • Clean Resolver Patterns
  • Advanced GraphQL Caching Strategies

Selamat mengoptimasi! 🚀

Artikel Terkait

💬 Komentar