Serverless bukan berarti tanpa server. Server-nya tetap ada, tapi dikelola sepenuhnya oleh provider: kamu cuma menulis sebuah function, dan provider yang menjalankannya setiap ada request, menambah salinannya saat ramai, dan mematikannya saat sepi. Ingat juru masak panggilan dari pelajaran Cloud Computing Basics. Dia cuma datang kalau ada pesanan. Pesanan pertama setelah sepi harus menunggu dia datang dan menyalakan kompor (cold start), tapi kalau dia masih di dapur dari pesanan sebelumnya, dia langsung memasak (warm start). Dia dibayar per piring dan per menit memasak, gak ingat pesananmu kemarin (stateless), punya batas waktu per pesanan (timeout), dan kalau pesanan membludak datang banyak juru masak sekaligus, padahal kompor di dapurmu terbatas (koneksi database).
🧪 Tebakan cepat: function
ambil-catatanpunyalet dipanggil = 0di luar handler, dan tiap request menambahnya 1. Lima request datang berurutan, berjarak beberapa detik. Berapa nilaidipanggildi request kelima? Dan kalau request keenam datang setelah 15 menit sepi? Tebak dulu, jawabannya ada di bagian "Stateless: memori instance gak bisa diandalkan".
Dari route ke function
Route GET /catatan/:id dari catatan-api dipindah jadi function di AWS Lambda. Gak ada app.listen: provider yang memanggil handler untuk setiap request.
// ambil-catatan.mjs (AWS Lambda, Node 20)
import pg from "pg";
// Jalan sekali per instance, saat cold start
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL, max: 1 });
let dipanggil = 0;
export const handler = async (event) => {
dipanggil += 1;
const { rows } = await pool.query("SELECT id, judul, isi FROM catatan WHERE id = $1", [event.pathParameters.id]);
if (rows.length === 0) {
return { statusCode: 404, body: JSON.stringify({ error: "Catatan tidak ditemukan" }) };
}
return { statusCode: 200, body: JSON.stringify(rows[0]) };
};
eventberisi request-nya: path, query, header, body.event.pathParameters.idadalah:iddi URL.- Handler mengembalikan object
{ statusCode, body }, danbodyharus string, makanyaJSON.stringify. - Kode di luar handler (
import,new pg.Pool,let dipanggil) jalan sekali saat sebuah instance dinyalakan, lalu dipakai ulang oleh request berikutnya di instance yang sama.
Cold start dan warm start
Di Lab, waktunya pakai model contoh (bukan ukuran pasti dari provider mana pun): menyalakan runtime Node 250 ms, membuka koneksi Postgres 50 ms, query 20 ms, sisa kerja handler 5 ms.
| Request | Yang terjadi | Waktu |
|---|---|---|
| ke-1 | cold: instance baru, runtime menyala, koneksi dibuka, lalu handler | 250 + 50 + 25 = 325 ms |
| ke-2 sampai ke-5 | warm: instance yang sama, langsung handler | 25 ms |
| ke-6, setelah 15 menit sepi | instance sudah dibuang provider, cold lagi | 325 ms |
| 5 request bersamaan | satu instance Lambda cuma melayani satu request pada satu waktu, jadi 4 instance baru | 1 × 25 ms, 4 × 325 ms |
Provider membuang instance yang lama diam (di Lab setelah 10 menit; provider asli gak menjanjikan angka pasti). Cara mengurangi cold start dibahas di Serverless Patterns & Cold Start.
Stateless: memori instance gak bisa diandalkan
Lima request berurutan mendarat di instance yang sama, jadi dipanggil di request kelima bernilai 5. Request keenam setelah 15 menit sepi dapat instance baru, jadi nilainya kembali 1. Itu jawaban tebakannya. Kalau lima request datang bersamaan, mereka tersebar ke beberapa instance, dan masing-masing punya dipanggil sendiri.
Jadi variabel di memori, cache di memori, dan file di /tmp cuma bonus yang bisa hilang kapan saja. Data yang harus awet disimpan di database, Redis, atau object storage.
Timeout
Tiap function punya batas waktu. Di AWS Lambda bawaannya 3 detik, maksimal 15 menit. Lewat dari itu, eksekusinya dihentikan dan log-nya mencetak Task timed out after 3.00 seconds. Di Lab, query lambat 4 detik selalu kena batas ini. Kerja panjang seperti membuat laporan bulanan atau memproses video dipecah, atau dikirim ke antrean (queue) dan dikerjakan worker.
Function dan database
Koneksi dibuka di luar handler, supaya request warm memakai ulang koneksi yang sama. Kalau dibuka di dalam handler, tiap request membayar 50 ms lagi (75 ms, bukan 25 ms).
Masalah berikutnya ada di jumlah. Tiap instance memegang koneksinya sendiri, dan provider bisa menyalakan ratusan instance sekaligus. Di Lab, 120 request bersamaan berarti 120 instance dan 120 koneksi, padahal Postgres-nya cuma menerima 100 (max_connections), jadi 20 request gagal dengan FATAL: sorry, too many clients already. Jalan keluarnya pooler di antara function dan database, seperti RDS Proxy atau PgBouncer, yang membagi sedikit koneksi asli ke banyak instance. Detailnya ada di pelajaran Connection Pooling.
Bayar per pemakaian
Serverless memakai model pay-per-use: kamu bayar sesuai jumlah eksekusi (request) dan durasinya (dikali memori yang dipilih, dihitung dalam GB-detik). Gak ada request, tagihannya nyaris nol, karena instance yang menganggur dibuang. Ini beda dengan VPS yang ditagih per jam walau sepi.
Cocok untuk: endpoint API dengan traffic naik turun, webhook, cron job, memproses gambar yang di-upload. Kurang cocok untuk: kerja yang lebih lama dari batas timeout, koneksi yang harus terus terbuka seperti WebSocket, dan traffic tinggi yang stabil sepanjang hari, yang biasanya lebih murah di server biasa. Lambda, Workers, dan Vercel dibahas lebih dalam di AWS Lambda Deep Dive, Cloudflare Workers & Edge Computing, dan Vercel Edge Functions & Middleware.
Di latihan: simulateLambda
simulateLambda(handler, event) meniru runtime: panggil handler(event), lalu kembalikan { statusCode, body, executionTime: "<1ms" }. Kalau handler melempar error, tangkap dengan try...catch dan kembalikan { statusCode: 500, body: error.message, executionTime: "<1ms" }, persis seperti runtime yang menjawab 500 saat function-mu crash.
⚠️ Jebakan yang sering ketemu
- Menyimpan state di memori instance. Hilang saat instance dibuang, dan beda di tiap instance. Simpan di database atau Redis.
- Membuka koneksi database di dalam handler. Tiap request membayar handshake lagi. Buka di luar handler, dan pakai pooler saat instance-nya banyak.
- Lupa timeout. Kerja panjang terpotong di tengah dengan
Task timed out. Pecah kerjanya atau pindah ke queue. - Body yang bukan string.
body: rows[0](object) bukan response yang benar; pakaiJSON.stringify.
TL;DR: Serverless = server dikelola provider, kamu cuma menulis function handler(event) yang mengembalikan { statusCode, body }. Provider menyalakan instance saat ada request: request pertama kena cold start (di Lab 325 ms), request berikutnya di instance yang sama warm (25 ms), dan instance yang lama diam dibuang sehingga cold lagi. Satu instance melayani satu request pada satu waktu, jadi request bersamaan menyalakan banyak instance. Function itu stateless: memori dan /tmp bisa hilang kapan saja. Ada timeout (Lambda bawaannya 3 detik, maksimal 15 menit). Buka koneksi database di luar handler dan pakai pooler supaya max_connections gak habis. Bayarnya pay-per-use: jumlah eksekusi dan durasi.