Episode ini membedah lapisan transport tRPC: HTTP batch dengan httpBatchLink, standard HTTP dengan httpLink, dan WebSocket dengan wsLink. Kalian juga belajar konfigurasi CORS, request headers, dan setup server subscription di Express.

Semua type-safety tRPC pada akhirnya melewati jaringan. Episode 10 membedah transport — lapisan yang menentukan bagaimana request dikirim. Kalian akan memahami perbedaan httpLink, httpBatchLink, dan wsLink, kapan memakai masing-masing, serta konfigurasi pendukung seperti CORS, request headers, dan proxy.
Pilihan transport yang tepat berpengaruh langsung pada latensi dan pengalaman user, jadi pahami karakteristik tiap link dengan baik.
httpLink mengirim setiap operasi sebagai request HTTP terpisah. Cocok untuk mutation dan aplikasi dengan sedikit query:
import { createTRPCClient, httpLink } from "@trpc/client";
const client = createTRPCClient<AppRouter>({
links: [httpLink({ url: "http://localhost:3000/trpc" })],
});
const user = await client.user.byId.query({ id: 1 });httpLink({ url }) mengirim operasi dengan metode POST ke URL yang ditentukan. Karena satu operasi satu request, model ini sederhana dan cocok ketika batching tidak memberi manfaat besar.
httpBatchLink menggabungkan banyak operasi yang terjadi bersamaan menjadi satu request. Query yang dipanggil beberapa komponen sekaligus akan dikirim sekaligus:
import { httpBatchLink } from "@trpc/client";
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({ url: "http://localhost:3000/trpc" }),
],
});
const [user, posts] = await Promise.all([
client.user.byId.query({ id: 1 }),
client.post.list.query({ authorId: 1 }),
]);Dengan httpBatchLink, dua query di atas berjalan dalam satu round-trip HTTP — server memproses keduanya dan mengembalikan hasil dalam satu response. Ini penghematan latensi yang besar di aplikasi dengan banyak query per halaman. Catatan penting: mutation tidak boleh di-batch. Jika perlu, pakai splitLink untuk mengarahkan mutation ke httpLink.
Subscription membutuhkan koneksi persisten. wsLink menjaga koneksi WebSocket dan mengalirkan event real-time:
import { wsLink } from "@trpc/client";
const client = createTRPCClient<AppRouter>({
links: [
wsLink({
url: "ws://localhost:3000/trpc",
}),
],
});
const unsub = client.clock.subscribe(undefined, {
onData: (data) => console.log("Waktu baru:", data.waktu),
});client.clock.subscribe(undefined, { onData }) membuka stream dan memanggil onData setiap kali server mengirim event. Karena WebSocket selalu terbuka, ini ideal untuk notifikasi, chat, dan update harga.
Gunakan splitLink agar satu client menangani ketiganya — persis pola dari episode 6:
links: [
splitLink({
condition: (op) => op.type === "subscription",
true: wsLink({ url: "ws://localhost:3000" }),
false: httpBatchLink({ url: "http://localhost:3000/trpc" }),
}),
],Subscription lewat WebSocket, sementara query dan mutation lewat HTTP batch — kombinasi yang paling umum di production.
Di sisi server, WebSocket dilayani bersama HTTP. Memakai wsServer (di tRPC v10 bernama applyWSSHandler) yang mengimpor router yang sama:
import { createHTTPServer } from "@trpc/server/adapters/standalone";
import { wsServer } from "@trpc/server/adapters/ws";
import { appRouter } from "./routers";
const { server } = createHTTPServer({
router: appRouter,
createContext: () => ({}),
});
wsServer({
wss: new WebSocketServer({ server }),
router: appRouter,
createContext: () => ({}),
});
server.listen(3000);Router yang sama melayani HTTP dan WebSocket sekaligus — tidak ada duplikasi definisi. WebSocketServer diimpor dari paket ws, dependency yang perlu diinstall:
npm install ws
npm install --save-dev @types/wsSaat client dan server berbeda domain, CORS harus dikonfigurasi. Di Express, cukup pasang middleware sebelum tRPC:
app.use(cors({ origin: ["http://localhost:5173"], credentials: true }));Jika otentikasi memakai cookie, wajib mengaktifkan credentials: true dan menentukan origin secara eksplisit — wildcard tidak didukung untuk cookie.
Client bisa menambahkan header lewat opsi headers di link, atau memanfaatkan op.context untuk nilai dinamis:
import { httpBatchLink } from "@trpc/client";
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: "/api/trpc",
headers: () => ({
Authorization: `Bearer ${getToken()}`,
}),
}),
],
});headers: () => ({ Authorization }) menambahkan token ke setiap request. Di sisi server, token dibaca dari ctx.req.headers pada middleware otentikasi — alurnya akan lengkap di episode 11.
tRPC berjalan normal di belakang reverse proxy seperti Nginx atau Traefik selama path di-route ke aplikasi. Yang perlu dijaga: WebSocket membutuhkan upgrade header yang diteruskan proxy, dan CORS tetap dikonfigurasi di aplikasi karena header dipertahankan proxy.
Warning
Jangan menaruh token auth di dalam URL query. Gunakan header Authorization. Query string umumnya terekam di log server dan proxy — header yang benar mencegah token bocor lewat log.
Episode 10 menuntaskan lapisan transport: httpLink untuk request tunggal, httpBatchLink untuk penghematan round-trip, wsLink untuk real-time, serta konfigurasi CORS, headers, dan proxy agar server siap diakses dari mana saja.
Inti yang harus dibawa pulang:
httpLink satu request per operasi; httpBatchLink menggabungkan query.splitLink.wsLink dibutuhkan untuk subscription real-time.wsServer (v10: applyWSSHandler) melayani WebSocket dengan router yang sama.credentials: true diperlukan saat client berbeda domain.Di episode 11 selanjutnya kita akan membahas otentikasi & authorization di tRPC — middleware autentikasi dengan token, session, atau cookie, authorization per procedure dan RBAC, serta integrasi dengan NextAuth, Clerk, atau custom auth provider.