Belajar tRPC - Resilience & Fault Tolerance
Series/Belajar tRPC/Episode 15
Episode 15 of 19

Belajar tRPC - Resilience & Fault Tolerance

Episode ini memperkuat ketahanan aplikasi tRPC: retry dengan exponential backoff dan error boundary di client, strategi fallback data serta penanganan downtime, dan graceful shutdown untuk server di environment production.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Jaringan itu tidak dapat diandalkan — server restart, koneksi putus, dan database sesekali lambat. Resilience adalah kemampuan aplikasi tetap berfungsi saat kegagalan terjadi. Episode 15 membahas pola ketahanan di client: retry dengan exponential backoff dan error boundary, strategi fallback data, serta graceful shutdown di server.

Tujuannya bukan menghilangkan kegagalan — itu mustahil — melainkan memastikan kegagalan tidak menggagalkan seluruh pengalaman.

Retry dan Exponential Backoff di Client

Retry Otomatis React Query

React Query melakukan retry otomatis pada query yang gagal. Kalian bisa mengatur berapa kali dan jedanya:

Retry dengan exponential backoff
import { QueryClient } from "@tanstack/react-query";
 
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: (failureCount, error) => {
        if (error.data?.code === "UNAUTHORIZED") return false;
        return failureCount < 3;
      },
    },
  },
});

retry menerima jumlah kegagalan dan error. Di contoh, request yang gagal karena UNAUTHORIZED tidak diulang — percuma mengulang masalah otentikasi — sementara kegagalan lain diulang maksimal tiga kali.

Retry Delay Default

React Query menambahkan jeda retry dengan pertumbuhan eksponensial secara bawaan. Jika perlu kontrol penuh, atur retryDelay:

Mengatur jeda retry
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: 3,
      retryDelay: (attemptIndex) =>
        Math.min(1000 * 2 ** attemptIndex, 10_000),
    },
  },
});

retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 10_000) menerapkan jeda 1 detik, 2 detik, 4 detik, dengan batas atas 10 detik — exponential backoff standar yang tidak membebani server saat sedang bermasalah.

Jangan Retry Mutation Secara Buta

Berbeda dengan query, mutation mengubah data. Mengulang mutation yang sebenarnya sudah sukses di server bisa menghasilkan data ganda. Atur retry pada mutation menjadi kecil atau gunakan idempotency key bila provider mendukungnya.

Error Boundary dan Fallback Data

Error Boundary di React

Error boundary menangkap kegagalan render di komponen anak. Untuk data dari tRPC, padukan dengan UI fallback:

Error boundary sederhana
class ErrorBoundary extends React.Component {
  state = { hasError: false };
 
  static getDerivedStateFromError() {
    return { hasError: true };
  }
 
  render() {
    if (this.state.hasError) {
      return <p>Terjadi kesalahan. Silakan muat ulang.</p>;
    }
    return this.props.children;
  }
}

Bungkus bagian halaman yang bergantung data jaringan dengan ErrorBoundary, sehingga satu komponen yang gagal tidak meruntuhkan seluruh halaman.

Strategi Fallback Data

Saat server sedang downtime, tampilkan data yang masih tersedia daripada layar kosong:

Fallback data dengan cache
const { data, error } = trpc.post.list.useQuery(undefined, {
  retry: 2,
  staleTime: 60_000,
  placeholderData: () => dataPostSebelumnya,
});
 
if (error && !data) {
  return (
    <div>
      <p>Server sedang gangguan. Menampilkan data terakhir yang tersimpan.</p>
      <CacheTerakhir />
    </div>
  );
}

Pola di atas memakai cache React Query sebagai fallback: selama ada data, halaman tetap menampilkan isi meskipun request terbaru gagal. Persist cache ke localStorage dengan @tanstack/query-persist-client jika ingin fallback bertahan lintas sesi.

Mendeteksi Downtime

Untuk memberi tahu user lebih awal, pantau kegagalan berulang lewat onError:

Mendeteksi pola kegagalan
const { data } = trpc.health.check.useQuery(undefined, {
  refetchInterval: 30_000,
  onError: () => setServerDown(true),
  onSuccess: () => setServerDown(false),
});

refetchInterval: 30_000 memeriksa kesehatan server setiap 30 detik. Status serverDown bisa menampilkan banner "Mode offline" kepada user.

Graceful Shutdown di Server

Mengapa Harus Graceful

Saat server dimatikan, request yang sedang berjalan harus diberi waktu selesai, bukan diputus paksa. Graceful shutdown menutup listener baru, menunggu request aktif selesai, lalu menutup koneksi:

Graceful shutdown server tRPC
const server = createHTTPServer({
  router: appRouter,
  createContext: () => ({}),
}).listen(3000);
 
const shutdown = async (signal: string) => {
  console.log(`Menerima ${signal}, memulai shutdown...`);
  server.close(async () => {
    await tutupDatabase();
    console.log("Shutdown bersih selesai");
    process.exit(0);
  });
  setTimeout(() => process.exit(1), 10_000).unref();
};
 
process.on("SIGTERM", () => shutdown("SIGTERM"));
process.on("SIGINT", () => shutdown("SIGINT"));

server.close berhenti menerima koneksi baru dan menunggu request yang sedang berjalan selesai. Setelah itu database ditutup rapi. setTimeout(...).unref() adalah pengaman: jika proses macet lebih dari 10 detik, paksa keluar agar orchestrator tidak menggantung.

Perhatian untuk Subscription

WebSocket juga harus ditutup saat shutdown — tutup server ws lebih dulu, kirim sinyal penutupan ke client yang terhubung, lalu tutup server HTTP. Ini mencegah client menggantung menunggu event yang tidak akan datang.

Warning

Jangan panggil process.exit tanpa menutup resource. Di Kubernetes dan container, proses yang keluar paksa meninggalkan koneksi database dan WebSocket yang putus mendadak — yang bisa meracuni connection pool instance berikutnya.

Penutup

Episode 15 memperkuat ketahanan aplikasi tRPC: retry dengan exponential backoff yang cerdas di client, error boundary dan fallback data untuk menjaga UI tetap berfungsi, serta graceful shutdown agar server berhenti dengan bersih di production.

Inti yang harus dibawa pulang:

  • Atur retry dengan bijak; jangan retry error UNAUTHORIZED.
  • Exponential backoff menghindari banjir request saat server bermasalah.
  • Mutation perlu retry yang lebih hati-hati karena mengubah data.
  • Error boundary membatasi dampak kegagalan pada satu bagian UI.
  • Cache yang dipersist bisa menjadi fallback saat downtime.
  • Graceful shutdown menunggu request selesai sebelum keluar.

Di episode 16 selanjutnya kita akan membahas tRPC di Next.js, Remix, dan Serverless — integrasi dengan Next.js App Router dan pages router, penggunaan di Remix, Astro, atau framework Node.js lain, serta deployment serverless di Vercel, Cloudflare Pages, dan AWS Lambda.

Belajar tRPC - Resilience & Fault Tolerance | Belajar tRPC