Lompat ke konten
dokumentasi.id
Commit 65055c7
v1
Star821

2.1

Alur Request dan Middleware `caching`

Satu request masuk lewat api/server.js, melewati cors(), express.json(), lalu Router dari api/routes.js. Router menempelkan header Cache-Control ke setiap respons, kemudian empat route GET memanggil middleware caching yang menyimpan body respons di sebuah objek in-memory. Halaman ini hanya membahas urutan lapis itu dan efek sampingnya; bentuk body serta kode status tiap endpoint dibahas di Kontrak Response dan Penanganan Error, sedangkan daftar lengkap route ada di Referensi Endpoint.

Urutan lapis di api/server.js

Berkas api/server.js hanya 18 baris: memuat .env lewat dotenv, membuat aplikasi express, memasang empat lapis, lalu memanggil listen. Tidak ada lapis lain di antara express() dan routes (api/server.js:8-18).

graph LR
  req["request masuk"] --> tp["trust proxy: 1"]
  tp --> cors["cors()"]
  cors --> json["express.json()"]
  json --> router["Router dari api/routes.js"]
  router --> cc["header Cache-Control"]
  cc --> match{"route cocok?"}
  match -->|"GET /"| idx["index handler tanpa caching"]
  match -->|"GET /surah, /juz/:juz"| caching["middleware caching"]
  match -->|"path lain"| fb["router.all('*') 404"]
  style tp fill:#f59e0b,color:#000
  style cc fill:#f59e0b,color:#000
  style caching fill:#8b5cf6,color:#fff
  style idx fill:#2d7d46,color:#fff
  style fb fill:#dc2626,color:#fff
%% fig: Lapis request dari express() sampai pencocokan route
%% desc: Request melewati trust proxy, cors, express.json, lalu Router; header Cache-Control dipasang sebelum route dicocokkan, dan hanya empat route GET yang masuk ke middleware caching.
Fig. 1: Lapis request dari express() sampai pencocokan routeRequest melewati trust proxy, cors, express.json, lalu Router; header Cache-Control dipasang sebelum route dicocokkan, dan hanya empat route GET yang masuk ke middleware caching.
BarisKodePeran
8const port = process.env.PORT dengan fallback 3000port server
11server.set('trust proxy', 1)mempercayai satu lapis proxy di depan aplikasi
12server.use(cors())memasang header CORS untuk semua respons
13server.use(express.json())memasang parser body JSON
14server.use(routes)menyerahkan seluruh path ke Router api/routes.js
16-18server.listen(port, ...)menyalakan server dan mencetak Server running at port:

Seluruh baris di atas berada dalam satu berkas (api/server.js:8-18). Dependensi runtime yang dipakai hanya cors ^2.8.5, dotenv ^16.0.1, dan express ^4.17.1 (package.json:15-19). Pada deployment Vercel, api/server.js dibangun sebagai fungsi @vercel/node yang menerima semua path (vercel.json:1-11), sehingga urutan lapis yang sama berlaku di lokal maupun di deployment.

Header Cache-Control di level Router

router.use pertama di api/routes.js berjalan untuk setiap request yang mencapai Router, sebelum route mana pun dicocokkan (api/routes.js:9-12). Nilainya ditulis literal sebagai satu string (api/routes.js:10):

public, max-age=0, s-maxage=86400, stale-while-revalidate

DirectiveNilaiArti
publictanpa nilairespons boleh disimpan cache bersama
max-age0cache klien wajib revalidasi setiap kali
s-maxage86400cache bersama boleh menyimpan hingga 24 jam
stale-while-revalidatetanpa nilaikode tidak menyebut durasinya, jadi jendela revalidasi bergantung pada cache penerima

Karena dipasang lewat router.use, header ini juga menempel pada respons index / dan pada respons fallback 404, padahal keduanya tidak memakai caching (api/routes.js:14-32, api/routes.js:40-45).

Middleware caching: key, hit, dan miss

api/middlewares.js hanya berisi satu objek const cache = {} di scope modul dan satu fungsi caching yang diekspor sebagai { caching } (api/middlewares.js:1, api/middlewares.js:3, api/middlewares.js:19-21). Objek itu hidup selama proses Node berjalan dan dipakai bersama oleh semua request.

sequenceDiagram
  autonumber
  participant R as Router routes.js
  participant M as caching
  participant K as cache in-memory
  participant H as Handler

  R->>M: GET /surah/:surah dengan caching
  M->>M: key = req.url
  alt cache[key] terisi
    M->>K: baca cache[key]
    K-->>M: body respons pertama
    M-->>R: res.status(200).send(cache[key])
  else cache[key] kosong
    M->>M: res.sendResponse = res.send
    M->>M: res.send diarahkan ke cache[key] = body
    M->>H: next()
    H-->>M: res.status(200).send(body)
    M->>K: cache[key] = body
  end

%% fig: Siklus hit dan miss di middleware caching %% desc: Pada hit, body yang tersimpan dikirim ulang dengan status 200 tanpa memanggil handler; pada miss, res.send diganti lebih dulu sehingga body handler ikut masuk ke cache sebelum next().

Perhatikan dua hal pada diagram di atas. Pertama, jalur hit mengembalikan res.status(200).send(cache[key]) dan berhenti di situ, sehingga handler tidak pernah dipanggil dan status asli respons pertama tidak dipakai lagi (api/middlewares.js:6-8). Kedua, jalur miss menyimpan referensi res.send yang asli ke res.sendResponse, mengganti res.send dengan fungsi yang menulis cache[key] = body lalu memanggil res.sendResponse(body), baru kemudian memanggil next() (api/middlewares.js:10-14).

TahapEkspresiCatatan
keyconst key = req.urlmemuat path sekaligus query string (api/middlewares.js:4)
hitres.status(200).send(cache[key])status ditulis tetap 200 (api/middlewares.js:6-8)
missres.sendResponse = res.send lalu res.send = (body) => ...patch berlaku pada objek response request itu saja (api/middlewares.js:10-14)
simpancache[key] = bodybody disimpan sebagai objek apa adanya, bukan string JSON (api/middlewares.js:11-13)
lanjutnext()handler baru dijalankan setelah patch terpasang (api/middlewares.js:16)
invalidasitidak adatidak ada TTL, penghapusan, atau pembatasan ukuran (api/middlewares.js:1)

Batas dan jebakan

⚠️ Respons 404 dari handler ikut tersimpan, lalu dikirim ulang dengan status 200. Handler mengirim 404 lewat res.send yang sudah dipatch (api/handlers/surah.js:30-35, api/handlers/juz.js:9-15), sehingga body Surah "..." is not found. masuk ke cache[key] sebagai body biasa (api/middlewares.js:11-13). Request kedua ke path yang sama menerima body tersebut dengan status 200.

⚠️ cache tidak pernah dibersihkan. Tidak ada TTL, tidak ada penghapusan entri, dan tidak ada batas jumlah entri di berkas middleware (api/middlewares.js:1), jadi objek itu tumbuh selama proses hidup dan hilang seluruhnya saat proses berhenti.

⚠️ Key memakai req.url mentah (api/middlewares.js:4). Query string ikut menjadi bagian key, padahal tidak satu pun handler membaca query, sehingga /surah/18 dan /surah/18?x=1 menempati dua entri berbeda dengan body yang identik.

Dua masa hidup yang berbeda perlu dibedakan: cache in-memory berakhir bersama prosesnya, sedangkan header s-maxage=86400 memberi izin cache bersama menyimpan respons hingga 24 jam (api/routes.js:10). Tidak ada kode di repository yang membersihkan salah satunya, sehingga perubahan isi data/quran.json tidak langsung terlihat oleh klien.

Route yang tidak melewati caching

RouteMiddleware yang dipasangMasuk cache?
GET /handler inline yang mengirim daftar endpointtidak (api/routes.js:14-32)
GET /surahcaching, SurahHandler.getAllSurahya (api/routes.js:34-38)
GET /surah/:surahcaching, SurahHandler.getSurahya (api/routes.js:34-38)
GET /surah/:surah/:ayahcaching, SurahHandler.getAyahFromSurahya (api/routes.js:34-38)
GET /juz/:juzcaching, JuzHandler.getJuzya (api/routes.js:34-38)
ALL *handler fallback Resource "..." is not found.tidak (api/routes.js:40-45)

Karena keempat route yang di-cache didaftarkan dengan router.get, semua metode lain — misalnya POST /surah — jatuh ke router.all('*') dan menerima 404 tanpa pernah menyentuh cache (api/routes.js:34-38, api/routes.js:40-45).