SinkronisasiReal-TimeSuperMulus:MengawinkanTanStackQueryDenganWebSockets
Menghubungkan aplikasi web dengan data real-time seringkali memicu redundansi fetching data. Artikel ini membahas cara mengintegrasikan event WebSockets dengan cache query milik TanStack Query secara efisien.
Halo gess! Di era aplikasi modern saat ini, fitur real-time synchronization sudah menjadi standar wajib. Pengguna tidak mau lagi menunggu halaman dimuat ulang secara manual.
Bayangkan kamu sedang membangun aplikasi chat room, kolaborasi dokumen real-time, atau dashboard pemantauan transaksi finansial. Pengguna tentu berharap data yang tampil di layar mereka langsung ter-update secara otomatis begitu ada transaksi baru masuk, tanpa harus menekan tombol refresh halaman berkali-kali.
Untuk mendapatkan data real-time, pendekatan klasik yang paling sering dipakai pemula adalah polling. Yaitu menembak API endpoint server setiap beberapa detik sekali menggunakan setInterval. Namun, polling manual ini sangat boros bandwidth internet klien dan membebani server backend karena harus memproses request berulang kali padahal datanya belum tentu berubah.
Alternatif yang jauh lebih efisien adalah menggunakan WebSockets. WebSockets membangun koneksi dua arah (duplex connection) yang tetap terbuka antara browser klien and server backend. Server bisa langsung membisikkan data baru ke klien secara real-time, begitu ada perubahan data di sisi database.
Namun, tantangan terbesarnya adalah bagaimana menyinkronkan data WebSocket instan tersebut dengan cache data fetching kita di frontend. Jika kita menggunakan TanStack Query (React Query) untuk fetching data, kita harus mencari cara agar event WebSocket dapat meng-update cache QueryClient secara cerdas tanpa harus memicu full refetch API.
Di artikel tutorial panjang ini, kita bakal membedah arsitektur integrasi ini. Kita akan membangun client hooks yang mendengarkan event WebSockets dan langsung memperbarui cache TanStack Query secara lokal. Let's dive in!
Tantangan Integrasi: Caching vs Push Data Real-Time
Saat kita menggunakan TanStack Query, library tersebut mengelola cache data di memori klien browser berdasarkan queryKey kustom yang kita definisikan. Misalnya, data daftar artikel di-cache dengan key ['articles']. Jika kita menerima event WebSocket berisi artikel baru yang baru saja dipublikasikan oleh user lain, bagaimana cara memperbarui daftar artikel tersebut?
Kita punya dua strategi utama. Strategi pertama adalah query invalidation. invalidateQueries({ queryKey: ['articles'] }). Strategi ini sangat mudah diimplementasikan, tapi masih menyisakan satu request HTTP tambahan ke server.
Strategi kedua adalah direct cache update. setQueryData. Strategi ini sangat efisien karena sama sekali tidak memicu request HTTP baru ke server. Aplikasi web terasa sangat instan dan responsif!
Implementasi Code: Setup WebSockets Listener & Cache Update
Mari kita langsung tulis kodenya. Kita berasumsi server WebSocket mengirimkan event dengan format event 'article:created' ketika ada artikel baru ditambahkan.
1import { useEffect } from 'react';2import { useQueryClient } from '@tanstack/react-query';34interface Article {5 id: string;6 title: string;7 excerpt: string;8}910export function useWebSocketSync() {11 const queryClient = useQueryClient();1213 useEffect(() => {14 // Membuka koneksi WebSocket ke server15 const ws = new WebSocket('wss://api.portoku.com/realtime');1617 ws.onmessage = (event) => {18 const message = JSON.parse(event.data);1920 if (message.type === 'article:created') {21 const newArticle: Article = message.payload;2223 // Memperbarui cache TanStack Query secara langsung!24 queryClient.setQueryData<Article[]>(['articles'], (oldData) => {25 if (!oldData) return [newArticle];26 // Menaruh artikel baru di baris paling atas27 return [newArticle, ...oldData];28 });29 }30 };3132 // Membersihkan koneksi saat komponen unmount33 return () => {34 ws.close();35 };36 }, [queryClient]);37}
Sederhana sekali kan? Fungsi setQueryData menerima queryKey target dan sebuah callback function. Callback function tersebut memberikan data cache lama (oldData), lalu kita mengembalikan array baru berisi artikel baru digabung dengan data lama. React component yang menggunakan useQuery untuk ['articles'] akan otomatis mendeteksi perubahan cache ini dan me-render ulang UI-nya secara instan!
Deep Dive: Optimistic Updates, Ping-pong, and Token Handshake
Tantangan berikutnya adalah bagaimana jika klien kita melakukan mutasi data (misalnya mem-posting komentar baru)? Daripada menunggu konfirmasi WebSocket dari server, kita bisa melakukan Optimistic Update. Yaitu memasukkan komentar tersebut ke dalam cache Query lokal secara instan seolah-olah penulisan ke database sudah sukses.
Namun, jika koneksi API kita tiba-tiba gagal di server, kita harus melakukan rollback data cache ke kondisi semula agar user tidak melihat data palsu. TanStack Query menyediakan property onMutate, onError, and onSettled pada hook useMutation.
Melalui onMutate, kita bisa menyimpan snapshot data cache lama sebelum di-update. Jika onError dipicu, kita tinggal mengembalikan status cache ke snapshot data lama tersebut. Ini adalah pola handling mutasi data paling premium dan profesional!
Untuk menjaga koneksi WebSocket tetap hidup, kita juga harus mengimplementasikan mekanisme Heartbeat atau ping-pong. Browser klien akan mengirimkan pesan ping kecil setiap 30 detik, dan server harus membalasnya dengan pong. Ini mencegah router modem atau load balancer memutus koneksi idle secara sepihak.
Terakhir, untuk keamanan WebSocket, kita harus mengirimkan JWT token saat proses jabat tangan (handshake) pertama kali. Kita bisa mengirimkan token ini sebagai query string di URL WebSocket wss://, lalu server memverifikasi keabsahan token tersebut sebelum membuka koneksi. Ini memastikan data real-time tidak disadap oleh orang luar!
Jika menggunakan backend dengan node-ws, pastikan kamu mendesain rate limiting untuk koneksi websocket. Terlalu banyak open socket connection secara bersamaan bisa melahap RAM server backend. Selalu bersihkan resource koneksi yang terputus secara berkala.
FAQ & Troubleshooting Real-time WebSockets Sync
Mari kita bahas beberapa pertanyaan and isu yang sering melanda saat mengawinkan WebSocket dan TanStack Query:
- Apakah WebSocket cocok untuk semua jenis data? Tidak. Gunakan WebSocket hanya untuk data dinamis tinggi. Untuk data statis, query HTTP standard dengan revalidate staleTime sudah sangat mencukupi.
- Bagaimana mengatasi tab browser inactive yang memutus socket? Browser modern sering melakukan pembekuan tab (tab freezing) untuk menghemat RAM laptop. Solusinya, pasang document visibility change listener, and pemicu reconnect socket secara otomatis saat user membuka tab kembali.
- Bagaimana jika server WebSocket crash? Selalu sediakan fallback HTTP API. Jika client mendeteksi WebSocket status CLOSED, alihkan data fetching menggunakan standard polling interval sampai server socket hidup kembali.
- Bagaimana cara mendesain Load Balancer untuk WebSocket? Kamu bisa menggunakan sticky sessions di Nginx atau Traefik, agar request user dialirkan ke server instance yang sama. Cermati setting routing websocket proxy di platform Coolify!
Menangani Reconnection & Offline States
Dalam skenario dunia nyata, koneksi internet user bisa terputus sewaktu-waktu (misal masuk ke terowongan kereta). Jika koneksi WebSocket terputus, kita kehilangan seluruh event real-time yang dikirim server. Oleh karena itu, kita butuh mekanisme auto-reconnection.
Saat koneksi WebSocket berhasil terhubung kembali setelah putus, kita harus berasumsi bahwa data cache kita di memori browser sudah usang karena kita melewatkan event di masa offline.
1ws.onclose = () => {2 console.log('Koneksi terputus, mencoba menghubungkan kembali...');3 // Pemicu reconnect logic...4};56ws.onopen = () => {7 console.log('Koneksi terhubung kembali! Membaca ulang data...');8 // Force invalidasi cache query untuk sinkronisasi ulang9 queryClient.invalidateQueries({ queryKey: ['articles'] });10};
Dengan memicu invalidasi cache saat koneksi kembali online, kita memastikan tidak ada data yang hilang (data drift) antara client browser dan database server. Ini adalah pola arsitektur yang sangat kokoh untuk aplikasi skala produksi.
Strategi Multiplexing & Skalabilitas Koneksi WebSocket
Ketika jumlah user aplikasi kita meledak, membuka satu koneksi WebSocket untuk setiap tab browser yang dibuka user akan membuat backend server overload. Bayangkan jika satu user membuka 5 tab dashboard, server harus melayani 5 open connection. Solusinya, kita bisa menggunakan SharedWorker untuk membagi satu koneksi WebSocket di antara beberapa tab browser yang aktif. Ini menghemat penggunaan resource backend.
Selain itu, untuk meminimalkan beban memori server, kita bisa menggunakan teknik event multiplexing. Alih-alih membuat socket server terpisah untuk setiap domain data, kita menyatukan semua event payload ke dalam satu koneksi socket tunggal. Klien browser kemudian mem-parsing tipe event (seperti 'chat:new' or 'product:update'), lalu meneruskannya ke queryClient cache kustom kita.
Jangan lupa untuk selalu membatasi ukuran payload data yang dikirim lewat socket. Jangan kirim seluruh isi row database. Cukup kirim field yang berubah or ID record saja, lalu biarkan client melakukan query manual ke cache jika butuh detail lengkap. Ini menjaga bandwidth server tetap aman.
Kesimpulan
Mengawinkan TanStack Query dan WebSockets memberikan kombinasi performa yang luar biasa. TanStack Query menangani data fetching awal, status loading, and caching secara modular. Sementara WebSockets memberikan dorongan push data real-time instan.
Dengan memperbarui cache lokal menggunakan setQueryData, kita melenyapkan jeda waktu request HTTP dan memberikan pengalaman pengguna (UX) yang sangat mulus tanpa refresh screen. Terapkan pola ini pada project-mu berikutnya untuk hasil UI yang premium! Selamat mencoba!
Glosarium Penting WebSockets & Sync Cache
Full-duplex Connection adalah komunikasi dua arah di mana client and server bisa saling mengirim data secara simultan kapan saja melalui satu open TCP connection.
Query Invalidation adalah mekanisme memberi tahu TanStack Query bahwa cache data tertentu sudah kadaluarsa (stale), sehingga memicu refresh data ke HTTP API server.
Optimistic Update adalah trik performa UI di mana kita mem-bypass loading state dengan memasukkan data baru ke cache secara instan, lalu melakukan rollback jika request API gagal.
Heartbeat Ping-pong adalah pengiriman packet data kecil secara konstan antara browser client and server socket untuk menjaga agar koneksi tidak diputus oleh router.
Token Handshake adalah proses verifikasi kredensial JWT saat browser pertama kali membuka koneksi wss:// ke server socket.
Query Cache adalah tempat penyimpanan data HTTP request di memory browser client yang dikelola secara otomatis oleh TanStack Query.
Data Drift adalah kondisi di mana data di client browser tidak sinkron dengan data di server database karena ada event offline.
Stale Time adalah durasi waktu di mana data cache dianggap masih segar, sehingga TanStack Query tidak perlu menembak HTTP API ulang.
Mutation adalah operasi untuk menulis, meng-update, or menghapus data di server database yang dikelola dengan hook useMutation.
Sinkronisasi Saat Koneksi Terputus (Offline Sync)
Dalam aplikasi real-time, skenario terputusnya koneksi internet adalah hal yang sangat wajar terjadi. Untuk menangani hal ini, TanStack Query menyediakan fitur sinkronisasi offline yang terintegrasi dengan online status manager. Ketika koneksi internet terputus, query akan menunda fetching dan mutasi akan masuk ke dalam queue antrean lokal. Begitu koneksi internet kembali aktif, TanStack Query secara otomatis memicu sinkronisasi ulang dan mengirimkan event akumulasi ke server melalui WebSockets, memastikan data client dan server kembali konsisten tanpa intervensi manual dari pengguna.
Selain itu, kita juga bisa memanfaatkan mekanisme queryClient.setQueryData untuk melakukan optimistic updates pada cache lokal sebelum request ke server benar-benar selesai dikirim. Jika terjadi kegagalan jaringan yang permanen, TanStack Query akan melakukan rollback otomatis ke state sebelumnya menggunakan backup cache yang disimpan di context mutation. Pola ini sangat krusial untuk menjaga agar UI tetap responsif dan tidak terkesan freeze saat jaringan internet user sedang tidak stabil.
(Share)