19 Implementasi Resolver untuk Query users
Pada artikel ini, saya ingin mengupas secara mendalam tentang berbagai cara mengimplementasikan resolver GraphQL untuk query users
. Dengan mengombinasikan pengalaman mengelola aplikasi skala menengah hingga besar, saya telah menemukan berbagai variasi, pola, dan teknik yang umum atau bahkan canggih digunakan dalam menangani permintaan data pengguna. Mari kita kupas 19 pendekatan implementasi resolver users
secara praktis, dilengkapi dengan kode, simulasi, tabel, serta diagram alur.
Overview: Apa Itu Resolver?
Untuk memulai, kita perlu paham dulu apa itu resolver dalam konteks GraphQL. Resolver adalah fungsi bertanggung jawab mengambil data yang diminta oleh query klien dan mengembalikannya ke GraphQL server. Contoh klasik resolver untuk endpoint users
:
// resolvers.js
const resolvers = {
Query: {
users: () => db.users.findAll()
}
};
Namun, implementasinya bisa jauh lebih kompleks, tergantung kebutuhan aplikasi.
19 Implementasi Resolver users
Berikut 19 pendekatan tersebut, disusun dari yang paling dasar hingga dapat menangani edge-case kompleks.
1. Query Sederhana
Implementasi dasar, mengambil semua user tanpa filter apa pun.
const resolvers = {
Query: {
users: () => db.users.findAll()
}
};
2. Dengan Filter Sederhana
Menambahkan filter berdasarkan field tertentu.
const resolvers = {
Query: {
users: (_, { role }) => db.users.findAll({ where: { role } })
}
};
3. Pagination (Limit & Offset)
Mengimplementasikan limitasi data agar tidak mengembalikan semua record.
const resolvers = {
Query: {
users: (_, { limit = 10, offset = 0 }) =>
db.users.findAll({ limit, offset })
}
};
4. Pencarian (Search Query)
Menambahkan kemampuan pencarian berdasarkan nama, email, dsb.
const resolvers = {
Query: {
users: (_, { search }) =>
db.users.findAll({
where: {
name: { [Op.iLike]: `%${search}%` }
}
})
}
};
5. Ordering/Sorting
Memberikan kemampuan urutan data pada hasil query.
const resolvers = {
Query: {
users: (_, { orderBy = "name_ASC" }) =>
db.users.findAll({ order: parseOrderBy(orderBy) })
}
};
function parseOrderBy(orderBy) {
const [field, direction] = orderBy.split("_");
return [[field, direction]];
}
6. Auth Middleware (Authentication & Authorization)
Melindungi query agar hanya user tertentu bisa mendapat data.
const resolvers = {
Query: {
users: (_, __, { user }) => {
if (!user || !user.isAdmin) throw new Error("Unauthorized");
return db.users.findAll();
}
}
};
7. Dynamic Field Selection
Mengoptimalkan query berdasarkan field yang diminta klien.
users: async (_, __, ___, info) => {
const fields = info.fieldNodes[0].selectionSet.selections.map(s => s.name.value);
return db.users.findAll({ attributes: fields });
}
8. Caching Layer
Menggunakan cache agar query lebih efisien.
users: async () => {
const cacheKey = "users:all";
let usersData = await redis.get(cacheKey);
if (usersData) return JSON.parse(usersData);
usersData = await db.users.findAll();
await redis.set(cacheKey, JSON.stringify(usersData), 'EX', 60); // cache 1 menit
return usersData;
}
9. DataLoader Pattern (Batching + Caching)
Mengurangi query N+1 dengan DataLoader.
// resolver
users: (_, __, { loaders }) => loaders.userLoader.loadMany(ids)
Pastikan Anda menggunakan per-request cache untuk DataLoader.
10. Error Handling & Logging
Membungkus query dengan error handling & logging.
users: async () => {
try {
return await db.users.findAll();
} catch (error) {
logger.error(error)
throw new Error("Failed to load users");
}
}
11. Soft Delete Awareness
Menghindari user yang sudah dihapus secara soft delete.
users: () => db.users.findAll({ where: { deletedAt: null } })
12. External API as Data Source
Mengambil data user dari API eksternal.
users: async () => {
const response = await fetch("https://external-api.com/users");
return response.json();
}
13. Field-level Authorization
Melindungi field tertentu dalam objek user.
User: {
email: (user, _, { user: currentUser }) =>
currentUser.isAdmin ? user.email : null
}
14. Derived Data (Computed Fields)
Menambahkan field hasil komputasi.
User: {
fullname: (user) => `${user.firstName} ${user.lastName}`
}
15. Aggregated Fields
Menambahkan field aggregate (mis: jumlah postingan per user).
User: {
postCount: (user) =>
db.posts.count({ where: { userId: user.id } })
}
16. Multi-Tenant Context
Menyiapkan resolver agar aware siapa tenant pemilik data.
users: (_, __, { tenantId }) =>
db.users.findAll({ where: { tenantId } })
17. Multi-Source Merge
Menggabungkan data dari banyak sumber.
users: async () => {
const [main, legacy] = await Promise.all([
db.users.findAll(),
legacyApi.getUsers()
]);
return mergeUsers(main, legacy);
}
18. Rate Limiting
Membatasi seberapa sering query dapat dieksekusi.
users: async (_, __, { user }) => {
if (await isRateLimited(user.id, 'users')) {
throw new Error("Rate limit exceeded");
}
return db.users.findAll();
}
19. Custom Business Logic/Rule
Menambahkan aturan bisnis custom sebelum return data.
users: async (_, args, context) => {
const users = await db.users.findAll();
return users.filter(u => customBusinessRule(u, context));
}
Simulasi: Membandingkan Respons
Misalkan ada 3 user sulap data berikut:
id | name | role | deletedAt |
---|---|---|---|
1 | Alice | admin | null |
2 | Bob | editor | null |
3 | Carol | viewer | 2024-01-01 |
Query GraphQL dasar akan return: Alice, Bob, Carol.
Dengan Soft Delete Awareness (deletedAt: null
) hanya akan return: Alice, Bob.
Diagram Alur Resolver users
(mermaid)
Mari visualisasikan bagaimana pipeline resolver dengan beberapa concern di atas.
graph TD A[Received Query: users] --> B{Is Authenticated?} B -- No --> Z[Throw Unauthorized Error] B -- Yes --> C{Is Rate Limit Exceeded?} C -- Yes --> Y[Throw Rate Limit Error] C -- No --> D[Check Cache] D -- Cache Hit --> E[Return Cached Data] D -- Miss --> F[Fetch From DB/API] F --> G{Apply Filter/Soft Delete} G --> H[Transform / Map Custom Logic] H --> I[Store To Cache(if enabled)] I --> J[Return Data]
Kapan Menggunakan Pola Tertentu?
Implementasi | Cocok Untuk | Kelebihan | Kekurangan |
---|---|---|---|
Pagination | Data besar | Lebih efisien | Agak kompleks |
Authorization | Data sensitif | Lebih aman | Perlu pengelolaan session |
Caching/DataLoader | Beban tinggi/N+1 masalah | Performan, hemat sumberdaya | Menambah layer |
Aggregation | Data insight/statistik | Insightful | Query lebih berat |
Multi-tenant | SaaS/berbasis klien | Isolasi data | Perlu context tenant |
Penutup
Sayangnya, tidak ada “one-size-fits-all” dalam membangun resolver GraphQL. Tergantung kebutuhan aplikasi, kebijakan bisnis, dan karakteristik data, Anda bisa memilih, memodifikasi, atau bahkan mengombinasikan berbagai implementasi di atas.
Justru di situlah seni seorang engineer: Menyusun pipeline resolver yang efisien, aman, dan scalable, dengan tetap menjaga fleksibilitas dan maintainability. Semoga referensi ini menginspirasi Anda membangun resolver users
yang solid, tidak hanya sekedar “mengembalikan array user”.
Bagaimana dengan implementasi resolver Anda? Sudah pakai optimasi apa saja? Silakan share pengalaman dan strategi unik Anda di komentar!
Referensi tambahan:
41 Memahami Nested Messages
42 Menggunakan Enum di Protobuf
Artikel Terhangat
43 Memanfaatkan `oneof` di Protobuf
07 Jul 2025
42 Menggunakan Enum di Protobuf
07 Jul 2025
19 Implementasi Resolver untuk Query `users`
07 Jul 2025
41 Memahami Nested Messages
07 Jul 2025

43 Memanfaatkan `oneof` di Protobuf

42 Menggunakan Enum di Protobuf

19 Implementasi Resolver untuk Query `users`
