tutorial

59 Skema dan Resolver Subscription

59 Skema dan Resolver Subscription: Memahami Konsep, Praktik, dan Implementasi dalam GraphQL

Berbicara tentang GraphQL, kita tidak bisa lepas dari tiga pilar utama: schema, resolver, dan subscription. Khusus untuk artikel ini, saya akan mengupas tuntas skema dan resolver subscription dan pentingnya mereka dalam pengembangan API modern.

Saya asumsikan pembaca sudah familiar dengan konsep dasar query dan mutation di GraphQL. Kali ini kita akan fokus pada mekanisme subscription, yang menjadi jembatan antara backend dan real-time frontend. Anda akan menemukan penjelasan, simulasi, tabel perbandingan, hingga contoh kode nyata seputar subscription di GraphQL.


1. Subscription di GraphQL: Apa itu dan Kenapa Penting?

Sebelum masuk ke skema dan resolver, mari kita refresh sedikit tentang subscription.

Subscription memungkinkan klien untuk berlangganan event tertentu dan menerima data secara real-time, tanpa harus melakukan polling manual ke server. Sangat bermanfaat untuk aplikasi chat, dashboard analytics, notifikasi, dan berbagai aplikasi yang mengusung konsep real-time.

Perbandingan model komunikasi data:

QueryMutationSubscription
SifatRequest/responseWriteStream/push
ResponSekaliSekaliBerkala (realtime)
Contoh UsecaseFetch tabelHapus dataChat masuk

2. Diagram Alur Subscription

Agar lebih mudah membayangkan, berikut diagram alur subscription yang akan kita implementasikan nantinya menggunakan Mermaid syntax:

sequenceDiagram
    participant Client
    participant Server
    participant PubSub

    Client->>Server: Kirim request subscription
    Server-->>Client: Mendengar event (connection ack)
    PubSub-->>Server: New event published
    Server-->>Client: Kirim data baru via websocket

Di sini, Server akan mendengarkan event dari PubSub (Publish/Subscribe mechanism) lalu meneruskan ke Client yang sudah membuka connection.


3. Deklarasi Skema Subscription di GraphQL

Schema pada GraphQL mendefinisikan bagaimana bentuk serta jenis event yang dapat di-subscribe oleh client.

type Subscription {
  messageSent(roomId: ID!): Message
}

type Message {
  id: ID!
  content: String!
  sender: String!
  sentAt: String!
}

Penjelasan:

  • Subscription bernama messageSent, bertipe return Message.
  • Parameter roomId bertipe ID — hanya akan menerima message dari ruang tertentu.
  • Type Message mendeskripsikan bentuk object yang dikirim ke klien.

4. Resolver: Jembatan Skema dan Backend

Resolver adalah kode logika yang menghubungkan schema dengan data sumber (entah database, event emitter, dsb).

Implementasi Subscription berbeda dengan Query/Mutation. Resolver subscription harus mengembalikan sebuah async iterator agar server bisa stream data ke klien.

Kita akan gunakan package populer graphql-subscriptions yang menyediakan PubSub sederhana dengan EventEmitter internal.

Langkah 1: Setup PubSub dan Resolver

const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();

const SUBSCRIPTION_TOPIC = 'MESSAGE_SENT';

const resolvers = {
  Subscription: {
    messageSent: {
      subscribe: (_, { roomId }) =>
        pubsub.asyncIterator(`${SUBSCRIPTION_TOPIC}_${roomId}`),
    },
  },
};

Penjelasan:

  • Ketika klien subscribe ke messageSent, resolver akan mengembalikan async iterator ke topik tertentu (dalam hal ini, berdasarkan roomId).

Langkah 2: Publish Event saat Message baru

Simulasikan publish event ketika pesan baru dikirim (misal lewat mutation sendMessage):

const resolvers = {
  Mutation: {
    sendMessage: async (_, { roomId, content, sender }) => {
      const message = { id: Date.now(), content, sender, sentAt: new Date().toISOString() };
      // Simulasi simpan ke DB
      // db.saveMessage(message);

      // Publish event ke subscriber topik yang sesuai
      await pubsub.publish(`${SUBSCRIPTION_TOPIC}_${roomId}`, {
        messageSent: message,
      });
      return message;
    },
  },
};

5. Simulasi Alur Subscription

Mari kita uji prosesnya:

Langkah Simulasi

  1. Client A subscribe ke subscription berikut:

    subscription {
      messageSent(roomId: "room-59") {
        id
        content
        sender
        sentAt
      }
    }
    
  2. Client B mengirim pesan via mutation:

    mutation {
      sendMessage(roomId: "room-59", content: "Halo, GraphQL!", sender: "Budi") {
        id
        content
      }
    }
    
  3. Client A menerima pesan baru tanpa perlu polling. Server otomatis push data:

    {
      "data": {
        "messageSent": {
          "id": "1718717500000",
          "content": "Halo, GraphQL!",
          "sender": "Budi",
          "sentAt": "2024-06-15T19:38:20Z"
        }
      }
    }
    

Seringkali, Anda akan menggunakan WebSocket (contohnya subscriptions-transport-ws atau package di Apollo Server) sebagai protokol underlying agar benar-benar real-time.


6. Studi Kasus: Tabel Perbandingan (Query vs Subscription)

FiturQuerySubscription
MekanismeHTTP, statelessWebSocket, stateful connection
Kapan dipakaiData snapshotReal-time streaming event
SkalabilitasMudah (caching, CDN)Cukup sulit, butuh infra proper
Use-case populerLaporan, statistikChat, notif, harga realtime

7. Best Practice & Tips

  • Gunakan Filtering: Selalu batasi event yang dikirim. Gunakan parameter pada subscription (misal, roomId).
  • Autentikasi: Pastikan hanya klien terotorisasi yang boleh subscribe; filter di layer resolver.
  • Scalability: Implementasikan alat seperti Redis PubSub atau Kafka kalau aplikasi sudah skala besar.
  • Handle Disconnect: Pastikan client dapat reconnect/subscribe ulang jika koneksi WebSocket terputus.

8. Contoh Implementasi End-to-End (Mini Project)

Berikut potongan kode server GraphQL (Node.js + Apollo Server) yang menjalankan subscription dengan PubSub local:

const { ApolloServer, gql } = require('apollo-server');
const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();

const typeDefs = gql`
  type Message {
    id: ID!
    content: String!
    sender: String!
    sentAt: String!
  }

  type Query {
    _empty: String
  }

  type Mutation {
    sendMessage(roomId: ID!, content: String!, sender: String!): Message!
  }

  type Subscription {
    messageSent(roomId: ID!): Message!
  }
`;

const SUBSCRIPTION_TOPIC = 'MESSAGE_SENT';

const resolvers = {
  Mutation: {
    sendMessage: async (_, { roomId, content, sender }) => {
      const message = { id: Date.now(), content, sender, sentAt: new Date().toISOString() };
      await pubsub.publish(`${SUBSCRIPTION_TOPIC}_${roomId}`, {
        messageSent: message,
      });
      return message;
    },
  },
  Subscription: {
    messageSent: {
      subscribe: (_, { roomId }) =>
        pubsub.asyncIterator(`${SUBSCRIPTION_TOPIC}_${roomId}`),
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url, subscriptionsUrl }) => {
  console.log(`🚀 Server ready at ${url}`);
  console.log(`🚀 Subscriptions ready at ${subscriptionsUrl}`);
});

9. Penutup: Subscription = Future of Real-Time Web

Dengan memahami skema dan resolver subscription, kita bisa membangun fitur real-time yang efisien, clean, dan maintainable. GraphQL subscription menawarkan solusi ideal di era aplikasi yang makin interaktif dan responsif.

Tantangan utamanya ada pada skalabilitas dan manajemen koneksi, namun dengan arsitektur dan best practice yang tepat, subscription bisa jadi senjata andalan backend modern.

Tertarik mencoba di project Anda? Let’s subscribe to the future! 🚀

comments powered by Disqus

Topik Terhangat

programming
295
tutorial
162
tips-and-trick
44
jaringan
28
hardware
11
linux
4
kubernetes
1