tutorial

57 Pengenalan WebSocket untuk Real-time GraphQL

57 Pengenalan WebSocket untuk Real-time GraphQL

Web telah berevolusi jauh dari sekadar dokumen HTML statis yang dikirim dari server ke browser. Sekarang, pengalaman pengguna menuntut interaksi yang real-time, sinkronisasi data instan, dan responsifitas ekstrem. Dalam dunia aplikasi modern, baik itu fitur chat, dashboard yang auto-refresh, atau feed yang terus diperbarui—semuanya membutuhkan satu hal: komunikasi real-time antara client dan server.

GraphQL telah menjadi standar de facto untuk API di banyak perusahaan karena fleksibilitas dan efisiensinya. Namun, bagaimana bila aplikasi kita membutuhkan update data yang bersifat real-time? Di sinilah WebSocket hadir dan berintegrasi mulus dengan GraphQL melalui fitur yang dikenal sebagai subscriptions.

Artikel ini akan membahas konsep dasar WebSocket, bagaimana ia bekerja dengan GraphQL, cara mengimplementasikan subscriptions, serta best practice-nya. Kita akan membahas kode, simulasi, tabel perbandingan, dan diagram alur. Ini bukan sekadar tutorial, melainkan peta jalan bagi siapapun yang ingin menghadirkan real-time pada API mereka.


Apa itu WebSocket?

WebSocket adalah protokol komunikasi network yang menyediakan saluran dua-arah (full-duplex) antara client dan server melalui koneksi TCP tunggal. WebSocket berbeda dengan HTTP yang tradisional (request-response), karena WebSocket memungkinkan server dapat mengirim pesan ke client kapan pun, tanpa harus menunggu request.

Kapan WebSocket Lebih Baik daripada HTTP?

HTTP (Polling/Long Polling)WebSocket
Client harus memulai semua requestServer dapat push data ke client secara spontan
Overhead besar, setiap request seperti handshake baruCuma 1 kali handshake, selanjutnya ringan
Latensi lebih tinggi untuk data real-timeLatensi serendah mungkin
Cocok untuk konten statis atau CRUD tradisionalCocok untuk chat, notifikasi, game, live dashboard, dll

GraphQL dan WebSocket: Mengapa Harus Subscriptions?

GraphQL memiliki tiga operation utama:

  • Query: Mengambil data.
  • Mutation: Mengubah data.
  • Subscription: Mendengarkan perubahan data.

Subscriptions inilah fitur yang memungkinkan client mendapatkan data real-time begitu ada perubahan di server, melalui channel WebSocket.

Alur Data: Query vs Subscription

Query:

  1. Client mengirim query GraphQL (HTTP).
  2. Server mengembalikan response sekali saja.
  3. Koneksi selesai.

Subscription:

  1. Client membuka koneksi WebSocket.
  2. Client subscribe ke event tertentu.
  3. Setiap ada event atau perubahan, server push data secara otomatis ke client.
  4. Koneksi terus aktif selama client butuh.

Mari lihat diagram sederhana alur WebSocket dengan mermaid:

sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: Membuka koneksi WebSocket
    C->>S: Mengirim GraphQL Subscription
    S->>C: Kirim ACK (Berhasil subscribe)
    loop Setiap ada perubahan data
        S->>C: Kirim data terbaru (event)
    end
    C-->>S: Tutup koneksi WebSocket

Implementasi Dasar: GraphQL Subscription dengan WebSocket

Kita akan gunakan stack populer: Node.js (Express), Apollo Server, dan graphql-ws.

  1. Setup Server
    Install dependencies:

    npm install express apollo-server-express graphql graphql-ws
    
  2. Schema GraphQL (subscription, query, mutation)

    // schema.js
    const { gql } = require('apollo-server-express');
    
    module.exports = gql`
      type Message {
        id: ID!
        content: String!
        user: String!
      }
    
      type Query {
        messages: [Message!]
      }
    
      type Mutation {
        postMessage(content: String!, user: String!): ID!
      }
    
      type Subscription {
        messagePosted: Message!
      }
    `;
    
  3. Resolvers dengan PubSub

    // resolvers.js
    const { PubSub } = require('graphql-subscriptions');
    const pubsub = new PubSub();
    const messages = [];
    
    module.exports = {
      Query: {
        messages: () => messages
      },
      Mutation: {
        postMessage: (_, { content, user }) => {
          const id = messages.length + 1;
          const message = { id, content, user };
          messages.push(message);
          pubsub.publish('MESSAGE_POSTED', { messagePosted: message });
          return id;
        }
      },
      Subscription: {
        messagePosted: {
          subscribe: () => pubsub.asyncIterator(['MESSAGE_POSTED'])
        }
      }
    };
    
  4. Apollo Server + graphql-ws

    // index.js
    const express = require('express');
    const { ApolloServer } = require('apollo-server-express');
    const { createServer } = require('http');
    const { useServer } = require('graphql-ws/lib/use/ws');
    const { WebSocketServer } = require('ws');
    const typeDefs = require('./schema');
    const resolvers = require('./resolvers');
    
    async function start() {
      const app = express();
      const httpServer = createServer(app);
      const apolloServer = new ApolloServer({ typeDefs, resolvers });
    
      await apolloServer.start();
      apolloServer.applyMiddleware({ app });
    
      // WebSocket server
      const wsServer = new WebSocketServer({
        server: httpServer,
        path: '/graphql',
      });
    
      useServer({ schema: apolloServer.schema }, wsServer);
    
      httpServer.listen(4000, () => {
        console.log(`🚀 Server siap di http://localhost:4000${apolloServer.graphqlPath}`);
        console.log(`⚡ WS siap di ws://localhost:4000/graphql`);
      });
    }
    
    start();
    
  5. Client Subscriptions
    Jika pakai Apollo Client di React:

    import { createClient } from 'graphql-ws';
    import { ApolloClient, InMemoryCache, split, HttpLink } from '@apollo/client';
    import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
    import { getMainDefinition } from '@apollo/client/utilities';
    
    const wsLink = new GraphQLWsLink(createClient({
      url: 'ws://localhost:4000/graphql',
    }));
    
    const httpLink = new HttpLink({
      uri: 'http://localhost:4000/graphql',
    });
    
    // Split based on operation type
    const splitLink = split(
      ({ query }) => {
        const definition = getMainDefinition(query);
        return (
          definition.kind === 'OperationDefinition' &&
          definition.operation === 'subscription'
        );
      },
      wsLink,
      httpLink
    );
    
    const client = new ApolloClient({
      link: splitLink,
      cache: new InMemoryCache(),
    });
    

    Contoh subscribe di React:

    import { gql, useSubscription } from '@apollo/client';
    
    const MESSAGE_POSTED = gql`
      subscription {
        messagePosted {
          id
          content
          user
        }
      }
    `;
    
    function Chat() {
      const { data } = useSubscription(MESSAGE_POSTED);
      // render chat messages pakai data
    }
    

Simulasi: Workflow Real-time Chat

  1. User A mengirim pesan via mutation postMessage.
  2. Server publish event ke semua subscriber.
  3. Semua client dengan subscription messagePosted otomatis menerima pesan baru tanpa reload.

Tabel Perbandingan Subscription vs Polling

FiturPolling setIntervalGraphQL Subscription (WebSocket)
LatencyTertunda hingga intervalHampir instan, waktu nyata
Resource ServerBanyak connection & CPUSatu koneksi & scalable
Pengiriman DataTidak efisien, fetch ulangHanya data event terbaru
SkalaKurang baikDirancang untuk fanout ribuan client
Real-timeSemuOtentik real-time

Best Practices dan Tantangan

  1. Scalability: Gunakan broker pub-sub (seperti Redis) untuk multi-instance server.
  2. Security: Selalu lakukan autentikasi saat handshake WebSocket.
  3. Backpressure & Disconnect: Siapkan logic untuk menangani jaringan buruk atau client disconnect.
  4. Protocol: Gunakan graphql-ws (bukan subscriptions-transport-ws, yang deprecated).
  5. Monitoring: Pantau koneksi aktif dan throughput WebSocket.

Penutup

WebSocket + GraphQL Subscriptions adalah kunci menghadirkan aplikasi real-time yang scalable dan elegan. Implementasinya memang teknis, tapi dengan fondasi yang kokoh, kita dapat membangun fitur kolaborasi, notifikasi, dan pelaporan instan hanya dengan beberapa baris kode tambahan dari API GraphQL biasa.

Ingat, real-time bukan lagi kemewahan, tapi kebutuhan mutlak aplikasi masa kini.


Ingin mengulik lebih jauh? Coba tambahkan Redis PubSub untuk scaling, atau gunakan paket seperti Hasura untuk pebangunan GraphQL real-time out of the box. Selamat berkreasi!


Referensi:

comments powered by Disqus