2
Arsitektur Sistem
Repository ini adalah satu aplikasi Express kecil: satu entry point api/server.js, satu Router di api/routes.js, dua handler, satu modul komputasi di api/lib/, dan dua berkas JSON sebagai sumber data. Di sisi lain ada crawler/ yang berdiri sendiri sebagai perkakas offline, tidak pernah di-require oleh kode api/. Seluruh ketergantungan mengalir satu arah: route memanggil handler, handler memakai lib atau membaca JSON, dan tidak ada lapisan yang memanggil balik lapisan di atasnya.
Peta modul dan arah ketergantungan
Diagram berikut menunjukkan arah require yang sebenarnya. api/routes.js mengimpor ./middlewares dan kedua handler api/routes.js:1-51. api/handlers/juz.js menyerahkan pekerjaan berat ke ../lib/juz.js api/handlers/juz.js:1-62. Modul lib dan handler surah membaca berkas JSON secara langsung api/handlers/surah.js:13, api/lib/juz.js:1-24.
graph TD
server["api/server.js"] --> routes["api/routes.js"]
routes --> caching["api/middlewares.js — caching"]
routes --> surahHandler["handlers/surah.js"]
routes --> juzHandler["handlers/juz.js"]
juzHandler --> juzLib["api/lib/juz.js"]
surahHandler --> quranJson[("data/quran.json")]
juzLib --> quranJson
juzLib --> juzJson[("data/juz.json")]
style server fill:#f59e0b,color:#000
style routes fill:#f59e0b,color:#000
style caching fill:#8b5cf6,color:#fff
style surahHandler fill:#2d7d46,color:#fff
style juzHandler fill:#2d7d46,color:#fff
style juzLib fill:#8b5cf6,color:#fff
style quranJson fill:#336791,color:#fff
style juzJson fill:#336791,color:#fff
%% fig: Arah ketergantungan modul runtime
%% desc: Route menjadi satu-satunya pemanggil handler, handler memakai lib atau membaca JSON, dan data JSON tidak pernah mengimpor kode api.Tidak ada panah balik dari handler ke route, dan tidak ada modul api/ yang menyentuh direktori crawler/. Konsekuensinya, setiap perubahan kontrak response hanya perlu dilakukan di handler, tanpa menyentuh routing.
Tanggung jawab tiap lapisan
| Modul | Tanggung jawab | Titik masuk |
|---|---|---|
api/server.js | Membuat instance Express, memasang cors() dan express.json(), lalu memanggil listen pada process.env.PORT | api/server.js:8-185 |
api/routes.js | Satu Router yang memetakan URL ke handler, menulis header Cache-Control untuk semua response, dan menutup dengan fallback 404 | api/routes.js:9-126, api/routes.js:34-387, api/routes.js:41-458 |
api/middlewares.js | Middleware caching yang menyimpan res.send asli dan menyalin body ke objek cache | api/middlewares.js:1-179 |
api/handlers/surah.js | Tiga method static: getAllSurah, getSurah, getAyahFromSurah, masing-masing menyusun body { code, status, message, data } | api/handlers/surah.js:5-1010 |
api/handlers/juz.js | Satu method static getJuz yang mem-parse req.params.juz lalu memanggil juzData | api/handlers/juz.js:1-62 |
api/lib/juz.js | Fungsi juzData yang memotong rentang verses dari quran.json memakai batas pada juz.json | api/lib/juz.js:1-24 |
crawler/ | Skrip npm run crawl: mengambil data sumber, menyusun ulang objek surah, lalu menulis ./data/quran.json | crawler/script.js:196-20311 |
config.js | Membaca .env dan mengekspor SOURCE_API_BASEURL, SOURCE_API_BASEURL_2, MONGODB_URI | config.js:1-1112 |
vercel.json | Menetapkan api/server.js sebagai satu-satunya build dan tujuan seluruh route | vercel.json:3-1013 |
api/server.js hanya merangkai
Berkas entry point ini hanya berisi 18 baris: memuat dotenv, membuat app, mengaktifkan trust proxy, memasang cors(), memasang express.json(), memasang router, lalu listen ke port api/server.js:8-185. Tidak ada rate limiter, logging middleware, maupun penanganan error terpusat di lapisan ini. Skrip start dan dev di package.json sama-sama menunjuk ke berkas tersebut package.json:4-1414.
api/routes.js sebagai satu-satunya pintu masuk
Router ini melakukan tiga hal. Pertama, middleware tanpa path menulis Cache-Control: public, max-age=0, s-maxage=86400, stale-while-revalidate ke setiap response, termasuk yang berakhir 404 api/routes.js:9-126. Kedua, empat route data didaftarkan dengan caching di antara path dan handler api/routes.js:34-387. Ketiga, router.all('*') menutup semua path lain dengan body code: 404 dan pesan Resource "${req.url}" is not found. api/routes.js:41-458.
/juz/:juz tidak memakai caching, sedangkan /surah, /surah/:surah, dan /surah/:surah/:ayah memakainya. Perilaku middleware itu dibahas di Alur Request dan Middleware caching, dan daftar lengkap route ada di Referensi Endpoint.
Handler menjawab, lib menghitung
Pembagiannya konsisten: handler surah bekerja langsung pada array quran yang di-require di baris pertama berkas, misalnya getAllSurah menyalin tiap item lalu menghapus verses dan preBismillah sebelum dikirim api/handlers/surah.js:5-1010. Handler juz justru tidak menyentuh JSON sama sekali; ia hanya memvalidasi hasil juzData(parseInt(juz)) lalu mengubah null menjadi 404 api/handlers/juz.js:1-62. Semua aritmetika rentang ayat berada di juzData pada api/lib/juz.js.
Bentuk field yang dikirim handler bergantung pada struktur data/quran.json dan data/juz.json; keduanya dijelaskan di Model Data quran.json dan juz.json, sedangkan algoritme pemotongan rentangnya di Komputasi Rentang Juz.
Data JSON dibaca sekali saat modul dimuat
Kedua sumber data diimpor dengan require di tingkat atas modul api/handlers/surah.js:13, api/lib/juz.js:1-24. Artinya Node mem-parse JSON itu sekali per proses, lalu menyimpannya di module cache. Dua konsekuensi praktis: mengganti isi data/quran.json tidak berpengaruh pada proses yang sedang berjalan, dan setiap instance serverless memegang salinan penuh datanya sendiri.
graph LR
env[".env"] --> config["config.js"]
config --> script["crawler/script.js"]
helpers["crawler/helpers.js"] --> script
script -->|"writeFile"| quranFile[("data/quran.json")]
quranFile -->|"require saat boot"| handler["handlers/surah.js"]
quranFile --> lib["api/lib/juz.js"]
juzFile[("data/juz.json")] --> lib
style env fill:#f59e0b,color:#000
style config fill:#f59e0b,color:#000
style script fill:#2d7d46,color:#fff
style helpers fill:#2d7d46,color:#fff
style quranFile fill:#336791,color:#fff
style juzFile fill:#336791,color:#fff
style handler fill:#8b5cf6,color:#fff
style lib fill:#8b5cf6,color:#fff
%% fig: Siklus data dari crawler ke runtime
%% desc: Crawler menulis data/quran.json, sementara runtime hanya membacanya saat modul di-require; panah menuju handler berarti ketergantungan baca, bukan pemanggilan kode.data/quran.json ditulis oleh writeFile di akhir main() crawler/script.js:196-20311, dan tidak ada jalur kode di api/ yang menulis berkas itu kembali. Rantai crawler lengkapnya ada di Crawler: Pipeline Pengambilan Data.
⚠️
crawler/script.jsme-require('../data/quran-tafsir.json')di tingkat atas crawler/script.js:2-1015, tetapi berkas itu tidak ada di repository pada commit ini.npm run crawlakan gagal saat modul dimuat, sebelum satu punfetchdijalankan.
Satu entry point untuk semua target deploy
Baik Vercel maupun Heroku mengarah ke berkas yang sama. vercel.json menetapkan @vercel/node untuk api/server.js dan mengarahkan /.* ke sana vercel.json:3-1013, sementara package.json menjadikannya main serta target skrip start package.json:4-1414. Lingkungan dan variabel yang dibutuhkan crawler dibaca lewat config.js config.js:1-1112; detail menjalankan dan men-deploy ada di Konfigurasi, Menjalankan Lokal, dan Deployment.