Membangun Website Layanan Kesehatan Rumah dengan Next.js 16 & Supabase: Dari Brief hingga Delivery
Cerita membangun website sewa alat kesehatan di Padang dengan Next.js 16, Supabase, dan chatbot RAG, dari brief hingga delivery dan hasil SEO minggu pertama.

Beberapa minggu lalu saya merilis Salkes Padang, website untuk usaha sewa alat kesehatan, homecare, dan apotek di Kota Padang. Proyeknya tidak glamor, tapi cakupannya lengkap: situs publik dua bahasa, panel admin, chatbot AI, dan fondasi SEO. Di tulisan ini saya runut prosesnya dari awal sampai serah terima, termasuk hal-hal yang baru ketahuan setelah rilis.
Fase 1: Memahami masalah, bukan langsung menulis kode
Klien menawarkan tiga layanan: sewa alat kesehatan (layanan utama), homecare (perawat ke rumah), dan apotek. Audiensnya keluarga pasien atau caregiver usia sekitar 25–65 tahun yang butuh solusi praktis tanpa harus ke rumah sakit.
Dua keputusan produk yang menentukan semua hal berikutnya:
- Sewa alat adalah hero service. Beranda, navigasi, dan CTA terkuat diarahkan ke sana.
- Konversi lewat WhatsApp. Di Indonesia, pertanyaan soal layanan kesehatan hampir selalu masuk lewat WhatsApp. Jadi tidak ada keranjang atau checkout, hanya tombol booking yang membuka chat dengan pesan terisi otomatis.
const waLink = (text: string) =>
`https://wa.me/${PHONE}?text=${encodeURIComponent(text)}`;
// pesan berisi nama alat, kategori, dan tarif, sehingga admin tidak perlu bertanya ulang
Sebelum menulis kode, saya menyusun PRD dan dokumen desain. Arah visualnya "Linear-clean minimalist": datar, kontras tinggi, warna inti teal #0D9488 dengan aksen amber #F59E0B, dan font Inter. Situs layanan kesehatan harus terasa bersih dan bisa dipercaya, bukan ramai.
Fase 2: Memilih stack
| Lapisan | Pilihan |
|---|---|
| Framework | Next.js 16 (App Router), React 19, TypeScript |
| Styling | Tailwind CSS v4 + plugin typography |
| i18n | next-intl v4 (Indonesia default, Inggris) |
| Database & storage | Supabase |
| AI | Gemini 2.5 Flash dengan pola RAG |
| Editor artikel | TipTap 3 |
Alasan utamanya:
- Next.js App Router karena halaman katalog dan artikel harus dirender di server agar mudah dibaca Google.
- Supabase karena database, storage gambar, dan akses datanya cukup dalam satu tempat untuk tim kecil.
- next-intl dengan
localePrefix: "as-needed"sehingga bahasa Indonesia tetap bersih tanpa prefix, sementara versi Inggris ada di/en. Semua konten bertipeLocalizedText { id, en }. - Animasi dengan Framer Motion saya batasi seperlunya. Situs kesehatan tidak perlu efek berlebihan.
Fase 3: Arsitektur data
Alurnya sederhana:
Supabase DB ──> data layer (cached) ──> halaman publik (SSR/ISR)
│
└──> endpoint chat (RAG) ──> Gemini 2.5 Flash
Admin panel ──> API routes (auth + rate limit) ──> Supabase (CRUD)
Data diambil lewat satu data layer dengan unstable_cache dan cache tags. Saat admin mengubah katalog, cache yang relevan di-invalidate, sehingga halaman tetap cepat tanpa data basi. Saya juga menyiapkan seed data sebagai fallback kalau database kosong, supaya situs tidak pernah menampilkan halaman kosong.
Fase 4: Fitur
Situs publik: beranda, katalog sewa beserta halaman detail tiap alat, layanan homecare, katalog apotek dengan badge "Butuh Resep", artikel edukasi, FAQ, dan kontak. Ada juga tombol WhatsApp melayang.
Panel admin supaya klien bisa mengelola konten sendiri tanpa menghubungi developer:
- CRUD untuk alat sewa, homecare, obat, artikel, dan FAQ
- Editor artikel berbasis TipTap
- Upload gambar ke Supabase Storage
- Penangkapan leads dari pengunjung
- Dashboard statistik, manajemen user admin, dan export data ke Excel
Untuk keamanannya saya memakai autentikasi sesi berbasis cookie dengan JWT, password di-hash dengan scrypt, serta rate limiter di endpoint sensitif.
Chatbot AI: pengunjung sering bertanya hal yang sama, misalnya harga, ketersediaan, dan area antar. Chatbot memakai Gemini 2.5 Flash dengan pola RAG. Profil perusahaan dan katalog produk disuntikkan ke system prompt, jawabannya di-stream, dan topiknya dibatasi hanya seputar Salkes Padang. Chatbot kesehatan yang menjawab di luar konteks adalah risiko yang tidak perlu.
Fase 5: SEO sejak hari pertama
SEO tidak saya tempel di akhir. Yang dipasang sejak awal:
- Metadata, Open Graph, dan Twitter card per halaman
sitemap.ts,robots.ts, dan komponen structured data- Kata kunci lokal di title, heading, dan deskripsi (sewa alat kesehatan, homecare, apotek, Padang)
- Artikel edukasi yang menjawab pertanyaan nyata, seperti panduan sewa tabung oksigen
- Endpoint
llms.txtdanllms-full.txtagar crawler AI memahami isi situs
Delivery dan hasil minggu pertama
Yang diserahkan: situs publik, panel admin, dan jalur data yang bisa diurus klien sendiri. Setelah rilis, saya memantau Google Search Console. Data sampai 28 September:
- 13 klik
- 86 impresi
- CTR 15,1%
- Posisi rata-rata 8,5
Angkanya kecil, jadi saya menganggapnya sinyal awal, bukan kemenangan. Tapi grafiknya bergerak dari nol tepat setelah halaman mulai terindeks.
Yang ketahuan setelah audit sendiri
Audit setelah rilis menemukan hal-hal yang lolos saat development:
- Canonical menunjuk ke URL yang masih redirect 307. Ini membingungkan crawler. Solusinya menyeragamkan canonical dengan URL yang benar-benar menjawab 200, dan memakai 301/308 untuk redirect permanen.
- Dua artikel berebut kata kunci yang sama. Keduanya akan digabung, dan yang lama di-redirect.
- Teks
Rp0/haribocor di pesan WhatsApp untuk alat yang harganya "hubungi kami". Fallback harga perlu ditangani eksplisit. - Slug dan copy yang belum rapi di beberapa layanan.
Pelajaran
- Pahami cara konversi pelanggan dulu. WhatsApp deep-link menyelesaikan lebih banyak masalah daripada sistem pemesanan yang rumit.
- Beri klien panel admin yang layak. Situs yang tidak bisa diperbarui akan cepat usang.
- Batasi chatbot. RAG dengan cakupan sempit lebih berguna dan lebih aman daripada chatbot serba bisa.
- SEO lokal soal menjawab pertanyaan warga setempat, bukan soal trik.
- Audit diri sendiri setelah launch. Hampir selalu ada yang lolos.
Kalau kamu sedang mengerjakan situs bisnis lokal dengan Next.js, saya senang bertukar pengalaman, terutama soal SEO dan cache di App Router.