Bagaimana Kami Membangun Aplikasi Produksi di Cloudflare Workers
16 April 2026
Di posting sebelumnya, kami menjelaskan mengapa kami membangun Shrtnr dan apa yang digantikannya. Posting ini membahas lebih dalam arsitekturnya: runtime, database, dan pilihan framework yang membuatnya bekerja.
Shrtnr dimulai sebagai alat internal dan menjadi proyek open-source karena keputusan arsitektur di baliknya layak untuk dibagikan. Inilah stack dan alasan di baliknya.
Masalah dengan "tinggal deploy saja ke server"
Sebagian besar URL shortener mengikuti pola yang familiar. Sebuah layanan redirect berjalan di container atau VPS. Request masuk, mengenai application server, melakukan query ke database, dan mengembalikan redirect 301 atau 302. Ini berfungsi. Tapi juga lambat bagi siapa pun yang tidak berada dekat secara geografis dengan lokasi server tersebut.
Jika seorang pengguna di Stockholm mengklik tautan pendek, dan servernya ada di Amerika Serikat, redirect harus menyeberangi Atlantik sebelum bisa mulai berjalan. Tambahkan resolusi DNS, TLS handshake, pencarian di aplikasi, dan perjalanan respons kembali, dan Anda sering melihat beberapa ratus milidetik sebelum browser mulai memuat tujuan sebenarnya. Untuk alat yang tugasnya hanya menyingkir secepat mungkin, itu banyak waktu yang terbuang untuk hal yang tidak berguna.
Kami menginginkan sesuatu yang berbeda. Setiap redirect harus di-resolve dekat dengan orang yang mengkliknya. Tidak ada ketergantungan pada application server terpusat. Tidak ada cold start. Arsitekturnya harus global secara default, bukan melalui konfigurasi.
Cloudflare Workers: apa arti sebenarnya dari "berjalan di edge"
Shrtnr berjalan di Cloudflare Workers. Bukan di container. Bukan di VPS di belakang load balancer. Ia berjalan di jaringan edge itu sendiri, terdistribusi di 300+ lokasi secara global.
Ketika seseorang mengklik tautan pendek di Jakarta, Tokyo, atau Stockholm, request ditangani oleh Cloudflare Worker yang berjalan di lokasi edge terdekat. Komputasi terjadi dekat dengan pengguna, yang menghilangkan application server terpusat dari persamaan. Dalam praktiknya, pembacaan database mungkin masih melibatkan latensi lintas wilayah tergantung di mana data D1 Anda berada, tetapi model eksekusi ini menghilangkan bottleneck origin server yang membuat shortener tradisional lambat.
Ini bukan sekadar trik performa. Ini mengubah arsitektur di setiap level. Workers adalah request handler yang berumur pendek, stateless, tanpa akses filesystem, dan dengan batasan memori yang ketat. Anda tidak bisa memindahkan aplikasi Node.js yang sudah ada ke Workers dan berharap yang terbaik. Anda harus mendesain untuk model eksekusinya, atau Anda akan melawannya sepanjang waktu.
Arsitektur yang baik dimulai dengan memilih runtime yang tepat untuk masalahnya, bukan dengan memilih yang paling familiar. Untuk beban kerja Shrtnr — redirect yang terdistribusi secara global, sensitif terhadap latensi, dengan analitik yang bisa di-query — Cloudflare Workers adalah pilihan yang paling jelas, dan aplikasinya dirancang untuk runtime ini sejak awal.
Mengapa kami memilih D1 daripada KV (dan mengapa itu penting)
Pembaruan (April 2026): kami sejak itu menambahkan KV sebagai write-through cache di depan D1 untuk jalur panas redirect. D1 tetap sumber kebenaran untuk semua hal di bawah; KV menangani resolusi slug di edge. Baca kelanjutannya →
Ini adalah salah satu keputusan yang membedakan Shrtnr dari setiap tutorial URL shortener lainnya yang Anda temukan secara online. Hampir setiap panduan "membangun URL shortener di Cloudflare" menggunakan KV (Key-Value) storage. Ini masuk akal untuk kasus paling sederhana: simpan pemetaan slug-ke-URL, cari saat setiap request, kembalikan redirect.
Tapi KV bersifat eventually consistent. Penulisan menyebar secara global, tapi tidak instan. Dan begitu Anda membutuhkan analitik, KV tidak memadai. Anda tidak bisa melakukan query. Anda tidak bisa melakukan agregasi lintas dimensi. Anda tidak bisa bertanya "tunjukkan klik berdasarkan negara selama 30 hari terakhir" karena KV tidak mendukung apapun yang menyerupai klausa WHERE.
Shrtnr menggunakan Cloudflare D1, yang memberi kami model database relasional berbasis SQLite yang cocok dengan Workers. Ini memberi kami query relasional, indexing, dan kemampuan untuk menjalankan analitik nyata pada data klik tanpa pipeline analitik terpisah. Pencarian tautan, pencatatan klik, pemfilteran rentang waktu, dan agregasi multi-dimensi semuanya terjadi di dalam D1, tanpa memerlukan pipeline analitik terpisah.
Kami mengindeks tabel untuk pola query yang benar-benar penting: resolusi slug (jalur panas di setiap redirect), pemfilteran rentang waktu untuk dashboard analitik, dan agregasi multi-dimensi di seluruh negara, referrer, perangkat, browser, dan OS.
Untuk analitik, Worker memfan-out query paralel di semua lima dimensi menggunakan Promise.all(). Satu request API memicu pembacaan D1 secara bersamaan dan merakit hasilnya. Tidak ada background job. Tidak ada pemrosesan antrian. Pada skala moderat ini bekerja dengan baik dan menjaga analitik tetap real-time. Pada volume traffic yang sangat tinggi, multiple query per request mulai menggerogoti batas pembacaan harian D1, dan Anda perlu memikirkan caching atau pre-aggregation. Untuk penggunaan kami, tradeoff-nya sepadan.
// Parallel analytics queries across all dimensions
const [countries, referrers, devices, browsers, os] = await Promise.all([
db.prepare(`
SELECT country, COUNT(*) as clicks
FROM clicks
WHERE link_id = ?
AND clicked_at BETWEEN ? AND ?
GROUP BY country
ORDER BY clicks DESC
`).bind(linkId, start, end).all(),
db.prepare(`
SELECT referrer, COUNT(*) as clicks
FROM clicks
WHERE link_id = ?
AND clicked_at BETWEEN ? AND ?
GROUP BY referrer
ORDER BY clicks DESC
`).bind(linkId, start, end).all(),
db.prepare(`
SELECT device_type, COUNT(*) as clicks
FROM clicks
WHERE link_id = ?
AND clicked_at BETWEEN ? AND ?
GROUP BY device_type
ORDER BY clicks DESC
`).bind(linkId, start, end).all(),
db.prepare(`
SELECT browser, COUNT(*) as clicks
FROM clicks
WHERE link_id = ?
AND clicked_at BETWEEN ? AND ?
GROUP BY browser
ORDER BY clicks DESC
`).bind(linkId, start, end).all(),
db.prepare(`
SELECT os, COUNT(*) as clicks
FROM clicks
WHERE link_id = ?
AND clicked_at BETWEEN ? AND ?
GROUP BY os
ORDER BY clicks DESC
`).bind(linkId, start, end).all(),
]);Apakah D1 pilihan yang tepat untuk setiap proyek? Tidak. KV masih lebih baik untuk pencarian key sederhana di mana Anda tidak membutuhkan query relasional. R2 lebih baik untuk object storage. Durable Objects lebih baik untuk koordinasi stateful. Tapi untuk produk yang membutuhkan pencarian cepat dan analitik yang bisa di-query, D1 adalah alat yang cocok.
Hono: dibangun untuk edge runtime, bukan diadaptasi ke sana
Lapisan API menggunakan Hono, sebuah framework TypeScript yang dirancang dari nol untuk edge runtime. Pilihan ini lebih penting dari yang terlihat.
Framework bergaya Express membawa asumsi tentang proses server yang berumur panjang, rantai middleware, dan API spesifik Node.js. Anda bisa membuatnya berjalan di Workers dengan adapter dan compatibility shim, tapi Anda menghabiskan waktu untuk mengatasi framework daripada bekerja dengan platform. Kami pernah melakukan pekerjaan berbasis adapter seperti itu di platform lain sebelumnya. Itu tidak pernah menyenangkan, dan kasus-kasus edge berlipat ganda.
Hono dibangun untuk model eksekusi yang digunakan Workers: handler berumur pendek, tanpa filesystem, batas memori yang ketat. Ia cocok dengan runtime seperti Rails cocok dengan VPS. Tidak perlu adapter.
Frontend melakukan rendering JSX sisi server melalui dukungan bawaan Hono. Dashboard admin, halaman detail tautan, dan UI pengaturan semuanya dihasilkan di edge dan di-stream ke browser. Tidak ada framework sisi klien. Tidak ada biaya hydration. Tidak ada bundle yang harus diunduh sebelum halaman menjadi interaktif.
// Server-side rendered page at the edge
app.get("/links/:id", async (c) => {
const link = await getLink(c.env.DB, c.req.param("id"));
const analytics = await getAnalytics(c.env.DB, link.id);
return c.html(
<Layout title={link.label || link.url}>
<LinkDetail link={link} analytics={analytics} />
</Layout>
);
});Ini adalah salah satu keputusan yang mungkin terlihat kecil, tapi efeknya berlipat ganda. Server-side rendering di edge berarti first paint terjadi dalam request yang sama yang me-resolve halaman. Tidak ada payload JavaScript yang memblokir render. Dashboard memuat cepat di koneksi apapun, di negara manapun, karena HTML dirakit dekat dengan pengguna dan dikirim sebagai stream.
Lebih penting lagi, ini memungkinkan kami menjaga seluruh aplikasi dalam model mental yang sama: satu Worker, satu framework, satu target deployment.
Apa selanjutnya
Itu mencakup arsitekturnya: mengapa Workers, mengapa D1 daripada KV, dan mengapa Hono daripada Express. Di posting berikutnya, kami membahas sisi produksi: tiga sistem autentikasi yang berjalan dalam satu Worker, deployment satu klik, TypeScript SDK, dan berapa biaya sebenarnya untuk menjalankan ini di produksi (spoiler: berjalan dalam tier gratis Cloudflare).
Tentang Oddbit
Kami adalah tim pengembang perangkat lunak senior yang berbasis di Bali, Indonesia, dengan akar di Swedia. Kami membangun perangkat lunak kustom untuk startup dan scale-up: aplikasi web, aplikasi mobile, platform SaaS, dan integrasi yang menghubungkan semuanya. Klien kami tersebar di Nordik, Eropa, dan Asia Tenggara.
Shrtnr adalah salah satu dari beberapa alat open-source yang telah kami bangun dan rilis. Lainnya termasuk Tanam (Firebase CMS dengan pembuatan konten AI), paket Flutter dengan sekitar seratus ribu unduhan di pub.dev, dan plugin Firebase Genkit untuk integrasi model AI.
Jika Anda sedang menyelesaikan keputusan arsitektur teknis, mengevaluasi edge computing untuk produk Anda, atau membangun sesuatu dari nol, hubungi kami. Kami lebih suka menunjukkan hasil kerja kami daripada menceritakannya.

