Client-server adalah cara paling dasar menyusun aplikasi web: client (browser, aplikasi HP) memulai percakapan dengan mengirim request, dan server menyediakan layanan atau resource, memproses request itu, lalu mengirim response. Bayangin restoran. Kamu (client) memesan ke pelayan, dapur (server) memasak, dan bahan diambil dari gudang (database). Kamu gak pernah masuk gudang sendiri, dan dapur gak pernah datang ke mejamu tanpa dipesan.
🧪 Tebakan cepat: Budi di Jakarta membuka halaman Konser Nusantara untuk pertama kali. Server TiketNada di Singapura, dan satu kali bolak-balik ke sana makan 20 ms. Berapa kali bolak-balik terjadi sebelum isi halaman sampai ke browser Budi: 1, 2, atau 3? Tebak dulu, jawabannya ada di bagian "Perjalanan satu request".
Client dan server
- Client memulai. Dia mengirim request, menunggu, lalu menampilkan hasilnya. Browser Budi dan aplikasi TiketNada di HP adalah client.
- Server menunggu. Dia menyediakan layanan, misalnya data konser dan pemesanan, menjawab tiap request yang datang, dan gak pernah memulai percakapan duluan.
Satu server melayani banyak client sekaligus. Itu sebabnya kerja berat dan data penting ditaruh di server: client bisa dimatikan, diutak-atik, atau dipalsukan, server gak.
Perjalanan satu request
Satu kalimat dulu: RTT (round-trip time) adalah waktu sebuah paket pergi ke server dan jawabannya kembali. Jakarta ke Singapura sekitar 20 ms. Ini perjalanan GET https://tiketnada.test/api/events/42 saat Budi baru pertama membuka halaman (angka model contoh):
| Tahap | Lama | ms ke- |
|---|---|---|
DNS: cari alamat IP tiketnada.test |
40 ms | 0 sampai 40 |
| TCP handshake: buka koneksi | 1 RTT = 20 ms | 40 sampai 60 |
| TLS 1.3 handshake: sepakati enkripsi HTTPS | 1 RTT = 20 ms | 60 sampai 80 |
| Request menuju server | ½ RTT = 10 ms | 80 sampai 90 |
| Server menjalankan kode | 8 ms | 90 sampai 98 |
| Server menjalankan 3 query database | 3 × 4 ms = 12 ms | 98 sampai 110 |
| Response kembali ke browser | ½ RTT = 10 ms | 110 sampai 120 |
Totalnya 120 ms, dan cuma 20 ms yang dipakai server bekerja. Sisanya perjalanan. Jawaban tebakannya 3 bolak-balik: TCP, TLS, lalu request dan response-nya. DNS juga perjalanan, tapi ke server lain: resolver DNS, bukan server TiketNada.
Request kedua jauh lebih murah. Alamat IP sudah di-cache, dan koneksi TCP plus TLS tetap terbuka (keep-alive), jadi tinggal 1 RTT + kerja server: 40 ms. Kalau server TiketNada dipindah ke Virginia, Amerika Serikat (RTT 230 ms), request pertama jadi 40 + 3 × 230 + 20 = 750 ms, padahal kodenya sama persis. Jarak ke pengguna itu mahal.
Tiga tier: presentation, application, data
Aplikasi web biasanya dipisah jadi tiga lapis, atau tier:
| Tier | Di TiketNada | Tugasnya |
|---|---|---|
| Presentation | browser, aplikasi HP | menampilkan halaman dan menerima klik |
| Application | server app |
aturan bisnis: maksimal 4 tiket, cek login, hitung harga |
| Data | database PostgreSQL | menyimpan konser, kursi, dan pesanan |
Di arsitektur 2-tier, client bicara langsung ke database. Lebih sederhana, tapi password database ikut ke client, dan aturan bisnis bisa dilewati. Di 3-tier, tiap tier bisa diubah dan di-scale sendiri tanpa mengganggu yang lain: server aplikasi bisa ditambah jadi tiga tanpa mengubah tampilan, dan database bisa diganti tanpa mengubah client.
Ini kedua sisinya dalam kode:
// client (browser): tier presentation
const res = await fetch("https://tiketnada.test/api/events/42");
const konser = await res.json();
console.log(konser.nama, konser.sisaKursi);
// server (Node.js + Express + node-postgres): tier application
app.get("/api/events/:id", async (req, res) => {
const id = req.params.id;
const event = await pool.query("SELECT id, nama, tanggal FROM events WHERE id = $1", [id]);
const kategori = await pool.query("SELECT nama, harga FROM ticket_types WHERE event_id = $1", [id]);
const sisa = await pool.query("SELECT COUNT(*) AS n FROM seats WHERE event_id = $1 AND status = 'kosong'", [id]);
res.json({ ...event.rows[0], kategori: kategori.rows, sisaKursi: Number(sisa.rows[0].n) });
});
fetch(...)di client cuma tahu alamat API. Dia gak tahu ada database, apalagi password-nya.app.get("/api/events/:id", ...)adalah server yang menunggu request ke path itu.req.params.idberisi"42".- Tiga
pool.query(...)adalah tiga perjalanan ke tier data. Itu 3 query di tabel tadi. res.json(...)mengirim response, danfetchdi client menerimanya.
Stateless dan stateful
TiketNada sekarang punya dua server aplikasi, app-1 dan app-2, di belakang load balancer. Siti login, dan app-1 menyimpan session-nya di memori app-1. Itu stateful: server mengingat informasi client di antara request. Request Siti berikutnya diarahkan ke app-2, yang gak punya session itu, jadi jawabannya 401 Unauthenticated. Siti tiba-tiba ter-logout.
Server stateless gak menyimpan state client di memorinya sendiri. Tiap request membawa semua yang dibutuhkan, misalnya token yang ditandatangani dan bisa diperiksa server mana saja, atau session disimpan di tempat bersama seperti Redis. Request Siti boleh jatuh ke server mana pun dan hasilnya sama. Karena itu server stateless gampang di-scale secara horizontal: tinggal tambah server. Detail session dan token ada di track Auth.
🤔 REST, GraphQL, gRPC
Cara client dan server bercakap juga dipilih. REST: URL per resource (/api/events/42) dan method HTTP, sederhana dan gampang di-cache, cocok untuk kebanyakan API. GraphQL: satu endpoint, client menulis query berisi field yang dia butuhkan, berguna kalau tiap layar butuh bentuk data berbeda. gRPC: format biner (Protocol Buffers) di atas HTTP/2, cepat dan bertipe, biasanya untuk percakapan antarserver di dalam sistem, bukan dari browser.
⚠️ Jebakan yang sering ketemu
- Server jauh dari pengguna. Kode yang sama 120 ms dari Singapura dan 750 ms dari Virginia. Taruh server dekat pengguna, atau pakai CDN untuk yang statis.
- Server aplikasi dan database beda region. Tiap query menyeberang jauh, jadi 3 query × 23 ms = 69 ms, bukan 12 ms. Taruh berdekatan.
- Session di memori satu server. Begitu ada server kedua, pengguna ter-logout acak. Jadikan server stateless.
- Client bicara langsung ke database. Kredensial database bocor ke client, dan aturan bisnis bisa dilewati.
TL;DR: Client memulai dan menampilkan, server menunggu, menyediakan layanan, dan menjawab. Request HTTPS pertama butuh DNS lalu 3 RTT (TCP, TLS, request-response) plus kerja server: 120 ms dari Jakarta ke Singapura, 750 ms ke Virginia. Request berikutnya memakai ulang koneksi (keep-alive), jadi tinggal 1 RTT + kerja server. Arsitektur 3-tier memisahkan presentation (browser), application (server), dan data (database), sehingga tiap layer bisa diubah dan di-scale sendiri. Stateful = server menyimpan session di memorinya; stateless = tiap request membawa semua yang dibutuhkan, sehingga server mana pun bisa menjawab dan scaling horizontal jadi gampang.