tutorial

33 Menulis Resolver Query dari Database

33 Menulis Resolver Query dari Database

Di era modern pengembangan aplikasi, arsitektur backend semakin memanjakan kebutuhan klien akan data yang efisien dan fleksibel. Salah satu pendekatan mutakhir adalah menggunakan GraphQL sebagai query language untuk API. Di sisi backend, kita sering dihadapkan dengan kebutuhan untuk menulis resolver agar data di database bisa di-query dengan mudah oleh klien. Tulisan ini akan membedah bagaimana menulis 33 resolver query dari database, dengan pendekatan yang sistematis dan skalabel.

Kita akan melangkah dari konsep dasar, contoh kode, simulasi flow, hingga benchmarking sederhana. Asumsi: Backend menggunakan Node.js dengan Apollo Server dan ORM Sequelize sebagai data layer ke database relasional (PostgreSQL).


Apa Itu Resolver Query?

GraphQL resolver adalah fungsi yang menangani fetching data dari sumber (umumnya database) berdasarkan permintaan query klien. Resolver bisa meng-handle fetch satu baris, banyak baris, filtering, sorting, hingga relasi data.

Setiap field pada GraphQL schema yang butuh data dari database biasanya punya resolvernya sendiri.


Desain Model dan Skema Database

Mari kita bayangkan sebuah database sederhana untuk aplikasi toko online:

TableFields
Userid, name, email
Productid, name, price, stock
Orderid, userId, productId, quantity, createdAt

Anggap saja, ada 33 skenario query yang ingin kita support, seperti:

  • Query user by id
  • Query all users
  • Query user by email
  • Query product by id
  • Query products by name
  • Query all products sorted by price, dst.

Diagram Alur Resolver Query

Untuk memahami prosesnya, berikut flow resolver query dengan kode mermaid:

flowchart TD
    A[Client queries GraphQL] --> B[GraphQL Server menerima query]
    B --> C[Parsing & Validating Query]
    C --> D[Menemukan Resolver]
    D --> E[Resolver Fetch ke DB lewat ORM]
    E --> F[ORM Kirim Data ke Resolver]
    F --> G[Resolver Format Data]
    G --> H[Balas ke Client]

Contoh Skema GraphQL

type Query {
  userById(id: ID!): User
  users: [User]
  userByEmail(email: String!): User
  productById(id: ID!): Product
  productsByName(name: String!): [Product]
  allProducts(sortByPrice: Boolean): [Product]
  # ...hingga 33 endpoint query
}

type User {
  id: ID
  name: String
  email: String
  orders: [Order]
}

type Product {
  id: ID
  name: String
  price: Float
  stock: Int
}

type Order {
  id: ID
  user: User
  product: Product
  quantity: Int
  createdAt: String
}

Struktur Resolver File & Praktik Baik

Agar maintainable, sebaiknya kita bagi resolver berdasarkan resource/domain:

resolvers/
  ├─ index.js
  ├─ userResolver.js
  ├─ productResolver.js
  └─ orderResolver.js

Dan tiap file mempunyai resolusi masing-masing.


Implementasi Resolver dengan Sequelize

Mari langsung ke contoh implementasi. Misal: userResolver.js

// userResolver.js
const { User, Order } = require('../models');

const userResolver = {
  Query: {
    userById: async (_, { id }) => {
      return await User.findByPk(id);
    },
    users: async () => {
      return await User.findAll();
    },
    userByEmail: async (_, { email }) => {
      return await User.findOne({ where: { email } });
    }
  },
  User: {
    orders: async (parent) => {
      return await Order.findAll({ where: { userId: parent.id } });
    }
  }
};

module.exports = userResolver;

Sebagai catatan:

  • Setiap function me-return promise, siap untuk async-await.
  • Penggunaan parent di field resolver untuk menjaga relasi.

Menambah Resolver untuk 33 Query

Untuk mengelola 33 resolver, gunakan pattern generator atau template. Contoh, di productResolver.js:

const { Product } = require('../models');

const productResolver = {
  Query: {
    productById: async (_, { id }) => await Product.findByPk(id),
    productsByName: async (_, { name }) =>
      await Product.findAll({ where: { name } }),
    allProducts: async (_, { sortByPrice }) =>
      await Product.findAll({
        order: sortByPrice ? [['price', 'ASC']] : undefined
      }),
    // ...Add 30 more as per requirements
  }
};

module.exports = productResolver;

Untuk scalability dan DRY (Don’t Repeat Yourself), buat utility untuk automasi beberapa resolver:

function createSimpleFindQuery(model, by) {
  return async (_, args) => await model.findOne({ where: { [by]: args[by] } });
}

Simulasi Query: User with Orders

Anggap pengguna ingin data User beserta daftar Order:

query {
  userById(id: "123") {
    id
    name
    orders {
      id
      product {
        name
        price
      }
      quantity
      createdAt
    }
  }
}

Bagaimana resolver menanganinya?

  • Pertama mem-fetch User via ID (userById)
  • Ketika field orders diakses, resolver User.orders di-trigger untuk fetch order
  • Field product dalam Order akan trigger resolver terpisah di tipe Order

Simulasi Output dari 33 Query

Berikut miniatur simulasi output untuk 3 dari 33 query:

QueryParameterContoh Output
userByIdid: 123{“id”:123, “name”:“Rafi”, “email”:“rafi@foo.com”}
userByEmailemail:foo@bar.com{“id”:99, “name”:“Siti”, “email”:“foo@bar.com”}
allProducts(sortByPrice: true)sortByPrice: true[{“id”:11,“name”:“T-shirt”,“price”:99.9,…}, …]

Aturan & Tips Menulis Banyak Resolver

  1. DRY Principle: Jika ada query yang mirip, gunakan helper atau generator.
  2. Validation: Lakukan validasi parameter sebelum ke DB untuk meminimalisir error dan resiko DB.
  3. Batching & Caching: Untuk nested resolver, gunakan DataLoader agar mencegah N+1 query problem.
  4. Error Handling: Gunakan try-catch di setiap resolver dan konsisten error formatnya.
  5. Documentasi: Tambahkan JSDoc atau komentar minimal endpoint/kegunaan tiap resolver.
  6. Testing: Siapkan test unit/integrasi khusus untuk setiap resolver.

Benchmark Sederhana

Misal, 33 query dijalankan paralel oleh client. Gunakan Promise.all untuk simultan test:

const allQueries = [
  gql1, gql2, /* ... up to gql33 */
];

Promise.all(allQueries.map(q => apolloServer.executeOperation({ query: q })))
  .then(results => {
    // Evaluasi time taken, error, dsb
  });

Penutup

Menulis 33 resolver query memang menantang, terutama untuk menjaga agar kode tetap scalable & maintainable. Fokus pada struktur resolver, automasi untuk query-query yang mirip, dan gunakan best practice GraphQL. Jangan lupa testing dan dokumentasi, agar tim Anda dapat menjaga & mengembangkan codebase secara berkelanjutan.

Kalau ada pengalaman kustom atau tips menulis resolver, boleh sharing di kolom komentar! Happy coding 🚀

comments powered by Disqus

Topik Terhangat

programming
245
tutorial
112
tips-and-trick
43
jaringan
28
hardware
11
linux
4
kubernetes
1